Operations
Errors
Every error code the API returns, what causes it and what to do next.
Errors use conventional HTTP status codes and a consistent JSON body. Branch on error.code, which is stable; message is for humans and may change.
Error object
{
"error": {
"type": "billing_error",
"code": "insufficient_words",
"message": "Your allowance is used and pay as you go is off, or its monthly cap is reached. Nothing was processed. This request needs 6,120 words; 2,400 are left in this period's allowance.",
"request_id": "req_01JD4R9QT2",
"doc_url": "/docs/errors#insufficient_words"
}
}Error codes
| Status | code | Cause | What to do |
|---|---|---|---|
| 400 | invalid_request | A parameter is missing, has the wrong type or is out of range. error.param names it. | Fix the request. Don't retry unchanged. |
| 400 | text_too_short | Detect input under 40 words, or Rewrite input under 20 words. | Send more text, or skip detection for short snippets. |
| 401 | invalid_api_key | Missing, malformed, revoked or expired key. | Check the Authorization header and key mode. |
| 402 | insufficient_words | Your allowance is used and pay as you go is off, or its monthly cap is reached. Nothing was processed. Also returned when the workspace has no plan. | Upgrade, or switch on pay as you go (or raise its cap) in Dashboard → Billing. |
| 403 | permission_denied | The key lacks a scope, a test key touched a live resource, or the feature needs a higher plan (deep strength, calibrated Detect, more voices). | Use a key with the scope in error.param, or upgrade. |
| 404 | not_found | Unknown voice, job or endpoint, or it belongs to another workspace. | Check the id and the key's workspace. |
| 409 | idempotency_conflict | Same Idempotency-Key reused with a different body. | Use a new key per distinct request. |
| 410 | result_expired | The job's result was already delivered (sync response, an earlier GET, the event stream or a webhook) or wasn't collected within 60 minutes, so it was deleted. | Save the result the first time you receive it. Run the request again if you need it (that is billed again). |
| 413 | text_too_long | Input over 25,000 words. | Split the document or contact us for higher limits. |
| 422 | fidelity_failed | Strict mode: a protected fact (number, unit, name, link, quote) changed in the output. Not billed. | Retry once, then route to a person. |
| 422 | platform_constraint | The text can't fit the platform preset without dropping a protected fact. | Shorten the source or use another platform. |
| 429 | rate_limited | Per-key request, concurrency or word limit exceeded. | Wait for Retry-After, then back off. |
| 500 | internal_error | Something failed on our side. | Retry with backoff; quote request_id to support. |
| 503 | overloaded | Temporary capacity limit. | Retry with backoff, or use async jobs. |
capacity_unavailable | An accepted job could not finish in time (after repeated waits, or 120 minutes after it was accepted) because model capacity stayed unavailable. Reported on the job and its job.failed webhook. Nothing was billed and your text was deleted. | Submit the job again later. |
422 fidelity_failed
The one error that is part of normal operation. It means the system worked: a fact changed during rewriting and we refused to return it.
{
"error": {
"type": "fidelity_error",
"code": "fidelity_failed",
"message": "1 protected fact changed between source and output. Nothing was billed.",
"request_id": "rw_01JD4R2N7P",
"changed_facts": [
{
"type": "number_unit",
"source": "14 days",
"output": "14 weeks"
}
]
}
}See Fidelity checks for the rules.
What to retry
- Retry with backoff:
429,500,503, and network timeouts. - Retry once, then escalate:
422 fidelity_failed. - Don't retry: other
4xxcodes. The same request will fail the same way.
Note:
Send anIdempotency-Key header on POST requests so a retry after a timeout can't bill you twice. A replay answers from the original job (we never store response text): an async request gets the job; a sync request gets its result if the first response never reached you, otherwise 410 result_expired.