Credentials
API keys, MCP tokens, sessions — which one to hold, and which routes each one can never reach.
Three credential classes reach this API, and they are not interchangeable. The distinction is not bureaucracy: it is what makes "a credential cannot promote itself" true.
| credential | what it is | profile scope |
|---|---|---|
atb_live_… API key | A machine credential minted for exactly one profile. No user identity, no consent screen. | one profile, implicitly |
atb_user_… MCP / user token | A person's token, minted through the MCP OAuth hand-off or in the console. Spans every workspace they belong to. | many — ?profile_id= is required on profile-scoped routes |
| Session | The Supabase session the console itself holds. | whatever the person can open |
A credential cannot administer its own class
/api/v1/me/** and the workspace key routes are session bearers only. An
API key is minted for one profile — it is not a person, it has no MCP
credentials of its own, and it cannot mint, revoke or rotate a sibling key. All
of those answer 403 insufficient_scope for a key, deliberately. Minting stays
a signed-in human's act.
Minting an API key
Settings → Developer → Create API Key. Choose the scopes, copy the key. It
starts with atb_live_ and is shown once.
POST /api/v1/workspaces/{workspaceId}/api-keysSession bearer only, profile_id required, capped at 10 active keys per
profile. The plaintext secret comes back exactly once.
An agent that holds only an atb_user_… token does not need a key: since
#1084 that token is a first-class /api/v1 principal on both REST and MCP.
Mint a key when a machine job should belong to a profile rather than to a
person — a nightly export, a CI check, a webhook consumer.
…/api-keys/{id}/rotate issues a replacement and returns the new secret;
…/revoke kills one. Rotation is the safe move when a key may have leaked —
revoke alone leaves the job that used it broken until someone notices.
MCP tokens
GET /api/v1/me/mcp-tokens
POST /api/v1/me/mcp-tokens
POST /api/v1/me/mcp-tokens/{tokenId}/rotate
POST /api/v1/me/mcp-tokens/{tokenId}/revoke
GET /api/v1/me/mcp-oauth-grants
GET /api/v1/me/mcp-usageCapped at 10 active tokens per user. mcp-oauth-grants lists the MCP apps a
person has connected through OAuth — a different object from a static token,
revocable independently. mcp-usage reports units against the cap, summed
across every workspace the caller belongs to, at the highest-cap active
subscription's tier — the same resolution the MCP server itself uses.
For an agent, the OAuth hand-off is the intended path and needs no console visit at all: see the API quickstart and the MCP quickstart.
Scopes
The full scope catalogue, generated from the source of truth, is on Authentication. Two rules that catch people out:
- An MCP scope is an intersection of what the human granted and what their
membership already allows.
mcp:readmaps toanalytics:read+campaigns:read; no MCP scope reaches exports, commerce or messaging. - A
403 insufficient_scopenames the scope it wanted whenever the scope is known by name — see Errors.
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/me | The signed-in user's own account |
PATCH | /api/v1/me | Update the signed-in user's display name or locale |
DELETE | /api/v1/me | Irreversibly delete the caller's account |
POST | /api/v1/me/email | Start an email change |
DELETE | /api/v1/me/email | Cancel a pending email change |
GET | /api/v1/me/mcp-oauth-grants | The caller's connected MCP apps |
POST | /api/v1/me/mcp-oauth-grants/{id}/revoke | Disconnect an MCP app |
GET | /api/v1/me/mcp-tokens | The caller's MCP tokens |
POST | /api/v1/me/mcp-tokens | Mint a new MCP token |
POST | /api/v1/me/mcp-tokens/{tokenId}/revoke | Revoke one MCP token |
POST | /api/v1/me/mcp-tokens/{tokenId}/rotate | Rotate one MCP token |
GET | /api/v1/me/mcp-usage | The caller's current-period MCP usage |
POST | /api/v1/me/password | Change the caller's password |
DELETE | /api/v1/me/sessions | Sign out every other device |
DELETE | /api/v1/me/sessions/{sessionId} | Revoke one session |
Generated from openapi.json. The full request and response schema for every operation is in the OpenAPI document.