Errors and spend
All errors on the API have the same envelope, and each code is a lowercase token that you can branch on. This page lists all the codes of the Formats surface in one place. It puts the codes in groups, by the time when they occur. The groups are: before a run exists, while you read it, when the run fails, and when a webhook delivery failed. Spend and rate limits are at the end.
The error envelope
| Field | Use it for |
|---|---|
code | The stable token to switch on. It matches ^[a-z0-9_]+$ and is never a sentence. |
message | Text for a human. It can change. Log it. Never match on it. |
request_id | The API also sends it as the x-sume-request-id header. Give it to support. |
retryable, retry_after_seconds | Tells you if the same request can succeed when you send it again, and how long to wait first. |
next_action | authenticate, fix_input, add_funds, retry_later, poll_status, inspect_events or contact_support. |
category, stage, public_reason | Less specific labels for dashboards and alerts. |
details | Code-specific data: required_scope, workspace_id, violations[], index, status, scope, …. Each code below names its fields. |
Branch on the HTTP status first. Then branch on code. A 4xx at create means that nothing ran
and the API charged nothing. Thus, correct the call. Do not retry it. The most frequent and most
expensive mistake is to retry a 403 insufficient_scope in a loop.
Errors at create
These errors apply to POST /v1/formats/{handle}/{slug}/runs and POST …/bulk-runs. Nothing
runs, and the API charges nothing. A failed create releases its Idempotency-Key.
| Status | error.code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_request | One of these conditions is true. The body names none of instruction / input / previous_run_id / attachments. The body names both output_schema and response_format. input is not an object, has more than 64 top-level keys, or is more than 2 MiB. generation_spend_cap_credits is 0 or above the platform maximum. model is not in the catalog. A bulk body has bad concurrency or items. A webhook URL is not public HTTPS. The communication aliases do not agree. | Read message and details. |
| 400 | unknown_parameter | A top-level field that the API does not know. This also applies to thread_id. details.errors[].suggestion names the field that you probably intended. | Rename the field, or remove it. |
| 400 | output_schema_invalid | The schema is not in the supported subset. details.violations[] lists each { path, rule, message }. | Correct each violation. Refer to Supported schemas. |
| 400 | invalid_attachment | An attachments[] item is bad, or the attachments go above the media budget. attachments[] shares this budget with input URLs. | Refer to Attachment errors. |
| 400 | attachment_not_found | asset_id is unknown in this workspace. | Upload it, or use a URL. |
| 400 | previous_run_format_mismatch | previous_run_id names a run that started on a different Format. | Continue it on that Format. |
| 400 | previous_run_not_resumable | That run left nothing to continue. details shows previous_run_status, has_thread, and artifact_count. | Start a new run. |
| 401 | unauthorized | There is no key, a malformed key, a revoked key, a key for the other host, or two credentials at the same time. | Correct the header. |
| 402 | insufficient_credits | The workspace does not have enough credits for the run. details.required_credits and details.available_credits give the numbers. next_action is add_funds. | Add credits: upgrade the plan, or buy credits for the team. If you retry before you add credits, you get the same answer. |
| 402 | organization_wallet_not_provisioned | The organization workspace has no credits provisioned yet. details is the same as for insufficient_credits. | An admin must add credits to it. |
| 403 | insufficient_scope | The key does not have formats:write (details.required_scope), or it is a service-account key (details.reason: service_account_format_runs_unsupported). | Mint a new key with the scopes. You cannot add scopes to a key that you already have. |
| 403 | workspace_key_required | The Format is a team Format, and the key is a personal key. details.workspace_id names the workspace. For a team key from a different workspace, the API uses its grant to make the decision (the run starts, or 404). | Create a key in that workspace, or ask its owner for a grant. |
| 404 | format_not_found | The handle or slug is unknown, or the Format is archived. Or the Format is not in the workspace of your key. Or it is a team Format, and your workspace has no accepted grant for it (a pending or revoked grant gives the same result). | Examine the address and the key. For a shared Format, ask the owner for a grant. |
| 404 | previous_run_not_found | previous_run_id is unknown or not yours. | Examine the id. |
| 409 | format_inactive | The owner workspace set the Format to Inactive. This applies to all callers, and also to the workspaces that the owner shared it with. | Owner workspace: Format page → API tab → Status Active. |
| 409 | format_api_trigger_disabled | The owner workspace disabled the API call trigger. This applies to all callers, and also to the workspaces that the owner shared it with. | Owner workspace: Format page → API tab → API call trigger On. |
| 409 | format_run_in_progress | on_active_run: "reject" is set, and a run is already in progress. | Wait, or remove reject. |
| 409 | format_not_forkable | You addressed a built-in capability, not a Format. | Call a Format by Sume, or call your own Format. |
| 409 | previous_run_not_terminal | The run that you want to continue is still in progress. | Poll the run. Then call again. |
| 409 | idempotency_conflict | You already used that Idempotency-Key with a different body. For bulk runs, details.queue_id names the original queue. | Correct how you derive your keys. Do not retry without a change. |
| 409 | idempotency_key_in_use | A different request with the same key is in progress. retryable: true. | Wait approximately one second. Then send the request again. |
| 413 | payload_too_large | The request body is more than 4 MiB (details.limit_bytes). | Make input smaller. Send media by URL. |
| 415 | unsupported_media_type | You did not send the body as application/json (details.received_content_type). | Set Content-Type: application/json. |
| 413 | attachment_too_large | An image is more than 30 MB, or a set is more than 500 MB. | Resize. |
| 429 | rate_limited | No write budget remains for this key. error.details.scope is write. | Wait for retry-after. Refer to Rate limits. |
| 502 | attachment_fetch_failed | Sume could not fetch an attachment (details.index). The status is 5xx, but the cause is your input: next_action is fix_input. | Make sure that the URL is public. |
| 503 | studio_agent_upstream_unavailable | An outage on the Sume side. | Retry at a later time with the same Idempotency-Key. |
| 4xx/5xx | format_run_failed_to_start | The run could not start, and no more specific code is applicable. | Read message. Retry one time. If the error occurs again, contact support with request_id. |
A 202 never changes to one of these errors at a later time. After you get a receipt, failures
arrive on it as status: "failed".
Errors while reading
These errors apply to GET /v1/format-runs/{run_id}, /status, /result, /events,
POST …/cancel, POST …/webhook/redeliver, GET /v1/format-run-queues/{queue_id}, and the
Format reads.
| Status | error.code | Meaning | What to do |
|---|---|---|---|
| 401 | unauthorized | The same as above. | Correct the header. |
| 403 | insufficient_scope | Reads need formats:read. Cancel and redeliver need formats:write. | Mint a new key. |
| 404 | format_run_not_found | The run id is unknown, or a different owner has the run. | Examine the id and the key. A run that you cannot see gives the same result as a run that does not exist. |
| 404 | format_run_queue_not_found | The queue is unknown, or a different owner has it. | The same as above. |
| 404 | format_not_found | The same as above. | The same as above. |
| 409 | run_not_completed | You sent GET …/result before the run was terminal. details.status is the current status. retry_after_seconds gives a recommended time for the next poll. | Poll status_url. Then read result_url. |
| 409 | webhook_not_configured | You sent redeliver for a run created without a webhook_url. | There is nothing to redeliver. |
| 409 | run_not_terminal | You sent redeliver while the run is still in progress. | Wait for the terminal receipt. |
| 429 | rate_limited | No read budget remains (details.scope: read). | Wait for retry-after. The run continues. |
| 503 | studio_agent_upstream_unavailable | An outage on the Sume side. | Retry. The run continues. |
A 429 or 503 in a poll loop is temporary. If you stop the loop, the run and its credits do not
stop. Thus, back off and poll again.
When the run itself fails
A run that could not finish comes back with status: "failed", error: { code, message },
and usually output_error with more data. artifacts[] still lists all the media that the run
generated. output still holds the partial result that satisfied your schema, if there is one. A
failure never gets a pointer: primary_output_url is null. Thus,
if (run.primary_output_url) stays a safe test for "the deliverable exists".
error.code | Meaning | What to do |
|---|---|---|
unattended_blocked | The run stopped at a gate that it could not pass without a person. No avatar matched the brief, or an input was missing that the run asks for in chat. message is text that you can show to a user. | Correct the input or the brief. Retry with a new Idempotency-Key. |
output_schema_unsatisfied | The run finished, but its result did not match your output_schema, or the result referenced media that the run did not produce. The data is in details.rejected_urls[] or details.violations[], with a harvested count by media type. | Compare details.harvested with the requirements of your schema. Usually, the schema asks for a file that the Format never makes. Change that field to nullable, or change the instruction. |
deliverable_missing | The Format declares that it produces media (io.output_kind), but this run made no media. | Retry. If the error occurs again, the input is not what the recipe expects. |
primary_output_missing | The result satisfied your schema, but the primary_output_key that you named is empty. output holds the partial result. | Continue the run to fill the gap, or retry. |
agent_reported_failure | The accepted return_format_output of the run said that the run did not deliver. The cause is an explicit-fail payload, or media slots that report failed / stand-in. Or the primary is not the declared deliverable of the Format (audio or a still under a video key). output holds the ledger of what the run made. primary_output_url is null. | Read details.reason and output. The clips on output are real, and a retry does not generate them again. Continue the run, or re-fire it with a new Idempotency-Key. |
incomplete_assembly | The run reached its time limit before all its generation jobs finished. Thus, the delivered media is not all the media that the run used credits for. details.pending_job_count and details.pending_jobs[] name the unfinished jobs. When your schema permits it, the partial ledger is on output. | Continue the run with previous_run_id. The finished clips are on the thread, and the continuation does not generate them again. |
output_extraction_failed | The projection could not run. details.reason: harvest_unavailable means that the media was not readable while the run finalized. In that case, status stays completed, and the receipt fills in on the next read. details.reason: harvest_threw means that the harvest of the host crashed after the run finished. In that case, the run is failed, and details.thrown holds the frame that threw and the build. If the ledger of the run holds none of the declared media of the Format, the run reports deliverable_missing, not this code. | harvest_unavailable: read the run one more time. Then retry with a new key. harvest_threw: this is a host defect. Report the run id. The clips on the thread are real, and a retry does not generate them again. |
mcp_unavailable | The per-turn Sume MCP tools that this run needed did not attach. Thus, the host failed the run before the model started, and did not run a turn without tools (#7378). details.retryable is true and details.charged is false. No generation ran, and there is no charge. | Retry with a new Idempotency-Key. If the error occurs again, the cause is the tools, not your request. |
provider_unavailable | The provider stream of the model stopped, and all its reconnects failed before the run produced its deliverable. Your input did not cause this error. details.retryable is true. | Retry with a new Idempotency-Key. The finished clips are on the thread, and the retry does not generate them again. |
provider_credits_exhausted | The account of Sume at the model provider used all of its credit, thus the run stopped. The cause is on the Sume side. It is not your Sume balance and not your input. details.retryable is false. | Do not re-fire immediately. When Sume reports that the provider account is restored, retry with a new Idempotency-Key. |
format_run_failed | The generic failure. | Read message. A run that tried to use more credits than its cap also gets this code. Thus, compare usage.cap.counted_credits with usage.cap.limit_credits before you raise the brief. |
This set of codes is open, and new codes can appear. Thus, handle the codes that you know, and use
a fallback for all other codes. Retry a failed run with a new Idempotency-Key. The old key is
bound to the receipt that you already have. When the failure left clips, it is better to
continue the run than to start a new run.
On a webhook, the same run arrives with status: "ERROR", outcome: "error", and an
error.code that is the same as payload.error.code. A run that completed but could not fill
your schema arrives as status: "OK", outcome: "degraded". It has real media and
output: null.
Webhook delivery failures
A delivery outcome never changes the run. The webhook_delivery block on each receipt tells you
the result:
webhook_delivery.status | Meaning | What to do |
|---|---|---|
retrying | An attempt failed. next_attempt_at is the time of the next attempt. For HTTP 429/503 from your endpoint, the API obeys Retry-After up to one hour. | Nothing, unless last_status_code shows a problem on your side. |
failed, exhausted | Ten attempts failed because of a refusal, a timeout (10 s each), or a redirect. Or the URL failed re-validation. last_status_code and last_error tell you which cause. | Read the receipt from result_url. Correct the endpoint. To replay, send POST …/webhook/redeliver. |
Envelope with payload: null | The receipt was more than 1 MiB. error.code is payload_too_large, and error.result_url gives the address to fetch it. status still shows the real outcome of the run. | Fetch result_url. A handler that always reads payload as an object will throw on your largest runs. |
For the full delivery rules, refer to Runs and results.
Credits and spend
A run uses credits from the workspace of the key. Two gates apply, at different times:
| Gate | When | On failure |
|---|---|---|
| Wallet | At create. The workspace must have enough credits for the run. | 402 insufficient_credits (next_action: add_funds), or 402 organization_wallet_not_provisioned. Nothing ran. |
| Spend cap | During the run. The run cannot use more credits than its effective cap. | The run ends as failed. usage.cap shows how near to the cap the run got. |
The cap is the control that you own. Each Format has a cap in credits
(generation_spend_cap_credits on the Format, or the platform default when the Format never set
one). generation_spend_cap_credits on the request names the ceiling of this run, up to the
platform maximum. The API accepts a value above the cap of the Format, runs null at the platform
maximum, and rejects 0. A production long-form run needs a much larger cap than a single-scene
retry, which needs only a fraction of it. For details, refer to
Spend caps.
Metered generation (video, image, avatar, voice, timeline work) uses credits at the rates on the
API rate card. The receipt shows this amount as
usage.cap.counted_credits. This value increases while the run is in progress. It counts
reserved and captured credits, and it settles when the run terminates.
This value does not include the LLM turn of the agent. Thus, it is not the total credits of the
run: that is usage.debited_credits. It is also a receipt value, not an invoice:
GET /v1/usage and GET /v1/balance are the billing records. usage is
null when the API could not read the credits. This is different from 0.
Generation that finished before a cancel or a failure keeps its credits. If a later step fails,
the credits of the generation that finished do not come back. A 4xx at create, an idempotent
200 replay, and a skipped run use no credits.
Rate limits
Each key has a per-minute request budget for all of /v1. The plan of the workspace sets this
budget. Reads and writes have separate budgets. Reads get forty times the write number, thus polls
cannot block your own creates.
| Plan | Writes per minute | Reads per minute |
|---|---|---|
| Free | 120 | 4800 |
| Pro | 300 | 12000 |
| Startup | 600 | 24000 |
| Scale | 1200 | 48000 |
| Enterprise | Contracted (Scale until provisioned) | Contracted |
A read is a GET of all types: the receipt, status_url, events_url, result_url, and the
Format and run lists. A write is all other requests: create calls for runs and queues, cancel,
and redeliver.
Each response holds the current state. A 429 names the budget that it came from:
| Header | Meaning |
|---|---|
ratelimit-limit | The number of requests permitted in the current window, for the budget that this request used. |
ratelimit-remaining | The number of requests that remain in that window. |
ratelimit-reset | The number of seconds until the window resets. |
retry-after | The number of seconds to wait. The API sends it on 429. error.details.scope is read or write. |
Use the headers to set your pace. Do not count the requests yourself. The request rate is not the generation capacity. The concurrency limit of the plan controls how many generations run at the same time. If you increase your request rate, this limit does not increase.
Getting help
Each response has x-sume-request-id, and each receipt has request_id. When you contact
support, include these values, the run id, and the error.code. Do not send API keys, signing
secrets, or raw media URLs.
Next
- Create a run: the create contract, with the
401/403/404dialect in detail - Runs and results: polls, webhooks,
webhook_delivery, and how to continue a failed run - Structured output: schema rules and each
output_error - Authentication: keys, rotation, and the full rate-limit notes