Pub/Sub push subscription for Sume events: ack codes and dedupe
Pub/Sub push redelivers on any code outside 102, 200, 201, 202 and 204. Fan Sume completions through Pub/Sub safely with run_id and job_id dedupe.

If you relay Sume completions into Google Pub/Sub, remember that a push subscription resends any message unless your endpoint answers with 102, 200, 201, 202 or 204, and that the per-message deadline cannot be modified. So the consumer must be idempotent: dedupe on job_id for job events and on run_id for run events. Sume itself retries up to 10 times, so duplicates can occur at two layers.
Pub/Sub push rules
From Google's push documentation:
| Topic | Fact |
|---|---|
| Success codes | 102, 200, 201, 202, 204 |
| Other codes or deadline expiry | Message is resent |
| Ack deadline for push | Cannot be modified per message |
| Authentication | JWT in the Authorization header |
Two retry layers, one dedupe key
Sume delivers to a public HTTPS URL and treats a non-2xx or a timeout as a failed attempt. If that URL is a small relay that publishes to a topic, a slow publish can make Sume retry after the message was already published. Downstream, Pub/Sub may then resend to your subscriber.
The key is stable across both layers: job_id for job.completed, job.failed and job.canceled, and run_id or request_id for format.run.terminal, action.run.terminal and agent.run.terminal. Insert it under a unique constraint and ignore conflicts.
Design checklist
Keep the relay and the consumer separate.
- Relay: verify the Sume HMAC, publish the raw body plus timestamp, return 200 fast.
- Consumer: check the Pub/Sub JWT, dedupe on the Sume id, then act.
- Do not return a non-success code for an already-seen id; return 200 so the message is acknowledged.
- Large run receipts arrive with
payload: nullandpayload_too_large; fetchresult_url.
Polling as the backup
Pub/Sub is not a replacement for reading the source of truth. If a message is lost or the subscriber was down for longer than your retention, call GET /v1/jobs/:id/status or /v1/format-runs/:id/status using the ids you stored at create time. Redelivery is also available: POST /v1/jobs/{id}/webhook/redeliver with jobs:write, and POST /v1/format-runs/{id}/webhook/redeliver with formats:write.
Sources
Related posts
More in Developers
- Python 3.15 TaskGroup.cancel: stop at the first Sume job done
Python 3.15 adds TaskGroup.cancel. Watch several Sume jobs and stop the other watchers when the first one completes, without cancelling the paid jobs.
- Python asyncio.timeout around a Sume job poll: a hard budget
Wrap a Sume status loop in asyncio.timeout so it stops at a fixed budget and returns still_running, leaving the job alone. A short version, run against a mock.
- Python httpx and asyncio: submit and poll a Sume image job
A runnable Python recipe: submit a Sume image job in async mode with an Idempotency-Key, poll with httpx and asyncio, honor retry-after, fetch the artifacts.
- Python Sume webhook handler: stdlib verify and SQLite dedupe
A stdlib Python handler for Sume webhooks: verify that accepts rotation, then INSERT OR IGNORE on job_id so a retry runs once. Tested on 3.14 and 3.15.
Written by Sume