Skip to content

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.

json
{
  "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.

python
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

CodeRetry?How
unauthorized, forbidden, not_foundNoFix the request
invalid_request, unsupported_formatNoFix the input
insufficient_credits, plan_required, subscription_requiredNoChange the account, then retry
rate_limitedYesAfter retry_after_seconds
backend_unavailable, internal_errorYesExponential 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.