Skip to content

Authentication

The Telbox developer API is authenticated with API keys (tb_live_…) that you mint in the developer console. A key authenticates a machine acting for one workspace, gated by deny-by-default scopes. This guide covers creating your developer account, then creating, using, and managing API keys. The separate per-agent MCP endpoint authenticates with OAuth instead — see MCP.

Gated until launch

The external API-key auth path is behind the apikey_auth_enabled flag and is dark on the public production host until launch. While it's off, point your base URL at your developer preview. Everything below describes the enabled behavior.

1. Create a developer account

The console sign-up is email + password + a 6-digit email code. These four endpoints are public (they mint your console session) and live under /v1/developer/auth.

Endpoint Body Returns
POST /v1/developer/auth/register { email, password, display_name } { email, needs_verification: true, dev_code? }
POST /v1/developer/auth/verify { email, code } { email, access_token }
POST /v1/developer/auth/login { email, password } { email, access_token }
POST /v1/developer/auth/resend { email } { email, needs_verification: true, dev_code? }
  • password must be at least 10 characters; email and display_name are required at registration.
  • register creates the account and emails a 6-digit code. verify confirms the code, marks the email verified, and returns a console session access_token.
  • login requires a verified email; if the account exists but was never verified, it re-sends a code and returns needs_verification: true instead of a token.

dev_code in non-prod

In non-production environments (no SMTP configured) the verification code is echoed back as dev_code so the local preview works without a mail server. Production sends a real email and never echoes the code.

Sign up, then verify
curl -X POST https://api.telbox.ai/v1/developer/auth/register \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@example.com", "password": "correct-horse-battery", "display_name": "Ada" }'

curl -X POST https://api.telbox.ai/v1/developer/auth/verify \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@example.com", "code": "123456" }'
# → { "email": "ada@example.com", "access_token": "eyJhbGciOi…" }

