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.

4 min readSume
All posts

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?

Sume pull surfaces from the docs and OpenAPI, read 2026-09-30
NeedSume surfaceNote
Stream or pushNone: no SSE or WebSocketEvents are a pull snapshot
One job's timelineGET /v1/jobs/:id/eventsPublic timeline for debugging and recovery
Current stateGET /v1/jobs/{id}/statusHonor next_poll_after_seconds
Many jobsGET /v1/jobsNewest first, up to 100 per page
Next pagedata.next_cursor as starting_afterAbsent on the last page
Be toldJob webhookVerify 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

All Developers posts

Written by Sume