Errors
Non-2xx responses use this envelope. Branch on error.code, not on the English message.
{
"error": {
"code": "not_found",
"message": "Project not found",
"request_id": "…"
}
}
Always log request_id and send it to support. The same value is also on the X-Request-ID response header.
Optional extras on the envelope include required / available / shortfall on payment_required, and current_status on not_awaiting_payment (intelligence resume) when the project status is known.
Do not parse a top-level detail string — that shape is gone on /v1.
Validation
400 / 422 with validation_failed may include field_errors:
{
"error": {
"code": "validation_failed",
"message": "…",
"request_id": "…",
"field_errors": [
{
"field": "source_language_code",
"message": "Field required",
"type": "missing"
}
]
}
}
field is the public field name (source_language_code, workflow.0, …). It is null when the offending name has no public equivalent.
field_errors is not always there, and when it is missing the key is absent — not null. You get it when the request failed against the request schema. You do not get it when the rejection is a rule about the request as a whole:
- an unknown language code (
unknown_locale_codes: …) - an unknown source language (
unknown source_language: …) - the source language also listed as a target (
source_in_targets: …) - an invalid
workflow(invalid_workflow: …) target_language_codesthat is not a JSON array of strings- an unsupported query parameter on
GET /v1/projects
All of those return validation_failed with the detail in error.message and no field_errors key at all. Read error.message in that case, and always reach for it defensively — error.get("field_errors", []), never error["field_errors"].
Common codes
| Code | Status | Meaning |
|---|---|---|
invalid_api_key |
401 | Missing, unknown, or revoked key |
insufficient_scope |
403 | Valid key, missing scope (required_scope is set) |
forbidden |
403 | Authenticated, but not allowed for this resource |
not_found |
404 | Resource missing or not visible to this key — including an unknown project |
method_not_allowed |
405 | Wrong verb on a real route |
gone |
410 | Route removed on purpose; the body names the replacement when there is one |
validation_failed |
400 / 422 | Bad body or query — see the note on field_errors above |
missing_api_version |
400 | You sent X-API-Version with a value other than 1 (omitting the header is fine) |
payment_required |
402 | Credits short — envelope also has required, available, shortfall |
not_awaiting_payment |
409 | Resume called when the project is not parked for payment — envelope may include current_status on the intelligence-credit path |
payload_too_large |
413 | Upload over 200 MB total or 100 files |
idempotency_in_progress |
409 | Idempotency-Key already used — in flight, or its outcome is unknown |
idempotency_conflict |
409 | Generic 409 fallback. Create never emits it |
rate_limited |
429 | Honor Retry-After and X-RateLimit-* |
upstream_unavailable |
502 / 503 / 504 | Safe to retry idempotent calls |
internal_error |
500 | Unexpected failure — include request_id |
What to do
- Read
error.code. - For validation, walk
field_errors. - For
402, top up credits and call the matching resume route. - For
429, wait forRetry-After/X-RateLimit-Reset. - Keep
request_idwhen you open a ticket.