Errors
The /api/ error envelope and every code it can carry, with what each one means and what to do.
Every refusal on /api/ is one JSON envelope, and every envelope carries a code to branch on, a hint that says what to do next, and a docs link to the page for its code. /api/graphql sends the same fields inside each error's extensions.
{
"error": {
"type": "authentication",
"code": "invalid_key",
"message": "This API key is not valid.",
"docs": "https://docs.capacms.com/errors/invalid_key"
},
"meta": {
"version": "2026-10-01",
"contract": 1,
"requestId": "req_0f3c…"
}
}Branch on code, never on message: messages are written for people and may be reworded. type is the coarse class, stable for each status. A 500 never carries details; quote meta.requestId to Capa support. The full envelope is in the API reference.
All 31 codes
| Code | Status | type | Meaning |
|---|---|---|---|
persisted_query_not_found | 200 | invalid_request | No document is stored for that hash. |
invalid_version | 400 | invalid_request | Capa-Version names a date Capa does not serve. |
unknown_field | 400 | invalid_request | A select, filter or sort names a field the model does not have. |
invalid_filter_value | 400 | invalid_request | A filter value does not fit the field's type, for example text for a number or a non-ISO date. |
invalid_operator | 400 | invalid_request | The filter or sort uses an operator this field type does not support. |
invalid_cursor | 400 | invalid_request | The after or before cursor is malformed, was minted for another sort, or belongs to another parent entry. |
invalid_select | 400 | invalid_request | The selection cannot be planned, for example a relation modifier where it does not apply. |
invalid_parameter | 400 | invalid_request | A request parameter is missing or out of range: first outside 1 to 200, more than 3 sort values, a non-UUID id, bad JSON in variables. |
query_too_complex | 400 | invalid_request | The query is over a budget, and the message says which, what it measured and the limit. |
count_unavailable | 400 | invalid_request | totalCount could not be computed cheaply for this filter (a relation filter matching over 50,000 entries). |
graphql_parse_failed | 400 | invalid_request | The GraphQL document has a syntax error. |
graphql_validation_failed | 400 | invalid_request | The document parses but asks for something the key's schema does not have: a field, an argument, a type or a missing variable. |
persisted_query_hash_mismatch | 400 | invalid_request | The sha256 sent does not match the query text sent. |
missing_key | 401 | authentication | The request carried no x-api-key header. |
invalid_key | 401 | authentication | The key is unknown, revoked or expired. |
preview_token_invalid | 401 | authentication | The preview token is forged, mangled or for another project. |
preview_token_expired | 401 | authentication | The preview token was real but has expired. |
subscription_required | 402 | payment | The project's plan does not include API access right now. |
scope_missing | 403 | permission | The key is valid but lacks the scope this route needs. |
origin_refused | 403 | permission | The key is restricted to other origins than the one this request came from. |
edge_only | 403 | permission | The request reached the origin directly. |
contract_not_found | 404 | not_found | Capa-Contract names a contract this project does not have. |
model_not_found | 404 | not_found | The model in the path does not exist, or this key cannot read it. |
entry_not_found | 404 | not_found | No entry with that id is visible to this key. |
page_not_found | 404 | not_found | No model declares that page and no read has reported it. |
route_not_found | 404 | not_found | No route under /api/ matches the method and path you sent. |
mutations_not_enabled | 405 | method | The request tried to write. |
rate_limit_exceeded | 429 | rate_limited | Capa refused the request to protect the service, for one of two reasons the message names: too many reads running at once (the API's read gate, per client, per key or per project), or too many uncached requests from this key in a minute (the CDN's limit). |
internal | 500 | api_error | Capa hit an unexpected error. |
service_unavailable | 503 | api_error | The API was busy with other projects' reads for the whole database budget, or had no database connection free in time. |
query_timeout | 504 | api_error | The database stopped the query at the statement timeout. |