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

ScopeClassReachesEndpoints
org:readstandardRead the organization record and its settings: defaults, budget, MFA, spend ceilingGET /org
org:writesensitiveUpdate organization settings: name, slug, model and rate defaults, budget, email OTP, spend ceilingPATCH /org †
members:readstandardList members with their roles and states, and pending invitesGET /members, GET /members/{id} †, GET /invites †
members:writesensitiveInvite members, change roles and project access, disable/enable, remove members, sign them out, revoke invitesPOST /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:readstandardList projects and read their effective guardrailsGET /projects, GET /projects/{id}
projects:writesensitiveCreate, rename, disable/enable, delete projects; set allowlist, spend cap, IP allowlistPOST /projects, PATCH/DELETE /projects/{id}, POST /projects/{id}/disable, POST /projects/{id}/enable — all †
keys:readstandardList a project's API keys (masked) with their status and creatorGET /projects/{id}/keys, GET /keys/{id}
keys:writestandardCreate, 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:readstandardRead metered usage and cost for a time window and export per-request usage recordsGET /usage †, GET /usage/export †
billing:readstandardRead the prepaid credit balance, credit grants and top-ups, and invoicesGET /billing/balance, GET /billing/grants, GET /billing/invoices, GET /billing/invoices/{id}
audit:readstandardList and export the organization audit logGET /audit †, GET /audit/export †
catalog:readstandardList the models the organization can call and the regions they are served inGET /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

  1. A token can only do what its creator can do. Scopes never add rights the creator lacks (see below).
  2. Read and write are independent. keys:write does not imply keys:read. A monitoring integration should hold only the :read scopes it reads; a provisioning pipeline should hold only the :write scopes 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.

ChangeTakes effect
Token minted, revoked or expiredImmediately — the credential is checked on every request
Member role change, disable, removal (the intersection rule)Within 60 seconds
Project access change for a memberWithin 60 seconds
Project guardrail change (model allowlist, spend limit, IP allowlist)Within 60 seconds
Project or key state (disable, enable, delete) on the data planeWithin 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.