Error codes
Every error the API returns is JSON — an unexpected internal failure included —
and there are only three shapes it can take.
Branch on the type of detail — string or array — not on the
status code: several 422s carry a string detail where you
might expect the framework’s validation array. Where a rejection needs to be
machine-readable, it puts a stable token at the front of that string or carries an
error_code field.
Three response shapes
All three are application/json.
1. Standard error — a string detail. The common
case for every explicit rejection.
{"detail": "job not found"}
2. Validation error — an array detail. Emitted by
the framework for malformed path, query, header, or body parameters. Always
422. Each entry carries loc (the path to the offending
value), msg, type, and optionally input and
ctx.
{
"detail": [
{
"loc": ["query", "limit"],
"msg": "Input should be less than or equal to 500",
"type": "less_than_equal",
"input": "900",
"ctx": {"le": 500}
}
]
}
3. Pipeline error — detail plus error_code and
request_id. Emitted when a typed pipeline exception reaches
the boundary — the status comes from the exception class — and by an unexpected
internal failure, which answers 500 with
{"detail": "internal error", "error_code": "internal_error", "request_id": "…"}.
request_id is never null: it is the same id as the
response’s X-Request-ID header — yours when you sent one,
otherwise one the server generated — so every failure is traceable.
{
"detail": "document layout is unavailable",
"error_code": "validation_error",
"request_id": "b0a1f2c3d4e5f60718293a4b5c6d7e8f"
}
422 uses the array shape. Language
rejections, batch expansion failures, the storage-binding validations, the
terminology and translation-memory capacity rejections, a content-policy
refusal, and every billing request-body rejection return 422 with a
string detail. Type-check detail
before you index into it.
Stable tokens
Four rejections put a stable token at the front of a string detail
so you can branch without parsing prose. Everything after the token is
human-readable detail — counts, allowance, remediation — whose wording may change,
so match on the prefix only.
| Token | Status | Endpoint | Meaning |
|---|---|---|---|
workspace_id_required: |
401 |
POST /api/oauth2/token (password grant) |
A password sign-in sent without a workspace ID matched none of the
accounts it checked, and the email address belongs to more workspaces than
that sign-in searches. Sign in again with your workspace ID in
tenant_id — the sign-in page’s optional
Workspace ID field. |
terminology_max_terms_exceeded: |
422 |
POST /api/terminology |
The upload would take the tenant past its terminology allowance for the language pair. Nothing was stored. |
tm_max_segments_exceeded: |
422 |
POST /api/tm |
The import would take the tenant past its translation-memory allowance for the language pair. Nothing was imported. |
tm_import_in_progress: |
409 |
POST /api/tm, DELETE /api/tm/{set_id} |
An import into the same set name is still running, whether it runs inline or in the background. An import replaces its set wholesale, so a second import cannot overlap it, and a delete would be undone when it lands. Nothing was imported or deleted — wait for the running import to finish (for a background import the message names the job to poll), then retry. |
error_code values
These are all the values that exist. Each maps to exactly one HTTP status, but a
status can carry more than one value — and most statuses are also returned
without one — so switch on error_code, the stable half.
error_code | Status | Meaning |
|---|---|---|
validation_error | 400 |
The request, or the referenced resource’s state, was invalid — an unsupported document format, a file that cannot be read or is past a document limit, a document with no layout, an approve on a job that is not translated yet. |
not_found | 404 |
A referenced job or resource does not exist — including a job deleted while it was still processing. |
conflict | 409 |
The request conflicts with current state. |
storage_binding_not_verified | 409 |
Bring Your Own Storage is configured for the workspace but cannot take
the work: the binding is not verified, or its stored secret is missing or
unreadable. A translation submission or document upload is refused before
anything is created or translated — verify the binding (re-entering the
secret if it is gone), then resubmit. A document download, review or
approval that has to reach that storage is refused the same way. When a
text job’s golden record hits an unverified binding instead (a review
approval, say), that job is parked as persistence_failed and
publishes once the binding is verified, the workspace returns to platform
storage, or the binding is deleted. |
content_filtered | 422 |
The model provider’s content policy refused the text. The same text is refused again on every retry — change the text. |
rate_limit | 429 |
Translation capacity is temporarily saturated, even after the service’s own retries. Retry with backoff. |
quota_exceeded | 429 |
A document found no allowance left this month once extraction had counted
its characters. Nothing was translated or billed. A wait=true
submission returns it; an asynchronous job records it as its failure code.
Retrying cannot help until a top-up or the next month adds headroom. |
internal_error | 500 |
Unexpected internal failure. The body’s request_id is
what support needs to find it. |
persistence_failed | 502 |
Writing the golden record failed after the translation finished. The
translation is kept — readable through GET, and billed — and
the job is parked as persistence_failed; verifying storage
(POST /api/storage/binding/verify) publishes it. |
transient_error | 503 |
A retryable upstream failure — the model provider or a storage dependency (the service’s own secret storage included) was unavailable, even after the service’s own retries. Safe to retry with backoff. |
service_unavailable | 503 |
A required dependency is unavailable or misconfigured. |
stage_timeout | 504 |
A wait=true document request whose processing stage used up
its time budget; the job is failed with that same code. The stage’s
work is fixed by the document, so resubmitting it unchanged is likely to
run out of time again — split it. |
transient_error and rate_limit are the
retryable set. Everything else, stage_timeout included,
indicates the request will fail the same way if you replay it unchanged — fix the
request or the resource state first.
HTTP status reference
What each status means in TrueIdiom, and the one thing to do about it. The exact
detail string you get back depends on the endpoint —
Conventions & errors lists
every detail string per status.
| Status | What it means | What to do |
|---|---|---|
200 |
Success — including synchronous submissions, reviews, approvals, and
deletions, which return a body of cascade counts rather than a
204. |
Nothing. Read the body. |
202 |
Accepted and genuinely in flight: a document translation sent to
POST /api/documents/translate without wait=true,
or a large TMX import. (A document uploaded to
POST /api/translations comes back 200 with the
queued job.) |
Poll the job at the URL in the body. Async jobs are poll-only. |
204 |
Deleted, no body — a terminology or translation-memory set, or a withdrawn invite. | Nothing. Do not parse a body. |
400 |
Malformed or missing input the endpoint itself checks — a bad
api-version, an empty or missing upload, blank source text, a
missing target_lang, a file that cannot be read (corrupt,
password-protected, or text that is neither UTF-8 nor UTF-16 with a
byte-order mark), or a Word or Excel file past a document limit. Also every
validation_error pipeline error. |
Fix the request. Retrying it unchanged fails identically. |
401 |
No usable credential: a missing or invalid API key, tenant credentials, or bearer token — or a sign-in step that failed: a wrong password, a wrong MFA code, or an MFA challenge that has expired or been used up. | Send a valid credential, or refresh an expired token; a used-up MFA
challenge means starting again from the password step. For
workspace_id_required:, sign in with your workspace ID. See
Authentication. |
402 |
Billing is on and the workspace cannot start this billable work — no active subscription; the free trial is used up, has ended, or has fewer characters left than the request needs; or the request is one the trial does not cover (a PDF, Word or Excel document, or a call made with an API key). A top-up also needs an active subscription. | Add a payment method at /billing. |
403 |
Authenticated but not permitted: the admin role is required, the action is reserved for the service operator, your organization requires an invitation to join through single sign-on, or the sign-in method you tried is switched off for this deployment. | Use an administrator credential, an invite link from your workspace admin, or the sign-in method the deployment allows. Never retry as-is. |
404 |
The job, document, artifact, set, tenant or user does not exist — or is not yours to see, or was deleted while it was still processing. | Check the id. Do not treat it as transient. |
409 |
A conflict with current state: the golden translation is not available at this job status; a document download before the job is translated (still in progress, or failed); an import into the same translation-memory set is running; the workspace’s own storage binding is not verified or has lost its stored secret; a billing change the account’s state rules out; or a required capability is not enabled. | Wait for the state to change and re-read, or enable the capability. A
failed document has no translation to download — resubmit it. For
tm_import_in_progress:, let the running import finish first;
for storage_binding_not_verified, verify the binding, then
resubmit. |
413 |
The upload, request body, source text or batch is past a size ceiling. | Split the work or shrink the file. See Limits & quotas. |
415 |
The file type is not one this endpoint accepts — a source document, a terminology file, or a TMX. | Convert the file. See Supported file formats. |
422 |
The request parsed but the values do not hold up: framework parameter
validation (array detail), an unsupported language code, a
batch that would not expand, a storage-binding validation, a terminology or
TM capacity rejection, or text the model provider’s content policy
refused (content_filtered). |
Fix the request. Type-check detail, and match the stable
token prefix for the capacity rejections. Refused text is refused again
unchanged. |
429 |
Rate limited — too many attempts, too many failed MFA codes, or
translation capacity saturated (rate_limit) — or the monthly
character allowance has no room for the request, including a document
refused once its characters were counted
(quota_exceeded). |
Honour Retry-After where present and back off; do not retry
inside the window. For an exhausted allowance, take the remedy the
detail names at /billing — a top-up for
this month, or an upgrade, which raises the allowance immediately and is
charged the prorated difference at once (a downgrade waits for renewal) —
or, once the month’s ceiling is reached,
contact us. |
500 |
An unexpected internal failure (internal_error), or a stored
document artifact that failed its integrity check. |
Do not use a download that failed its check. Retry once; if it persists,
give support the request_id — the same id as the
X-Request-ID response header. |
502 |
A backend needed for a terminology or TM import was unavailable, writing the golden record failed, or the workspace’s platform storage could not be set up. | Retry the import or the storage change. A golden-record failure parks the
job with its translation intact and billed, so do not resubmit it —
POST /api/storage/binding/verify publishes it. A TM import that
ran asynchronously records the same message on the import job’s
error instead of returning 502. |
503 |
Retryable upstream failure (the service’s own secret storage included), a misconfigured dependency, or a capability switched off or not set up on this deployment (translation, billing or top-ups). | Retry with backoff. If the message says a capability is disabled or not configured, retrying will not help — that is a deployment setting. |
504 |
A wait=true document request whose processing stage used up
its time budget (stage_timeout); the job is marked failed. |
Split the document; resubmitting it unchanged is likely to run out of time again. |
507 |
This workspace’s stored document artifacts, plus the upload being submitted, would pass the cap this deployment sets for platform-hosted bytes. | Delete document jobs you no longer need, or bring your own storage so the bytes land in your account instead. Bound tenants skip the check entirely. |
404, never 403. Existence is never confirmed to a caller
who may not see it, so a 404 means “not yours or not
there” — the two are deliberately indistinguishable.
What's next
- Conventions & errors —
the canonical catalog, including every
detailstring each status can carry. - Limits & quotas — the ceilings behind the
413,422and429rejections. - Text translation — the endpoint most of these statuses come from, with the errors it can raise.