Skip to main content
Six habits. The first two are the ones that matter most; the rest follow from them.

Acknowledge first, work afterwards

Answer 2xx as soon as you have the payload, then queue the work. Your endpoint has 30 seconds, and a handler that does its processing inline is a handler whose latency is your database’s latency. The shape that works: verify the signature, write the event to your own queue or table, answer 2xx, process from there.

Make processing idempotent

Design so that handling the same event twice changes nothing the second time. Key on the identifier in the payload and record what you have already handled. This is worth doing even where you believe an event arrives once, because the alternative failure — paying someone twice — is not recoverable by an apology.

Treat the event as a signal, not as the truth

The payload tells you something changed. When the decision matters — releasing goods, paying someone, telling a customer their money arrived — read the resource back and act on what it says now. An event describes a moment. The resource describes the present, and by the time you process a queued event those can differ.

Never trust an unverified payload

Check the signature before you read a single field. An endpoint that acts on the body first and verifies later has already acted. See Validate signatures.

Ignore what you do not recognise

Answer 2xx to an event name you do not handle, and log it. New events are added over time, and a handler that throws on an unfamiliar name turns an addition into an outage. Answering 4xx instead discards the event for good: a refusal is the one failure that is never retried. The same goes for unfamiliar fields inside a payload you do handle.

Keep the endpoint available

A transient failure is retried four times over roughly 80 minutes, so a brief outage costs you nothing. A longer one does: after the last retry the event is not sent again. Uptime is still part of the integration — the retries buy you minutes, not a day. Where correctness depends on not missing something, pair the webhook with a read: reconcile against the resource on a schedule, so a gap is something you notice rather than something a customer reports.