Deno: time out a Sume video submit, then retry with the same key
A fetch that times out may still have created a job. A 20-line Deno submit retries only 408, 429, 5xx and timeouts, always with one Idempotency-Key.

Generate one Idempotency-Key per intended render, send it on every attempt, and retry only when the outcome is unknown: a timeout, a network error, 408, 429 or a 5xx. A timed-out fetch tells you nothing about the server. Sume's video docs say a replay with the same key returns the original job, so the retry cannot bill a second clip.
What counts as unresolved
The typed error docs for the paid create endpoints say a failed response proves the work was refused only for a validation, authorization or balance reason, and that a 5xx never proves it. That rule gives the retry set below.
| Outcome | Retry with the same key? | Why |
|---|---|---|
| Timeout or network error | Yes | The job may already exist |
| 408, 429, 5xx | Yes | Transient; honor retry-after on 429 |
| 400, 402 | No | Fix the request or the balance first |
| 409 idempotency_conflict | No | Same key, different payload; error details name the existing job |
| 202 | Done | Store the id |
The submit
Run it with deno run --allow-net --allow-env. The key is made once at module load, which is what keeps retries identical; in a real service, store it with the row before the first attempt so a restart reuses it. Top-level await is fine in Deno, unlike in a Python script.
const base = Deno.env.get("SUME_BASE") ?? "https://api.sume.com";
// Make the key once per intended render and store it with the row, so a restart reuses it.
const key = "veo-migrate-" + crypto.randomUUID();
const retryable = (s: number) => s === 408 || s === 429 || s >= 500;
async function submit(): Promise<{ id: string }> {
for (let attempt = 1; ; attempt++) {
const res = await fetch(`${base}/v1/videos`, {
method: "POST",
headers: { Authorization: `Bearer ${Deno.env.get("SUME_API_KEY")}`, "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ model: "gemini-omni-flash-1.1", prompt: "a lighthouse in fog" }),
signal: AbortSignal.timeout(30_000),
}).catch(() => null); // timeout or network error: the job may already exist
if (res?.status === 202) return await res.json();
if (res && !retryable(res.status)) throw new Error(`HTTP ${res.status}: fix the request, do not retry`);
if (attempt === 3) throw new Error(`gave up; a job may exist under key ${key}`);
console.log(`attempt ${attempt} unresolved, retrying with the same key`);
}
}
console.log(await submit());Where it stops
After three tries the function throws and says a job may exist under the key. Do not mint a new key at that point. Retry later with the same one, or list jobs and match on idempotency_key, which every job object carries. This sample also does no backoff; add a delay that honors retry-after. The SDK's own client does that for POSTs, but only when the header is present.
Sources
Related posts
More in Developers
- Idempotency-Key from an order id and version, never a fresh uuid
A fresh uuid per request makes Idempotency-Key do nothing. Derive it from the order id plus a version you bump only to re-run. Scope: one Format, 255 chars.
- How do I narrate a DIY tutorial step by step with a TTS API?
Narrate an 8-step DIY tutorial with one TTS job per step: 1,570 characters, $0.10 on Sume. Why per-step jobs make a fixed step a 1-cent redo.
- Do I pay for a failed AI avatar video job? Refunds on Sume
Sume reserves the avatar video price at submit, captures it on completion, and releases or refunds it where a job fails. What it means for retries.
- Does MiniMax H3 have an API? Three endpoints, or one route on Sume
Yes. MiniMax lists three Open Platform endpoints for H3: create, h3-context-ir and regeneration. Sume wraps H3 in one async video route, minimax-h3.
Written by Sume