Handle every Sume API error with one switch on next_action
Sume errors share one envelope. Branch on next_action, retryable and retry_after_seconds, and your client handles new codes without a code change. JS sample.

Read error.next_action from the Sume error envelope and switch on it. It has seven values (authenticate, fix_input, add_funds, retry_later, poll_status, inspect_events, contact_support), and together with retryable and retry_after_seconds it tells a client what to do without knowing every error.code. Keep the code for logs and for the few cases where you want custom behavior.
What the envelope carries
Every error on the API has the same shape: an error object with a stable lowercase code, a human message, a request_id, and the fields below. The docs say to match on code and never on message, because the text can change.
The HTTP status still matters, and the order is status first, then code. A 4xx at create means nothing ran and nothing was charged, so you correct the call instead of retrying it. next_action is the third layer: it turns the pair into an instruction.
| Field | Use it for | Notes |
|---|---|---|
code | Branching on a known cause | Matches ^[a-z0-9_]+$; never a sentence |
next_action | The default response | One of seven tokens |
retryable | Whether the same request can succeed later | Can be false on a 5xx whose cause is your input |
retry_after_seconds | How long to wait first | Can be absent or null |
request_id | Support and logs | Also sent as the x-sume-request-id header |
details | Code-specific data | For example scope, required_scope, violations[] |
Why next_action beats the status code
Status codes lie about the cause often enough that a status-only handler goes wrong. attachment_fetch_failed returns a 502, which looks like an outage, but its next_action is fix_input, because the cause is a URL that Sume could not fetch. A status-only client would retry a request that can never succeed. A 403 insufficient_scope is the opposite: nothing about the status says the retry is pointless, and the docs call retrying it in a loop the most frequent and most expensive mistake.
next_action also gives you one place to handle codes that do not exist yet. When Sume adds an error code, an old client that switches on the action still stops on fix_input and still waits on retry_later.
A handler you can paste
The function maps a response body to a step, a retry flag, a delay and the identifiers to log. Unknown or missing actions fall back to the status class: retry for 429 and 5xx, stop for everything else.
const ACTIONS = {
authenticate: "stop: fix the key, do not retry",
fix_input: "stop: correct the request body, do not retry",
add_funds: "stop: fund the wallet, then resend the same key",
retry_later: "retry with the same key after the delay",
poll_status: "do not resubmit: poll status_url",
inspect_events: "read /events, then decide",
contact_support: "stop: send request_id to support",
};
export function decide(status, body) {
const e = body.error ?? {};
const delay = e.retry_after_seconds ?? 5;
const step = ACTIONS[e.next_action] ?? (status >= 500 || status === 429 ? ACTIONS.retry_later : ACTIONS.fix_input);
return { step, retryable: e.retryable === true, delay, requestId: e.request_id ?? null, code: e.code ?? "unknown" };
}
console.log(decide(402, { error: { code: "insufficient_credits", next_action: "add_funds", retryable: false, request_id: "req_1" } }));
console.log(decide(503, { error: { code: "provider_capacity_exceeded", retryable: true, retry_after_seconds: 20 } }));What each action should do in your code
authenticate and fix_input are stops. Page a human or return a 4xx to your own caller, and do not loop. add_funds is also a stop, with one extra property: retrying before the wallet is funded gives the same answer. After funding, resend with the same Idempotency-Key, because a failed create releases its key.
retry_later is the only action that means resend, and you should wait retry_after_seconds when it is present. poll_status and inspect_events mean the job exists: read status_url or /events and never resubmit a paid request. contact_support should log request_id and stop.
Log the fields that make the next incident short
Whatever the action, a retry of a create should reuse the same Idempotency-Key. The same key with the same body returns the original job, the same key with a different body is 409 idempotency_conflict, and a key held by an in-flight request is 409 idempotency_key_in_use, which is retryable after about a second. Store the key next to the intent in your database, not in a variable, so it survives a restart.
Log the same four fields every time
On every non-2xx response, write the HTTP status, error.code, error.next_action and request_id in one structured log line. The request id is also the x-sume-request-id response header, and it is the single identifier support needs to find the call. Redact API keys, signed URLs and raw media URLs before the line is written.
With those four fields you can chart errors by action instead of by status. A rising fix_input count points at your own validation, a rising retry_later count points at capacity or limits, and a single contact_support deserves a ticket. That is a better alert than a count of all 5xx, because it separates your bugs from Sume's and from your wallet.
Sources
Related posts
More in Developers
- Hindi speech to text API: Sume STT with language_code hi
Transcribe Hindi audio with Sume STT: send language_code hi, check the reported language, and review code-mixed speech. $0.01 per audio minute.
- A 3-minute Timeline render: poll job status, don't sleep a fixed time
Timeline renders are async by default. Submit with an Idempotency-Key, poll /v1/jobs/:id/status until terminal, then read /result. Cost: 3 minutes is $0.30.
- Image-to-video not starting on my photo: frame_images vs references
Your photo is a reference, not a first frame, when it goes in input_references. Use frame_images with first_frame on Sume /v1/videos to pin the opening shot.
- Japanese speech to text API: Sume STT with language_code ja
Transcribe Japanese audio with Sume STT: send language_code ja, read word times, and test a sample first. $0.01 per audio minute, 10 minute jobs.
Written by Sume