Missed an agent run webhook? Poll status_url, redeliver is Format-only
Sume's docs describe a webhook redeliver route for Format runs. For an Agent Completion you did not get a POST for, read the run from its status or result URL.

If an Agent Completion's webhook never arrived, read the run from its receipt URLs. The run webhook docs describe POST /v1/format-runs/{run_id}/webhook/redeliver, which needs the formats:write scope, for Format runs. I could not find a matching route for Agent Completions in those docs, so this post does not assume one. A delivery problem does not change the run: the docs say to fetch the run from result_url (Run webhooks).
What the docs say about missed deliveries
Sume makes up to 10 attempts per run, with backoff of min(max(30s x 2^(attempt-1) with jitter, Retry-After), 1h), then marks webhook_delivery.status as exhausted. If an endpoint rejects all ten attempts, you have a failed delivery and a run that is still completed.
The receipt's webhook_delivery object describes the delivery row. Read it to learn whether delivery succeeded, was exhausted or never started.
| Situation | What to do | Surface |
|---|---|---|
| Delivery exhausted | Read the run; it is still complete | status_url or result_url |
| Endpoint was down | Poll the run for its terminal status | status_url |
| Format run, want a replay | POST webhook/redeliver with formats:write | Format runs |
| Agent Completion, want a replay | Not documented; poll instead | Agent runs |
| Canceled run | No webhook by design; poll status | status_url |
A recovery decision
The function below picks the action from a receipt-shaped dictionary. The webhook_delivery field names follow the docs. It runs offline.
def next_step(run: dict) -> str:
status = run.get("status")
wd = run.get("webhook_delivery") or {}
if status in ("queued", "processing"):
return "poll status_url"
if status == "canceled":
return "done: cancel sends no webhook"
if wd.get("status") == "exhausted":
return "read result_url; delivery gave up"
return "handle receipt"
if __name__ == "__main__":
print(next_step({"status": "processing"}))
print(next_step({"status": "completed",
"webhook_delivery": {"status": "exhausted"}}))
print(next_step({"status": "canceled"}))Design for a missing webhook
Treat the webhook as a convenience, not the source of truth. The docs say you still get status_url and result_url and can still poll. A webhook only saves you from running a loop for each run.
So run a slow sweeper: list recent runs with GET /v1/agent-runs, which returns newest first, and check any run that has been non-terminal longer than you expect. That closes the gap for every cause of a missed POST, from your own deploys to network trouble.
Which URL to read
An Agent Completion receipt includes status_url for polling and result_url for the finished result. Polling GET /v1/agent-runs/{id} needs the agent_completions:read scope. Keep the run id from the 202 receipt in your own store at submit time, so a lost webhook never leaves you without a handle.
Back off between polls. A run that is queued or processing can take a while, and the receipt's next_action field tells you to keep polling. Stop as soon as the status is completed, failed or canceled, and read the final receipt once.
Checklist
Keep these in your runbook.
- Never re-submit a completion because a webhook was late.
- Poll with backoff, not in a tight loop.
- Use the Format redeliver route only for Format runs.
- Dedupe on
request_idwhen a replay does arrive.
Sources
Related posts
More in Developers
- Build an AI image progress UI: gate on supports_streaming false
Sume's image catalog reports supports_streaming false on every row. Build the progress UI from job states and gate a live preview on that field.
- AI image to CMYK for print: Pillow convert and what it misses
Image.convert('CMYK') makes a print-mode TIFF from a Sume image in two lines, but it is not color-managed. The code, the gamut risk, and when to ask for an ICC.
- AI video API: rate limit or concurrency, which stops a batch first
Concurrency binds long before request rate on Sume. Free allows 120 writes a minute but 1 running job; Pro 300 and 4. How to size a batch against both.
- AI video generator app: four server calls, key never on the client
An AI video app on Sume needs four server-side calls: submit, poll, download and balance. The key stays on your server, never in the browser or mobile app.
Written by Sume