Consume webhooks at your own pace: Sume has no offset commit
Sume has no polling endpoint with offset commits. Page GET /v1/jobs with starting_after, read each job's status, and keep your own cursor.

Sume has no queue you read from with offset commits. Events are a pull snapshot: GET /v1/jobs/:id/events returns one job's timeline, and GET /v1/jobs lists jobs newest-first with a starting_after cursor. To consume at your own pace, keep your own cursor and re-read job status, or take a webhook and keep polling as the backup.
The offset-commit design is from Svix's September 2026 changelog; Sume's side is from Jobs and results, read 2026-09-30.
What does a polling endpoint with offset commits do?
Svix's changelog says its polling endpoints v2 are fully rolled out and use explicit offset commits, so consumers can process messages at their own pace and pick up where they left off, with uncommitted messages redelivered automatically. That is a stream with a server-side cursor. Sume does not offer that shape.
What does Sume offer instead?
| Need | Sume surface | Note |
|---|---|---|
| Stream or push | None: no SSE or WebSocket | Events are a pull snapshot |
| One job's timeline | GET /v1/jobs/:id/events | Public timeline for debugging and recovery |
| Current state | GET /v1/jobs/{id}/status | Honor next_poll_after_seconds |
| Many jobs | GET /v1/jobs | Newest first, up to 100 per page |
| Next page | data.next_cursor as starting_after | Absent on the last page |
| Be told | Job webhook | Verify the signature; keep polling as backup |
How do I build a pull consumer?
Page the list until next_cursor is absent, which the schema calls the loop terminator. Do not read identity off array position: each row carries the idempotency_key it was created with, so join your own labels on that. Store the ids you have finished with, since there is nothing to commit on Sume's side.
const base = "https://api.sume.com/v1/jobs";
const headers = { Authorization: "Bearer " + process.env.SUME_API_KEY };
const done = new Set();
let cursor;
do {
const url = base + "?limit=100&status=completed" + (cursor ? "&starting_after=" + cursor : "");
const res = await fetch(url, { headers });
const { data } = await res.json();
for (const job of data.jobs) {
if (!done.has(job.id)) done.add(job.id); // handle the job here
}
cursor = data.next_cursor;
} while (cursor);
console.log(done.size + " completed jobs seen");What replaces redelivery of uncommitted messages?
Your own set of handled ids. A webhook delivery is retried up to 10 times, and a failed delivery leaves a job that still reached its real terminal state, so the job record is the recovery path. If you need push as well, see Cloudflare Workflows subscribe vs Sume job events for how the two compare.
Sources
Related posts
More in Developers
- Webhook rate limits: Zapier, Airtable, Pipedream vs a bulk of 100
Zapier, Airtable and Pipedream each publish a webhook intake limit. Compare them with a Sume bulk queue of up to 100 items and pick a receiver that fits.
- Webhook receiver was down: replay missed Sume job webhooks
After a receiver outage, list completed jobs and POST /v1/jobs/{job_id}/webhook/redeliver one by one. Sume has no bulk cancel-before-cutoff call.
- Webhook signature mismatch: compare the secret fingerprint
Before filing a ticket for a Sume signature mismatch, compare the secret fingerprint header with the dashboard value. It is safe to paste into a ticket.
- What not to log from an AI API: keys, signed URLs, private media
Safe to log: request ids, job ids, status and sanitized media metadata. Unsafe: API keys, signed URLs, raw private media URLs and excess user content.
Written by Sume