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? } |
passwordmust be at least 10 characters;emailanddisplay_nameare required at registration.registercreates the account and emails a 6-digit code.verifyconfirms the code, marks the email verified, and returns a console sessionaccess_token.loginrequires a verified email; if the account exists but was never verified, it re-sends a code and returnsneeds_verification: trueinstead 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.
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¶
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.
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: 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:
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.