Sume webhooks plus a sweeper: recover jobs whose callback never came
Webhook delivery can fail after 10 attempts while the Sume job still finishes. Run a sweeper that polls jobs stuck non-terminal in your own table.

A Sume job webhook can fail to reach you and the job will still finish. Delivery is retried up to 10 attempts total with a 10 second timeout each, and ten refused attempts leave a failed delivery but a job in its real terminal state. The docs say to keep status_url polling available for exactly this, and a sweeper is that polling, done once in a while rather than in a tight loop.
The pattern
Store every job id with a status column on submit. The webhook handler marks a row terminal. A scheduled sweeper selects rows still non-terminal after your usual job time, reads their status once, and marks them. Redeliver is a second option: POST /v1/jobs/{job_id}/webhook/redeliver re-sends the real terminal event with a fresh signature and does not use one of the automatic attempts.
import asyncio, os
import httpx
async def sweep(open_job_ids: list[str]) -> dict[str, str]:
headers = {"x-api-key": os.environ["SUME_API_KEY"]}
found: dict[str, str] = {}
async with httpx.AsyncClient(base_url="https://api.sume.com", headers=headers) as c:
for job_id in open_job_ids:
r = await c.get(f"/v1/jobs/{job_id}/status")
r.raise_for_status()
body = r.json()
if body.get("terminal"):
found[job_id] = body.get("sume_status", "unknown")
return found
async def main() -> None:
print(await sweep(["job_123"]))
asyncio.run(main())Make handling idempotent
Both paths can report the same job, so key your handler on job_id and ignore a second report. Use the same handler function for the webhook and the sweeper.
| Path | Latency | Covers |
|---|---|---|
| Webhook | Seconds after the terminal event | Normal case |
| Sweeper | Your schedule | Deliveries that exhausted retries |
| Redeliver call | On demand | One known job, after you fix the endpoint |
What not to do
Do not resubmit the paid request because a callback is missing; that creates a second paid job. Read the status instead.
Sources
Related posts
More in Developers
- How to test a webhook URL before a Sume Format run uses it
POST /v1/webhooks/test-deliveries sends a signed webhook.test event to your URL. See the scope, the response fields, and the secret check, with no paid run.
- Transactional outbox for paid API calls in Python (Sume)
Write the Sume request and its Idempotency-Key in the order's transaction, drain later: a tested Python outbox that survives crashes, 429s and 409s.
- Transcribe a three-hour recording when the API caps at ten minutes
Streaming sessions end at an hour; Sume STT jobs take up to 600 seconds. Split with Timeline audio, transcribe 18 chunks, and stitch word times back together.
- Translate an SRT and burn it in: Sume caption cues, limits, Python
Sume takes no SRT upload, but caption cues take the same text and times. A Python converter, the 200-cue and 60-second limits, and which fonts apply.
Written by Sume