Errors
Every error has the same JSON shape: a code to branch on, a message to show, and a request ID to quote when you contact us. Validation errors also say which fields are wrong.
The error envelope
Errors return a non-2xx status and an error object. code is a stable identifier for your code to check; message is a short explanation that may change. Messages never include internal details or the values you sent.
{
"error": {
"code": "DOCUMENT_NOT_FOUND",
"message": "No document found for the given document_id.",
"request_id": "req_4f1c9e0b2a7d4c3e8b6a5f0e1d2c3b4a"
}
}Some errors add fields: rate limits add limit and retry_after_seconds (and a Retry-After header), and validation errors add details. Common statuses:
| HTTP | error.code | Typical error.message |
|---|---|---|
| 401 | UNAUTHORIZED, INVALID_TOKEN, BEARER_TOKEN_REQUIRED | Missing, invalid or expired key or token |
| 402 | FREE_TIER_LIMIT_REACHED, TRIAL_ENDED, OVERAGE_LIMIT_REACHED | Trial allowance used up, the trial has ended, or the overage limit is reached |
| 403 | FORBIDDEN | Your account or key may not do this |
| 404 | NOT_FOUND, DOCUMENT_NOT_FOUND, … | The resource does not exist or is not yours |
| 422 | VALIDATION_ERROR | The request body, query or path is invalid (see details) |
| 429 | RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED | Too many requests, or the monthly allowance is used up |
| 500 | INTERNAL_ERROR | An error occurred |
Request IDs
Every response, successful or not, has an X-Request-Id header (req_ followed by 32 hex characters), and error bodies repeat it as error.request_id. Log it with your own errors, and include it when you contact support so we can find the exact request.
Validation errors and bad JSON
A request that is not valid JSON, is missing a required field, has a value of the wrong type or out of range, or includes a field the endpoint doesn't accept gets 422 VALIDATION_ERROR. details lists each problem (up to 20): where it is (body, query or path), the field (with array positions, such as page_numbers[1]; null for a rule about the whole request) and what is wrong:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request",
"request_id": "req_9a8b7c6d5e4f40312a1b0c9d8e7f6a5b",
"details": [
{ "location": "body", "field": "page_number", "issue": "Unknown field." },
{ "location": "body", "field": "page_numbers[1]", "issue": "Must be an integer." }
]
}
}page_number fails with Unknown field. instead of being ignored.Failed jobs
A job that fails after it was accepted is not an HTTP error: GET /v1/jobs/{id} returns 200 with status: "failed" and an error object with a code (such as PDF_CORRUPT, TIMEOUT or INTERNAL_ERROR) and a message you can show. See Jobs.
Key management errors
Situations to model against when you integrate key management on /v1/keys:
| Situation | Status | Notes |
|---|---|---|
| No Authorization header on /v1/keys | 401 | BEARER_TOKEN_REQUIRED. Key management uses a sign-in token, not an API key. |
| Invalid or expired token, bad signature, wrong audience | 401 | INVALID_TOKEN with the message “Authentication failed”. The reason is never given. |
| Sign-in service unreachable | 500 | AUTH_CONFIG_ERROR. Retry later. |
| Invalid JSON, a wrong type or an unknown field | 422 | VALIDATION_ERROR, with one entry in details per problem. |
| Revoking an unknown key_id, or another user's | 404 | NOT_FOUND with the message “Key not found”. |
402: allowance used up
402 is returned when you submit an analysis job (for example a drawings or specs job) with an API key and the plan's trial allowance is used up (FREE_TIER_LIMIT_REACHED in the code detail), or the trial has ended (TRIAL_ENDED). An allowance error also carries unit, used, allowance, remaining and requested. On the Developer plan, usage past the monthly allowance is billed as overage up to your organization's overage limit (its owner sets it in the portal); a job that would pass the limit gets OVERAGE_LIMIT_REACHED, which also carries overage_limit_cents, overage_spent_cents, overage_requested_cents and reset_at. Creating or listing keys never checks the allowance, so you should not expect 402 on POST, GET or DELETE /v1/keys. See Usage for how analyses are counted.
429: rate limits
RequestRateLimitMiddleware rate-limits only specific POST paths (/v1/documents, /v1/specs/..., /v1/drawings/..., /v1/batches). /v1/keys is not in that set, so those limits don't apply to key minting. (You could still hit other infrastructure limits.) A paid plan whose monthly allowance is used up, with no overage billing, also gets 429 (QUOTA_EXCEEDED); check error.code to tell the two apart. A rate-limited response has a Retry-After header and retry_after_seconds in the body.
403: elsewhere in the API
The keys router itself rarely returns 403; the Cognito path uses 401 for auth failures. 403 appears on other routers for "forbidden" cases.