Errors
Errors carry a stable machine-readable code and a human-readable message. Branch on code; display message. Codes will not change meaning within /v1.
{
"error": {
"code": "insufficient_credits",
"message": "Your balance does not cover this rig."
}
}Codes
unauthorized — 401
The API key is missing, malformed, revoked, or unknown. The response is identical in all four cases so the endpoint cannot be used to probe for valid keys.
Check that you are sending Authorization: Bearer ck_live_..., that the key has not been revoked on the keys page, and that your secrets manager is handing you the value you think it is.
Do not retry. A retry with the same key gets the same answer.
forbidden — 403
The key is valid but the resource belongs to a different account. Usually means a rig id from one account is being polled with another account's key.
Do not retry.
not_found — 404
No rig with that id, or no such endpoint. Check the id came from a POST /v1/rigs response, and check the path against the API reference.
Do not retry.
invalid_request — 400
The body is malformed or a parameter value is not recognised. message names the specific field. Common causes: neither model_url nor model_base64 supplied, an unknown rig_type, or an output_format other than glb or fbx.
Do not retry without changing the request.
unsupported_format — 400
The file could not be read as a riggable mesh. Either the format is not supported, the file is corrupt, or it is a ply/vrm file, which are recognised but not riggable.
Note that binary STL is only detected when the URL or filename ends in .stl, since it has no magic bytes to sniff. See formats.
Do not retry with the same file.
insufficient_credits — 402
The account balance will not cover the rig. Top up at cinevva.com, or check headroom before a batch with GET /v1/account.
Do not retry until the balance changes.
subscription_required — 402
The key is valid but the account has no active Standard or Pro subscription. API access is a paid-plan feature, so this fires on every /v1 route, not only the billable ones.
The response includes the current plan and subscription_status so you can tell a lapsed subscription from one that was never started. Manage plans at cinevva.com/pricing.
Do not retry until the subscription is active. If you want to catch a lapse before it breaks a pipeline, poll GET /v1/account and watch cancel_at_period_end.
plan_required — 402
The requested engine needs a higher plan than the account holds. In practice this means engine: "pro". Either upgrade, or fall back to fast.
rate_limited — 429
More than 60 requests in the trailing minute for this key. The response includes retry_after_seconds, and every authenticated response carries X-RateLimit-Remaining.
Retry after the window resets. See rate limits for backoff guidance.
backend_unavailable — 503
The rigger is at capacity or temporarily unavailable. This is a capacity signal, not a problem with your request; the same request will likely succeed shortly.
Retry with exponential backoff. Capacity pressure is reported as 503 rather than being disguised as a 429, so that "you sent too much" and "we are busy" stay distinguishable.
internal_error — 500
Something failed on our side. Retry with backoff, and if it persists, tell us with the rig id and rough timestamp.
Failed rigs are not HTTP errors
A rig that is accepted and then fails during processing returns 200 from GET /v1/rigs/{id} with status: "failed" and a populated error string. Only the submission itself produces a 4xx/5xx.
job = get_rig(rig_id) # HTTP 200
if job["status"] == "failed": # but the rig did not work
handle(job["error"])Common in-processing failures: geometry too sparse or fragmented to infer a skeleton, a mesh above the face limit, or a file that decoded but turned out not to contain a usable mesh. Model requirements covers avoiding these.
Retry policy summary
| Code | Retry? | How |
|---|---|---|
unauthorized, forbidden, not_found | No | Fix the request |
invalid_request, unsupported_format | No | Fix the input |
insufficient_credits, plan_required, subscription_required | No | Change the account, then retry |
rate_limited | Yes | After retry_after_seconds |
backend_unavailable, internal_error | Yes | Exponential backoff, cap at ~5 attempts |
A retry loop that ignores this table and retries everything will hammer the API with requests that cannot succeed, and burn your rate limit doing it.