Scopes
The permission scopes a personal access token can carry and which operations each scope allows.
Scopes are the token half of every authorization decision; the creator's live rights are the other half (the intersection rule). They are grouped by the object they touch — the same objects the portal's screens are built over.
Scopes are enforced by the Management API, the only place a personal access token works: tokens are rejected on the portal's own API and on the Inference API.
The scope vocabulary
| Scope | Class | Reaches | Endpoints |
|---|---|---|---|
org:read | standard | Read the organization record and its settings: defaults, budget, MFA, spend ceiling | GET /org |
org:write | sensitive | Update organization settings: name, slug, model and rate defaults, budget, email OTP, spend ceiling | PATCH /org † |
members:read | standard | List members with their roles and states, and pending invites | GET /members, GET /members/{id} †, GET /invites † |
members:write | sensitive | Invite members, change roles and project access, disable/enable, remove members, sign them out, revoke invites | POST /invites, PATCH/DELETE /members/{id}, PUT /members/{id}/projects, POST /members/{id}/disable, POST /members/{id}/enable, DELETE /members/{id}/sessions, DELETE /invites/{id} — all † |
projects:read | standard | List projects and read their effective guardrails | GET /projects, GET /projects/{id} |
projects:write | sensitive | Create, rename, disable/enable, delete projects; set allowlist, spend cap, IP allowlist | POST /projects, PATCH/DELETE /projects/{id}, POST /projects/{id}/disable, POST /projects/{id}/enable — all † |
keys:read | standard | List a project's API keys (masked) with their status and creator | GET /projects/{id}/keys, GET /keys/{id} |
keys:write | standard | Create, rename, rotate, disable/enable, revoke API keys; set expiry, IP allowlist (spend limits are set on the project) | POST /projects/{id}/keys, PATCH/DELETE /keys/{id}, POST /keys/{id}/rotate, POST /keys/{id}/rotate/cancel, POST /keys/{id}/disable, POST /keys/{id}/enable |
usage:read | standard | Read metered usage and cost for a time window and export per-request usage records | GET /usage †, GET /usage/export † |
billing:read | standard | Read the prepaid credit balance, credit grants and top-ups, and invoices | GET /billing/balance, GET /billing/grants, GET /billing/invoices, GET /billing/invoices/{id} |
audit:read | standard | List and export the organization audit log | GET /audit †, GET /audit/export † |
catalog:read | standard | List the models the organization can call and the regions they are served in | GET /catalog/models, GET /catalog/regions |
Parent scopes in the portal. The Create token dialog groups the tree
under four parent rows — org, members, projects, keys — one for each
group that has both a read and a write scope. Ticking a parent is a UI
convenience that ticks both child scopes; it does not mint an extra scope. The
token itself still carries only the scopes above, exactly as ticked. In
particular, write does not imply read on the wire (see below), so a token
minted with only projects:write (tick that child row on its own, without
the parent) cannot list projects — GET /projects fails
with insufficient_scope naming projects:read.
† Admin-only: the token owner must currently be an organization Admin, whatever the token's scopes — the same rule the portal applies to those screens. The other routes are open to any member, within the portal's member rules (a Member sees only their own projects and the keys they created).
/me/tokens needs no scope: any valid
token may list and revoke its owner's own tokens.
The sensitive class
Three scopes — org:write, members:write and projects:write — govern the
changes the portal protects with step-up re-authentication: security and
organization settings, who is in the org and with which role, deletions, and
the guardrails that move money.
A token cannot answer an interactive challenge, so the equivalent controls are structural instead:
- Sensitive scopes are off by default at mint and carry a plain-language warning when selected.
- Minting any token requires re-authentication in the portal.
- Every write made under a token is audited with the scope named.
A token is never sent automatically — every call presents it explicitly — and it belongs to one person who can be offboarded in one action.
Two rules for any scope set
- A token can only do what its creator can do. Scopes never add rights the creator lacks (see below).
- Read and write are independent.
keys:writedoes not implykeys:read. A monitoring integration should hold only the:readscopes it reads; a provisioning pipeline should hold only the:writescopes it changes plus the reads it checks.
There is no * wildcard. Naming every scope costs one more click at mint
time and saves an incident later. A token must carry at least one scope, and
the portal refuses to mint one with an unknown scope.
The intersection rule
Effective permission = token scopes ∩ creator's live rights. The creator's live rights are their role in this organization (Admin or Member) intersected with their project access, for any call that touches a project. Evaluated on every request.
Three consequences:
- Demotion narrows tokens — and the API says so. An Admin demoted to Member keeps their tokens, but those tokens' reach collapses to a Member's rights on the next evaluation.
- Promotion never adds scopes. Scopes are fixed at mint. A promoted owner's token can use the scopes it already carries on Admin-only routes again — nothing it was not minted with.
- Removal kills tokens, not keys. Disabling or removing a member ends
every personal access token they created. The project API keys they created keep working,
unless the member is removed with
"delete_keys": true.
When a call is refused
Both halves fail the same way — 403 permission_error with
code: insufficient_scope — and the message names which half is missing,
so the pipeline log reads like a sentence rather than a bare 403.
The token lacks the scope. param names the scope the call needs:
{
"error": {
"message": "this token does not carry the projects:write scope",
"type": "permission_error",
"code": "insufficient_scope",
"param": "projects:write",
"action": "Mint a token that includes projects:write in Settings → API Tokens.",
"request_id": "req_cd71a3d856604660b8f12bbebb5bd8dc"
}
}The creator lacks the right. Example: Dana minted a token with
projects:write while she was an Admin. She has since been changed to
Member. The token still carries the scope, but POST /projects now fails
because Dana can no longer create projects:
{
"error": {
"message": "your role in this organization is member; this call needs admin",
"type": "permission_error",
"code": "insufficient_scope",
"param": null,
"action": "Ask an organization admin to perform this call, or to change your role.",
"request_id": "req_607c5f49f2ea43778fa6dde880ec4bca"
}
}The project-access variant reads your project access does not include this project; a Member acting on a key someone else created gets members can only manage keys they created.
Warning
A token minted "for the org" narrows silently when its creator is demoted, and the breakage shows up in someone's pipeline first. When you change an Admin's role, check the tokens they own (Settings → API Tokens) and re-home any automation that depends on them.
The 60-second propagation window
Authorization-relevant changes take effect within 60 seconds.
| Change | Takes effect |
|---|---|
| Token minted, revoked or expired | Immediately — the credential is checked on every request |
| Member role change, disable, removal (the intersection rule) | Within 60 seconds |
| Project access change for a member | Within 60 seconds |
| Project guardrail change (model allowlist, spend limit, IP allowlist) | Within 60 seconds |
| Project or key state (disable, enable, delete) on the data plane | Within 60 seconds |
In practice: a just-minted token works at once; a just-demoted Admin's token may keep its previous reach for up to a minute.