Downloading the mp4 inside the webhook? Sume's 10 s limit
A callback handler that fetches the video before replying will time out. Sume allows each webhook delivery 10 seconds and 10 attempts: store the id, return 2xx.

If your old video callback downloaded the finished mp4 before answering, it will misbehave on Sume. The webhooks guide gives every delivery attempt a 10 second timeout and up to 10 attempts total, spaced 30 seconds apart by default. A slow handler burns that budget and gets retried. Verify the signature, durably store the job_id, return 2xx, and do the download in a separate worker.
The budget you are working with
Sume sends terminal events only: job.completed, job.failed and job.canceled. There are no progress callbacks, so every delivery matters, and the payload for a completed job carries artifacts with Sume media URLs.
| Property | Value |
|---|---|
| Attempts | Up to 10 in total |
| Spacing | Fixed 30 seconds by default, not exponential |
| Timeout per attempt | 10 seconds |
| Events | job.completed, job.failed, job.canceled |
| Idempotency key on your side | job_id |
Why a download in the handler goes wrong
A multi-megabyte download, a transcode, and a database write easily exceed 10 seconds on a busy host. Sume sees a timeout, schedules a retry, and your handler runs twice. If the first run finishes late and the second starts, you can store two copies or, worse, charge your own customer twice. The guide's advice is to treat job_id as the idempotency key on your side.
A handler that returns fast
Keep the handler to four steps: read the raw body, verify the HMAC over timestamp.raw_body, insert a row keyed by job_id with ON CONFLICT DO NOTHING, and return 200. A queue consumer then fetches the artifact URL, copies it into your storage, and marks the row done. The status endpoint remains the backup for events that never arrive, and the guide notes you can redeliver a real terminal event with POST /v1/jobs/{job_id}/webhook/redeliver after attempts run out.
# pseudo-flow, any framework
body = request.raw_body
if not verify(body, headers, secret): # reject empty secret
return 401
job_id = json.loads(body)["job_id"]
db.insert_if_absent(job_id, body) # unique on job_id
queue.enqueue("fetch_artifact", job_id)
return 200Check that you are safe
Send a test delivery from the dashboard to your staging URL and time it. Under about two seconds leaves headroom. Then kill the consumer, complete a job, and confirm the row exists without the file: that is the failure mode the split is designed to survive. See Jobs and results for the polling fallback.
Sources
Related posts
More in Developers
- Sora replacement smoke test: one prompt, three Sume models, in Python
A short Python script that sends the same 5-second prompt to seedance-2.5, wan-3.0 and gemini-omni-flash-1.1 on Sume and prints status, cost and URLs.
- Sora files lived 1 hour: storing Sume job results
OpenAI's Sora 2 and Videos API shut down Sept 24, 2026, and videos were downloadable for 1 hour. Download and store Sume job results promptly.
- Sort your MCP tool allowlist: stable order keeps the prompt cache warm
MCP 2026-07-28 asks servers for ttlMs, cacheScope and a deterministic tool order. On the client, sort your media tool allowlist the same way. Python snippet.
- STT words array has spacing tokens: filter type word before you count
Word counts and captions go wrong because the words array can include spacing items. Filter by type, keep timings, and compute speaking rate correctly.
Written by Sume