Errors come back in a JSON envelope:
{
"error": {
"code": "insufficient_budget",
"message": "Top up this Project wallet before making a call",
"requestId": "req_…"
}
}Every /v1 response is JSON — including 404s and 5xx; there is no HTML error
page behind this prefix.
Credentials — 401#
| Code | Means |
|---|---|
invalid_api_key |
no bearer token, or the key is unknown, expired or revoked |
Terminal for that request — retrying with the same key never helps. (The
session-based console API uses 401 unauthenticated for a missing or revoked
cookie; the bearer surface always answers invalid_api_key.)
Money — 402#
| Code | Means | What clears it |
|---|---|---|
insufficient_budget |
the project wallet cannot cover the reservation | an Owner tops up the project |
Access and policy — 403#
| Code | Means | What clears it |
|---|---|---|
project_mismatch |
a selector named a project other than the key's bound project | drop the selector or restate the bound project |
key_scope |
the resource is eligible but excluded by the key's own allowlist | a key whose policy permits it |
key_unbound |
a legacy key with no bound project | issue a new project-bound key |
key_issuer_denied |
you may not issue a key for this project (key creation) | the membership + team grant + project grant rule in Authentication |
product_not_assigned |
the product is callable in the organization but not assigned to this project | an administrator assigns it |
policy_denied |
an invocation policy refused the model or the output size | the policy owner |
budget_limit |
an invocation policy's daily/monthly spending ceiling is reached | the window rolls or the Owner raises it |
key_limit |
the key's own spending limit is spent | its reset window rolls |
denied |
the caller lacks consumption authority — no project grant, or a suspended membership, organization or project | the grant or suspension is restored |
Denied calls create no usage record and no reservation.
Not found — 404#
| Code | Means |
|---|---|
model_not_found |
no resource with this id is eligible for the caller right now — a guessed id, a draft, a suspended deployment, a removed assignment, or another project's resource |
not_found |
the named object (key, sandbox product, project…) does not exist in the caller's scope |
A 404 for something that exists elsewhere is deliberate: cross-project and cross-organization access is concealed, not distinguished.
Conflict — 409#
| Code | Means |
|---|---|
revision_not_active |
external invocation runs the actively deployed sandbox revision; omit revision or activate that one |
revision_conflict |
a sandbox save's baseRevision is stale — someone saved first; reload before saving |
state |
the record is in the wrong state for the action — e.g. rotating a non-active key, or touching an archived sandbox product |
key_unbound |
a legacy unbound key cannot be rotated |
Request shape — 422#
| Code | Means |
|---|---|
unsupported_parameter |
an unknown field, or a known field outside its bounds — see Chat completions |
project_required |
the call or key creation did not name a project where one is required |
sandbox_contract |
a sandbox id in model whose contract is not one text in, one text out — use /v1/sandbox/{id}/invoke |
resource_not_eligible |
a key allowlist entry is not an eligible resource of the bound project |
invalid |
a malformed value — bad message role, a tool message without tool_call_id, out-of-range temperature |
context_limit |
the request exceeds the model's published context or output limit |
media_endpoint |
a media-only product was sent to chat completions — invoke it through a sandbox media node |
All 4xx refusals happen before any reservation, so they are never charged.
(A body that is not one well-formed JSON document is refused earlier still, with
400 invalid_body.)
Rate limit — 429#
One code: rate_limit — 30 requests per minute per caller within an
organization; the window is one minute. See Rate limits.
Upstream and internal — 5xx#
| Status | Code | Means |
|---|---|---|
502 |
provider_failed |
the provider gave a definite refusal — the reservation is released |
502 |
provider_reconciling |
the outcome is uncertain (timeout, incomplete answer, unknown acceptance) — the reservation is held for reconciliation |
502 |
invalid_response |
the provider returned no usable completion |
503 |
authority_unavailable |
the authority check could not be completed — nothing was dispatched |
504 |
timeout |
the request deadline was exceeded |
Mid-stream, the same failures arrive as data: frames instead — codes
provider_failed, provider_reconciling or settlement_pending, each carrying
the invocation id as id.
Never retry a 5xx blindly as a brand-new request. A timed-out or uncertain
call may still hold a reservation or even have completed provider-side; use the
invocation id to check the recorded outcome first. Replays with the same
Idempotency-Key are safe and never charge twice. There is no automatic paid
retry.
Nothing that never reached a provider is charged#
Credential failures, access refusals, policy stops, validation errors and empty wallets all happen before reservation or dispatch, and leave the balance untouched. A definite provider refusal releases the reservation; only an uncertain outcome holds one, and that hold is visible in usage until reconciliation resolves it.