Scopes
Every API token carries exactly one of two scopes, chosen at creation
and immutable for the life of the token: read_only or read_write.
The rule, in one sentence
Mutating HTTP methods (POST, PATCH, PUT, DELETE) require a
read_write token. Everything else — GET, HEAD, OPTIONS — is
available to both scopes.
Defense in depth
The scope check fires twice on every mutating request: once at the
edge auth middleware (immediately after the token is verified), and
once again at the route handler before any database write. The
duplicate check is intentional — a routing bug that lets a GET
handler accept a POST body cannot become a scope-escalation hole.
The second gate catches it.
What you see on a violation
A read_only token attempting a mutating call gets a 403 with
code: token_scope_insufficient in the error envelope. The body
names the offending method and the token's actual scope so CI logs
make the misconfiguration obvious.
Picking a scope
Default to read_only for any token whose job is dashboards,
exports, or read-side automation. Reserve read_write for tokens
that genuinely provision or mutate state — Terraform pipelines,
incident-management bots, account-bootstrap scripts. Token scope
is the cheapest blast-radius control you'll ever apply.
See also: Auth — token format and lifecycle, and Errors for the full error shape.