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.