The console access_token authenticates the first-party developer endpoints (/v1/developer/*, /v1/agents/*) that you use to manage keys and agents from the portal. For calling the API from your own server, mint an API key (next).

2. API keys

An API key is the credential your integration uses. The format is tb_{env}_{token}tb_live_… on the production host, tb_test_… in non-prod — and only an argon2 hash plus a short display prefix (tb_{env}_{token[:8]}) is ever stored. The env is chosen by the server from the environment; you don't pick it.

Shown exactly once

The raw key is returned only in the create/rotate response. Store it in a secret manager immediately — you cannot retrieve it again. Lost a key? Rotate or revoke it and mint a new one.

Manage keys — /v1/developer/api-keys

These are first-party endpoints, so call them with your console access_token (or from the portal). They manage your own workspace's keys.

Endpoint Purpose
GET /v1/developer/scopes The grantable scope catalog
GET /v1/developer/api-keys List your keys (newest first)
POST /v1/developer/api-keys Create a scoped key → raw key (once)
POST /v1/developer/api-keys/{key_id}/rotate Mint a replacement + revoke the old
DELETE /v1/developer/api-keys/{key_id} Revoke a key

POST /v1/developer/api-keys takes { name, scopes?, expires_at? }. If you omit scopes, the key defaults to ["threads:read", "messages:write", "voice_notes:write"]. expires_at is an optional ISO-8601 expiry. The response is { key, raw_key, env }, where key is the key's metadata view and raw_key is the full secret.

One-shot key

POST /v1/agents/api-keys mints a single default-scoped key (threads:read, messages:write, voice_notes:write) for your workspace in one call — the portal's "Get an API key" button. Use POST /v1/developer/api-keys when you want to choose the name, scopes, or an expiry.

Create a key

curl -X POST https://api.telbox.ai/v1/developer/api-keys \
  -H "Authorization: Bearer <console_access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "crm-sync",
    "scopes": ["threads:read", "messages:write"]
  }'
201 Created
{
  "key": {
    "id": "8f0c…",
    "name": "crm-sync",
    "prefix": "tb_live_a1b2c3d4",
    "scopes": ["messages:write", "threads:read"],
    "revoked": false
  },
  "raw_key": "tb_live_a1b2c3d4e5f6…",
  "env": "live"
}
from telbox import TelboxClient

# Authenticate the SDK with your console session to mint a key, once.
admin = TelboxClient(api_key="<console_access_token>")
created = admin.create_api_key()       # default scopes, returns the raw key once
print(created.raw_key)                  # tb_live_… — stash this now
import { TelboxClient } from "@telbox/sdk";

// Authenticate the SDK with your console session to mint a key, once.
const admin = new TelboxClient({ apiKey: "<console_access_token>" });
const created = await admin.createApiKey();   // default scopes, raw key once
console.log(created.rawKey);                    // tb_live_… — stash this now

Use a key

Send the key as a Bearer token on every request. The SDKs take it once at construction.

curl https://api.telbox.ai/v1/agents \
  -H "Authorization: Bearer tb_live_a1b2c3d4e5f6…"
from telbox import TelboxClient

tb = TelboxClient(api_key="tb_live_…", base_url="https://api.telbox.ai")
agents = tb.list_agents()
import { TelboxClient } from "@telbox/sdk";

const tb = new TelboxClient({ apiKey: "tb_live_…", baseUrl: "https://api.telbox.ai" });
const agents = await tb.listAgents();

Rotate and revoke

Rotating mints a replacement key (same name, scopes, and expiry) and revokes the old one in a single call — update your secret store with the new raw_key, then the old key stops working. Revoking is immediate and irreversible.

Rotate, then revoke
# Rotate: returns a new raw_key; the old key_id is revoked.
curl -X POST https://api.telbox.ai/v1/developer/api-keys/{key_id}/rotate \
  -H "Authorization: Bearer <console_access_token>"

# Revoke: 204 No Content, no body.
curl -X DELETE https://api.telbox.ai/v1/developer/api-keys/{key_id} \
  -H "Authorization: Bearer <console_access_token>"

Every create, rotate, and revoke is rate-limited and recorded in the platform audit log.

Scopes

Keys are deny-by-default: a key can only do what it was explicitly granted. Fetch the live catalog from GET /v1/developer/scopes. The vocabulary:

Scope Grants
threads:read List threads and enumerate message metadata (ids, kinds, sender, timestamps, delivery state). Encrypted content / transcripts / audio URLs are stripped unless the key also holds messages:read.raw.
messages:read.raw Raw decrypted message + voice content (high-privilege).
voice_notes:read Transcripts, summaries, and extracted actions + their metadata.
messages:write Send text messages (non-voice-note kinds).
voice_notes:write Create / send a Telbox voice note.
tasks:write Create a task.
contacts:read Read contacts.
webhooks:manage Manage webhook endpoints.
scim SCIM provisioning.

messages:read.raw exposes plaintext

threads:read is the metadata-only sibling — "show me what happened" without "show me what was said." Grant messages:read.raw only when the integration genuinely needs decrypted content; it's the high-privilege scope and is audited.

3. Per-agent MCP uses OAuth, not API keys

A published agent exposes its own MCP endpoint at POST /v1/agents/{agent_id}/mcp (JSON-RPC over Streamable HTTP). That endpoint is OAuth-gated, not API-key-gated — an MCP client (Claude Code, Cursor, Zed) completes an OAuth flow to obtain a token bound to a client and scope ceiling. The API-key Bearer described above does not authenticate the per-agent /mcp endpoint.

For first-party / owner-driven calls, the SDKs offer a convenience method that forwards a tool call through the agent's MCP surface:

tb.agent_mcp_call(agent.id, "read_thread", {"thread_id": "…"})
await tb.agentMcpCall(agent.id, "read_thread", { thread_id: "…" });

See MCP for the full OAuth flow, the tool surface, and how to wire an external MCP client to a published agent.

Errors

A missing or malformed credential returns 401 with a machine-readable error code. Unknown scopes at key creation return 400 unknown_scopes: …; calling a first-party endpoint without a valid console session returns 401/403. See Errors and Rate Limits & Quotas.

Next steps

  • Getting Started — base URL, conventions, your first request.
  • MCP — the OAuth-gated per-agent MCP endpoint.
  • Webhooks — subscribe to events with webhooks:manage.
  • Rate Limits & Quotas — per-developer key-mutation and write budgets.