API errors, decoded#
Every refusal answers a JSON error with a code. Refusals are enforced before
any reservation, so a refused call never charges the project wallet.
| Error | Cause | Fix |
|---|---|---|
401 invalid_api_key |
Missing, mistyped or revoked key | Check the Authorization: Bearer … header; if revoked, rotate or create a new key |
402 insufficient_budget |
Project wallet unfunded or fully held | Fund the wallet — see below; already-held reservations release on settlement |
403 key_unbound |
Legacy key issued before keys became project-bound | Create a new key bound to a project; the console shows these as Needs reissue |
403 project_mismatch |
A project selector that differs from the key's bound project | Drop the selector, or restate the bound project exactly |
403 product_not_assigned |
The model is published but not assigned to the key's project | Ask an Owner/Administrator to assign the exact version on the project's Products tab |
403 key_scope |
The model is eligible but outside the key's resource list | Use a model in the key's list, or create a key with broader Allowed resources |
403 denied |
No consumption right: no project grant (or team grant for keys) | Ask an Owner/Administrator for a Project Consumer grant on the project's Members tab |
404 model_not_found |
Guessed id, another project's model, draft or suspended deployment | List what the key can call with GET /v1/models |
404 not_found |
A project or resource outside your organization | Keys and sessions only reach their own organization — by design |
409 revision_not_active |
Sandbox called on a revision that is not the deployed one | Activate the revision in the sandbox Studio, or call without a revision |
422 project_required |
Playground call with no Project that pays selected | Select the paying project in the playground |
422 unsupported_parameter |
A request field outside the accepted set | Remove the field — unknown fields are refused, not dropped, so you are never billed for an unapplied limit |
422 resource_not_eligible |
A key's resource list names something the project cannot call | Pick from the eligible resources shown at key creation |
422 sandbox_contract |
Sandbox with a non-chat contract called via chat/completions | Use POST /v1/sandbox/{id}/invoke for rich contracts |
429 |
Too many requests — admission caps at 30 per minute per caller within an organization; source/global protection can also return 429 | Back off and honor Retry-After when present; creating another key for the same caller does not reset their admission count |
503 products_unavailable |
No callable product behind the requested id right now | Check the deployment's status, or pick another assigned model |
Console symptoms#
A page shows a "NoAccess" or denied state. The Go backend is the authority, not the UI: the server refused even though you reached the page. Check your role — most mutating pages need Owner or Administrator; Financial audit needs administration; proposing a deployment needs Owner.
A control I expect is missing. The client hides controls you cannot use, but the server enforces the gate anyway. Missing Propose deployment, New policy or Invite people means your role doesn't qualify — it is not a rendering bug.
The playground picker is empty. Either no Project that pays is selected, or the selected project has nothing assigned. An Owner/Administrator assigns products on the project's Products tab and grants you access on Members.
"Not bound to a Project — Refused on every call; create a new key." This is a
legacy key (403 key_unbound). It cannot be rotated or repaired — create a new key
and revoke the old one. See API keys.
A stream stopped without [DONE]. The reply was interrupted, not completed.
Treat it as unknown outcome: the call holds budget until verified usage settles it,
and it appears in Usage and call records under its invocation id.
A call is "Holding budget" for a long time. The outcome was uncertain (timeout, 5xx, unverified usage) and the reservation waits for operator reconciliation — abandoned holds become eligible after five minutes. It is a hold, not a charge; the charge is settled only from verified usage.
Money expectations#
- Every admitted call holds at least one micro-USD, even a free one — a project
with an empty available wallet is refused at
402before any dispatch. - Regenerate in the playground is a new charge; Retry delivery replays the same request under its idempotency key and does not double-charge.
- Revoking a key or removing a member stops the next call, never a running one — admitted work always settles so records stay reconcilable.