Redelivered job.completed must not start the trim twice
Redeliver re-sends the same job's terminal event with a fresh signature. Dedupe on job_id and derive the next step's Idempotency-Key from it.

If your receiver starts the trim step when a render's job.completed arrives, a redelivery will start it again unless you stop it. The fix is small: treat job_id as the idempotency key, as the docs tell receivers to, and send the trim with an Idempotency-Key built from that job_id. Then a second delivery of the same event returns the original trim job and bills once.
What redeliver changes and what it keeps
POST /v1/jobs/{job_id}/webhook/redeliver needs jobs:write. Sume re-POSTs the real terminal event of that job (job.completed, job.failed or job.canceled) with a fresh timestamp and signature. It still works after all 10 automatic attempts are used, and it does not use one of them. It does not change the destination URL: a new URL is a new job.
| Field | Same as the first delivery? | Use it for |
|---|---|---|
job_id | Yes | Dedupe key |
| Timestamp | No, fresh | Replay-window check only |
sume-v1 signature | No, fresh | Verify each delivery separately |
| Terminal event type | Yes | Route to the next step |
Derive the next key
Build the key from values that do not change. A key like trim-after-<job_id> is the same on every delivery of that event, so the platform's own idempotency does the deduping for you, even if your local table is lost. Keep a local job_id set too, so that you skip the work of a verification and an HTTP call when you know the step has already begun.
def trim_key(job_id: str) -> str:
return f"trim-after-{job_id}"
# send as: Idempotency-Key: trim-after-job_123
# same job_id on redelivery -> same key -> same trim jobPitfalls
Do not use the timestamp or signature as part of the key: both change on every redelivery, which would start a second trim. Do not skip signature verification on a redelivery because it looks like a repeat; each delivery is signed afresh and must be verified over <ts>.<raw_body>.
Only job.completed should start the next step. A job.failed or job.canceled event for the render means there is nothing to trim. When you redeliver by hand to test, send it against a job whose terminal state is the one your handler expects.
Testing the dedupe
Test the path with a real redeliver rather than a copy of a payload. Complete a render, let your handler start the trim, then call POST /v1/jobs/{job_id}/webhook/redeliver. You should see a second delivery with a new signature, your handler should verify it, and the trim submit should return the same job as before.
If the second delivery starts a second trim, the key is not derived from the job_id. Fix that before you rely on webhooks in production. The local dedupe table is an optimization; the derived key is the guarantee.
Remember that redeliver needs the jobs:write scope on the key you call it with.
- Complete, redeliver, compare trim job ids.
- Same id: pass.
- Different id: the key is wrong.
When the receiver was down
The usual reason to redeliver is an outage. If your endpoint was unreachable, Sume tried up to 10 times, 30 seconds apart. After that, the automatic attempts are used up, and the event is not lost: you can still redeliver by hand, and the call does not take away from the 10.
Once the receiver is back, redeliver the jobs that missed their event in the order you want the chain steps to run. Each delivery goes through the same verified, deduplicated handler, so the result is the same as if the event had arrived the first time.
Sources
Related posts
More in Developers
- reference_ingest OCR: needs_verification crops under 0.85 confidence
reference_ingest never corrects OCR text. Lines under 0.85 confidence come back as needs_verification with a native-resolution crop to check.
- Reference-to-video with 3 images, 6 seconds: cost by Sume model
A 6-second reference-to-video clip with three images costs $0.45 on H3 at 768p up to $3.47 on Seedance 2.5 at 720p on Sume. Eight rows priced.
- Resume a video chain after a worker crash from stored job ids
Your worker died between generate and trim. Read the stored job id from /v1/jobs, do not resubmit paid work, and continue from the first step with no result.
- Retry a Kling motion control submit without paying twice
All four Sume motion-control routes take an Idempotency-Key. Resend the same body and key after a timeout and you get the original job, not a second reserve.
Written by Sume