Model APIs
Request errors
Use the HTTP status to decide whether to correct a request, restore access, follow an existing job, or retry later. A failed HTTP request and a failed generation are separate events: a successful job read can report a failed job.
Response structure #
Problem responses use the same Error object as Model errors. status describes this HTTP response; code, when supplied, gives a stable reason. Show detail to the customer and keep request_id for support. Do not require a code or request id on every error: the captured 401 below has neither.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | No | Machine-readable error code.… |
type |
string | Yes | A URI reference identifying the problem type. |
title |
string | Yes | |
status |
integer | Yes | |
detail |
string | No | |
instance |
string | No | |
job_id |
string | No | The job this refusal points at, so a client can follow it without reading detail.… |
request_id |
string | No |
There is no separate runner-error object or error-type header; the problem object and the job's failure are the available error surfaces.
Status reference #
The example routes below declare these responses in the OpenAPI contract. 500 is covered by the route's default problem response and verified in its handler. Other routes can use the same status for a different refusal, so read their response contract too.
| Status | When it happens | Retry unchanged? | Example endpoint |
|---|---|---|---|
400 |
Invalid input, unsupported fields, or a malformed upload. A generation may supply validation or a remembered moderation code. |
No; correct the request. See the new-model rollout exception. | POST /assets |
401 |
Authentication is missing, invalid or expired. | No; obtain a valid credential first. | GET /me |
402 |
Insufficient credits, a member budget ceiling, or a plan requirement. | No; resolve the balance, budget or plan. | POST /generate/image |
403 |
The credential or organization role lacks permission. | No; resolve access with the owner or administrator. | POST /assets/{id}/share |
404 |
A resource is unknown or not visible in the current scope. | No; check its id and scope. | GET /jobs/{id} |
408 |
The long-poll window closed before the job became terminal. | Yes; wait again on the same job id. | GET /jobs/{id}/wait |
409 |
Duplicate generation submission; other endpoints also use it for busy or conflicting state. | Follow the supplied job_id; do not create a replacement automatically. |
POST /generate/image |
410 |
A share link expired, was revoked, or points to trashed/deleted media. | No; ask the owner for a new usable link. | GET /share/{token} |
413 |
The uploaded file exceeds the accepted size. | No; reduce the size or use the appropriate upload flow. | POST /assets, POST /assets/uploads |
422 |
A generation or cost-confirmation gate refused the request. | No blind retry; inspect code. Re-quote for confirmation_rejected. |
POST /generate/image |
429 |
A rate limit was reached; generation routes also use it for concurrency or quota. | Yes, after the supplied delay or reset time, with backoff. | GET /share/{token} |
500 |
The server could not complete its operation, such as a job lookup failure. | Retry a read with backoff; establish whether a write took effect before repeating it. | GET /jobs/{id} (default problem response) |
502 |
An upstream model or service failed. | Yes for a temporary failure, with backoff and the same generation key. | POST /generate/video/enhance-prompt |
503 |
A required service or feature is temporarily unavailable. | Yes with backoff; persistent configuration unavailability needs support. | POST /jobs/{id}/sse-ticket |
This unauthenticated request returned the following production response on 2026-09-21.
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "valid authentication is required"
}
| Field | Meaning |
|---|---|
type |
Problem type URI. |
title, status |
The request was unauthorized; this is not a credit refusal. |
detail |
Authenticate before retrying. |
A repeated generation returned this production 409. The missing code is deliberate: status and job_id fully identify the duplicate.
{
"detail": "this exact request was already submitted as job 35b6ff8d-0c78-440b-bdb3-bfe476b56d76 less than 5m0s ago and has not been billed twice — check it with GET /jobs/35b6ff8d-0c78-440b-bdb3-bfe476b56d76. To run it again anyway, resubmit with a different Idempotency-Key header.",
"job_id": "35b6ff8d-0c78-440b-bdb3-bfe476b56d76",
"request_id": "localhost/7bnUUXx7b2-007291",
"status": 409,
"title": "Conflict",
"type": "about:blank"
}
| Field | Meaning |
|---|---|
job_id |
The already accepted generation to read or wait for. |
request_id |
This refusal's correlation id for support. |
status, title, type |
The duplicate was refused with 409; there was no second bill. |
detail |
Explanation for the customer; use job_id directly instead of extracting it from this text. |
Handling request errors #
Retry 408, 429, 502 and 503 with bounded exponential backoff and jitter. A 408 from wait retries only the wait, not generation submission. On 429, pause your queue so parallel workers do not immediately consume the next available slot together. Stop after a bounded number of attempts and preserve the last problem and request id.
Do not automatically retry 401, 402, 403 or 404. They require a valid credential, sufficient balance or entitlement, permission, or the correct resource and scope. Resolve 400, 410, 413 and 422 using the status table rather than repeating the same input.
| Header | Where it is supplied | How to use it |
|---|---|---|
Retry-After |
Public share-link 429 responses use 60 seconds. OAuth token/revocation 503 responses also declare it. |
Do not retry before the indicated delay. Generic HTTP clients should also handle the standard HTTP-date form. |
Retry-After-Reset |
Generation quota refusals when a reset time is available. | Wait until that RFC 3339 UTC instant before submitting again. |
| Neither | Some concurrency and transient service errors do not include a retry header. | Use bounded exponential backoff with jitter; a missing header does not mean retry immediately. |
If you already have a job id, keep following that job through temporary read errors. Read failure.code and failure.credits_refunded only when the generation itself fails; a failed status request does not prove that happened. See Model errors for typed refusals and job failures.

