Blog
Webhooks

Webhook handlers that never miss a result

Acknowledge fast, process once, and know what to do when retries run out. A short guide to the delivery guarantees you can build on.

Webhooks are how your product finds out that a referral was issued or a result came back. Most integration problems we see come from three assumptions that do not hold. Here is what you can rely on, and how to build around the rest.

Acknowledge within three seconds

Your endpoint has 3 seconds to return a 2xx. Anything slower counts as a failure and is retried. So do not fetch the result, write to your database and send a notification inside the request.

Verify the signature, store the event, return 200, and do the work in the background.

const json = express.raw({ type: 'application/json' });
app.post('/webhooks/eirly', json, async (req, res) => {
const signature = req.header('x-bgt-hmac-sha256') ?? '';
if (!isFromEirly(req.body, signature)) {
return res.status(401).end();
}
await queue.add('eirly-event', JSON.parse(req.body.toString()));
res.status(200).end();
});

Expect duplicates, process once

A delivery can arrive more than once. Every event carries an eventUuid; record the ones you have processed and skip any you have seen. A unique index on that column is the simplest way to make this hold under concurrency.

Order is per entity, and a failure holds the line

Events for the same referral or result arrive in order: referral.created always comes before referral.updated. If one delivery fails, later events for that entity wait until it succeeds or its retries run out. Events for other entities keep flowing.

So a slow or failing endpoint for one referral does not block the rest, and you will never see an update before the create it depends on.

When retries run out, poll

A failed delivery is retried four more times, with gaps that grow from about one minute to about sixteen. The whole schedule spans roughly half an hour. After the fifth failure we stop, and the API is the source of truth: fetch the referral or result directly.

A nightly job that lists recent referrals and compares them with what you hold is cheap insurance.

Summary

Rely onBuild around
Signed payloadsVerify every one, with a timing-safe compare
Order per entityNo order across different entities
Retries for about 30 minutesPoll the API after retries are exhausted
One eventUuid per eventThe same event delivered more than once

The webhooks guide has the full list of events and the payload shape.