Errors
Every failure has the same shape:
{
"error": {
"code": "insufficient_credits",
"message": "You don't have enough AI Credits for this."
}
}Branch on code, never on message — the wording can change, the code won't.
| Status | Code | What happened |
|---|---|---|
| 400 | invalid_request | Something in the body or query is wrong; message says what |
| 400 | invalid_json | The body wasn't a JSON object |
| 400 | invalid_job_handle | A job handle that isn't <pack>:<kind>:<name> |
| 401 | missing_api_key | No Authorization: Bearer header |
| 401 | invalid_api_key | The key isn't one of ours, or has been revoked |
| 402 | insufficient_credits | Out of AI Credits |
| 403 | plan_required | The key is valid but the account is on Starter |
| 403 | forbidden | Valid key, but not this action in this pack |
| 403 | pack_limit_reached | Your plan's pack limit |
| 403 | asset_limit_reached | This pack's asset limit |
| 404 | not_found | No such pack, asset or job |
| 409 | still_generating | You asked for an asset's bytes before it was ready |
| 409 | generation_failed | That asset's generation failed |
| 503 | unavailable | The feature behind this endpoint is switched off |
Each endpoint lists the codes it can actually answer, under Error codes.
Credits and failures#
A generation takes its credit when the job is accepted, not when it
finishes. If the run fails, the credit is refunded automatically and the asset
is left in the pack carrying the error — so a 402 means you were out before
anything started, and a job that ends error has already been refunded by the
time you read it.