Crash-safe Sume submit: write the intent row and key first
If your process dies after a Sume submit but before storing the job id, a pre-written intent row and Idempotency-Key let the retry return the original job.

Store the intent and the idempotency key before you submit to Sume, then store the job id after. A crash between those two writes is then harmless: on restart you resend the same request with the same key and Sume returns the original job instead of billing a second one.
Why the order matters
The job id only exists after the response, so you cannot store it first. The key you can. The docs say a retried submit with the same Idempotency-Key returns the original job, and that you must not submit a new paid job for the same intent just because a local process timed out.
import asyncio, os, sqlite3, uuid
import httpx
async def main() -> None:
db = sqlite3.connect("jobs.db")
db.execute("create table if not exists jobs(key text primary key, job_id text)")
key = str(uuid.uuid4())
db.execute("insert into jobs(key) values (?)", (key,))
db.commit()
headers = {"x-api-key": os.environ["SUME_API_KEY"], "Idempotency-Key": key}
async with httpx.AsyncClient(base_url="https://api.sume.com", headers=headers) as c:
r = await c.post("/v1/image-1.0/generate",
json={"prompt": "matte black bottle", "mode": "async"})
r.raise_for_status()
job_id = r.json()["request_id"]
db.execute("update jobs set job_id=? where key=?", (job_id, key))
db.commit()
asyncio.run(main())On restart
Select rows with a key but no job id and resend each with its stored key. Reusing a key for the same payload is what the header is for; a different payload under the same key returns 409 idempotency_conflict, so store the payload or its hash with the row.
Related limit
A failed create releases its key, so a retry after a rejected request can proceed on the same key once you fix the request. Nothing is charged for a 4xx at create.
| Crash happens | State on restart | Recovery |
|---|---|---|
| Before the intent row | Nothing stored | Safe to start over |
| After row, before submit | Key, no job id | Submit with stored key |
| After submit, before job id saved | Key, no job id | Resubmit with stored key returns same job |
Sources
Related posts
More in Developers
- Zod 4 discriminated union for Sume job and run webhooks (TypeScript)
Parse Sume job.* and format.run.terminal webhooks with one Zod 4 discriminatedUnion: typed branches, degraded runs, oversized receipts. Tested with Zod 4.
- Zod 4 toJSONSchema to Sume output_schema: nullable, not optional
z.toJSONSchema works for a Sume Format output_schema if you use nullable instead of optional. A tested table of what passes and what the validator rejects.
- Which MCP server lets Claude Code or Cursor generate video and images?
MCP servers that let Claude Code and Cursor make video and images: Sume, fal, Replicate, Runway, Higgsfield. Endpoints, sign-in, billing, setup.
- Idempotency keys for AI video APIs: retry without paying twice
An idempotency key makes a retried create return the original run or job instead of a second paid one. How Sume's Idempotency-Key works on each API.
Written by Sume