Which request_id do you dedupe a Sume run webhook on?

Dedupe on the envelope request_id, which equals run_id and is stable across retries. The nested payload.request_id is a different id. A code check.

5 min readSume
All posts

Dedupe a Sume run webhook on the envelope's top-level request_id, which equals run_id and is the same on every retry of the same run. Ignore the nested payload.request_id: it is a correlation id for the call that produced the receipt, and it differs depending on how you read the receipt.

Two ids with the same name

The run webhooks docs call this out. In a webhook, the envelope request_id is the run id, and it is deliberately stable across retries. The receipt value at payload.request_id is a correlation id; in the webhook it is also the run id, but when you read the same receipt from GET /v1/format-runs/{run_id} it is an HTTP req_ id.

Which id to use, from the Run webhooks page (read 2026-10-08)
FieldValueUse for dedupe?
request_id (envelope)Run id, stable across retriesYes
run_idThe run this delivery is aboutYes, same value
payload.request_idCorrelation id; differs between webhook and pollNo
created_atWhen Sume built this delivery bodyNo; use it to order deliveries

A handler that dedupes

The shape is the same for Action, Format, and Agent Completion events, which are action.run.terminal, format.run.terminal, and agent.run.terminal.

const seen = new Set();

export function accept(event) {
  const id = event.request_id; // envelope id, equals run_id
  if (!id) throw new Error("missing request_id");
  if (seen.has(id)) return "duplicate";
  seen.add(id);
  return event.outcome; // ok | degraded | error
}

Why a Set is not enough in production

An in-memory Set vanishes on restart, and Sume retries for up to 10 attempts over hours. Store the id in a database with a unique constraint, and write the row before you return the 2xx. The docs advise recording the event durably first and processing after the response, since a slow endpoint uses the 10-second attempt budget.

One run, one event

A run is one agent turn, so its terminal event fires once, however many clips or images it made. A continued run starts a new run with a new id and its own single event. The original run's webhook does not fire again. Treat the pair of run id and event name as your key if you handle several families at one URL.

Verify the signature first, using the raw body. The SDK's verifyWebhook does that for TypeScript receivers, per the verifying webhooks page.

Test with a replay

To check your dedupe, deliver the same event twice. For Format runs, the Redeliver control or POST /v1/format-runs/{run_id}/webhook/redeliver re-sends the current receipt with a new timestamp and signature, signed with the same secret. Your verifier should accept both, and your dedupe should treat them as one run. Send test deliveries (webhook.test) are not a replay of a real run, so they will not exercise this path.

Dedupe is about duplicates of the same delivery. It is not a substitute for ordering. The docs say to use created_at, the time Sume built the delivery body, to put deliveries in order, because request_id is stable across retries and cannot do that. If you update a record from a webhook, compare created_at with what you stored, and ignore an older one.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume