Cloudflare Queues: add a dead letter queue for Sume webhooks

Cloudflare Queues retries a message 3 times by default, then deletes it unless a dead letter queue exists. Set one up for the events Sume sends to your Worker.

5 min readSume
All posts

If a Cloudflare Worker puts Sume webhook events on a Queue, add a dead letter queue to that consumer. By default a message is retried 3 times, and without a dead letter queue it is then deleted permanently. A dead letter queue keeps the failed messages; if no consumer reads it, they persist for 4 days, which is your window to replay them.

The Cloudflare facts are from Dead Letter Queues, read 2026-10-10. The Sume facts are from Run webhooks and Verifying webhooks.

The design goal is simple: no paid result is ever lost because a downstream step had a bad minute.

Why put a queue behind the webhook?

Sume gives a delivery 10 seconds per attempt and tries up to 10 times. A Worker that verifies the signature, writes one message and returns 2xx finishes well inside that, and Sume stops retrying. Everything slow, such as fetching the result or updating a database, happens in the queue consumer, where Cloudflare's retries apply instead of Sume's.

That split has a catch: once you return 2xx, Sume considers the event delivered. If your consumer then fails for good, nobody retries it for you unless you set up the dead letter queue.

Two retry layers (Cloudflare page read 2026-10-10; Sume run webhooks docs)
LayerRetriesIf it finally fails
Sume to your WorkerUp to 10 attempts, 10 second timeout, redirects count as failuresRedeliver with POST /v1/format-runs/{run_id}/webhook/redeliver
Queue to your consumer3 retries by defaultDeleted permanently without a dead letter queue
Dead letter queueHolds failed messagesPersist for 4 days if no consumer reads the queue

What goes in the message?

Verify first, then enqueue only the identifiers: the request_id (equal to the run id), the status and the outcome. The envelope's payload is the run receipt, and it is null with payload_too_large when the receipt would pass 1 MiB, so the consumer should fetch the result from the result URL rather than trust the body.

Check the signature before the enqueue, not in the consumer. A forged event that reaches the queue costs storage and a retry cycle, and the consumer can no longer see the raw body and timestamp in the form the signature covers. Keep the headers you need, or reject early.

Dedupe on request_id. The docs say it is the stable key, since a retried delivery carries the same one. A consumer retried three times must therefore be written so a second run of the same message does nothing new.

How do I recover dead messages?

Give the dead letter queue a small consumer that logs the message to storage and alerts a person, or leave it consumerless and read it within the 4 day window. Then use the Sume redeliver endpoint, which needs the formats:write scope, to send the event again to the current webhook URL. The webhook_delivery object on the run receipt shows the URL, status and attempt count, which tells you whether Sume ever succeeded.

Test the path before you need it. Send a test delivery with POST /v1/webhooks/test-deliveries, which sends a webhook.test event signed like a real one, and confirm it travels through the Worker, the queue and the consumer. Then break the consumer on purpose and watch the message land in the dead letter queue. A recovery path you have never run is a guess.

Treat the 4 day figure as a deadline, not a store. A message you have not dealt with by then is gone, and the receipt on Sume's side is the record you fall back on.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume