burneh
Infrastructure

Every event delivered, and only counted once.

A webhook is a promise to another server, and most implementations break it quietly: an unsigned payload anyone can forge, a retry that gets processed twice, an outage that drops the event for good. We sign every delivery, stamp it with an idempotency key, and retry with backoff until it lands or dead-letters, so your endpoint gets every event, once, and can prove it came from us.

Drive it: send an event, then take the endpoint down and watch the retries back off and dead-letter, bring it back and replay. Every attempt is signed and carries the same idempotency key.

webhooks · delivery console
endpoint
delivery log
signed request
POST api.acme.com/webhooks
X-Webhook-Id: evt_3f9a2c7d
X-Signature: sha256=9c1f4b2e8a3d07f…
X-Idempotency-Key: evt_3f9a2c7d
{ "type": "order.paid", "amount": 4999 }
status
attempt 1 in flight…

same idempotency key on every retry · your endpoint processes it once

One event, delivered until it lands.

What you just operated is this. The naive version fires one POST and hopes. This one signs the payload, treats only a 2xx as delivered, and keeps retrying on a growing delay until your server takes it or it parks in a dead-letter queue you can replay from.

eventorder.paidsignHMAC + idPOSTyour endpoint2xx?the only successdeliveredackedretry× backoffdead-letterparked · reason2xxfailreplay when healthy0102030405
01
Sign

Each event gets an HMAC signature over its body and timestamp, plus a stable idempotency key.

02
Deliver

A POST to your endpoint, with a tight timeout. A 2xx is the only thing that counts as success.

03
Retry

Anything else is retried on exponential backoff, so a brief outage never loses the event.

04
Dead-letter

After the last attempt it parks in a DLQ with every attempt and the final reason.

05
Replay

One click resends a parked event once your endpoint is healthy. Nothing is lost.

On your side: verify, then dedupe.

Delivery is only half of it. Your endpoint recomputes the signature over the raw body with your secret and compares it in constant time, rejecting anything forged or stale. Then it checks the idempotency key, so a retry of an event you already handled is acknowledged, not run twice. We ship this handler with the build.

verify.ts
1// verify.ts — reject what we didn't sign, dedupe the rest
2import { createHmac, timingSafeEqual } from "node:crypto";
3
4export function verify(req, secret, seen) {
5 const sig = req.headers["x-signature"];
6 const ts = req.headers["x-webhook-timestamp"];
7 if (now() - Number(ts) > 300) return reject("stale"); // replay window
8
9 const expected = "sha256=" + createHmac("sha256", secret)
10 .update(ts + "." + req.rawBody).digest("hex");
11 if (!timingSafeEqual(buf(sig), buf(expected)))
12 return reject("bad signature"); // constant-time
13
14 const id = req.headers["x-webhook-id"];
15 if (seen.has(id)) return ack("duplicate"); // idempotent
16 seen.add(id);
17 return handle(req.body);
18}
incomingPOSTsignature valid?HMAC · constant-time401 rejectforged / staleseen this id?idempotency200 ackdedupedhandlerun once200 ackprocessedbadvalidseennew

The promises that make it dependable.

A webhook you can build a business process on has to be more than a POST. These are the guarantees we wire in, so the other side can trust every event.

At-least-once, deduped to once

Retried until delivered, and idempotency keys let your endpoint collapse the duplicates to a single effect.

Signed and timestamped

HMAC over the raw body plus a timestamp, so payloads can't be forged and old ones can't be replayed.

Exponential backoff

Retries space out, 2s to minutes to hours, so a struggling endpoint isn't hammered while it recovers.

Auto-disable on hard failure

An endpoint that keeps 4xx-ing is paused and you're alerted, instead of piling up dead deliveries forever.

Dead-letter and one-click replay

Nothing is silently dropped. Failed events wait with their history and replay when you're ready.

A searchable delivery log

Every attempt, response code, and payload recorded, so a missed webhook is a query, not a mystery.

Firing a POST is a line of code. Delivering it, exactly once, is the system.

Almost every webhook bug is the same story: the one that fired twice and double-charged, the one that got lost when the endpoint blinked, the forged one nobody checked. None of that shows up in a demo on a good day. It shows up in production, on the bad one. So we build the signing, the idempotency, the backoff, and the dead-letter queue up front, because that boring machinery is the entire difference between a webhook and a promise you can actually keep.

Webhooks your integrations can build on.

Signed payloads, idempotency keys, retries with backoff, and a dead-letter queue with replay, so every event lands once and can be proven.