Recipe

Handle out-of-order webhooks

Webhook delivery is unordered. A retried payment.failed can land after the payment.succeeded that superseded it. Never drive state purely from arrival order. Compare the event created timestamp against what you have, and re-fetch current state when in doubt.

2 min read

UnorderedDelivery reality
createdCompare timestamps
Re-fetchWhen unsure

Guard with timestamps

Store the created of the last event you applied per resource. Ignore any event older than that.

if (evt.created <= lastApplied[resourceId]) return;   // stale, skip
applyEffect(evt);
lastApplied[resourceId] = evt.created;

Re-fetch on conflict

When two events touch the same field close together, treat the webhook as a nudge and read the authoritative state from the API. The event’s data.object is a snapshot; the read endpoint is the source of truth.

Pair with idempotency

Ordering guards and idempotency solve different problems — duplicates vs sequence. Use both. See Make a webhook handler idempotent.

Frequently asked questions

Can I ask for ordered delivery?

No. Order is not guaranteed and cannot be enabled. Reconcile with timestamps and current state instead.

Which timestamp do I trust?

The envelope created, which marks when the event occurred — not when it was delivered. Delivery time drifts with retries; created does not.

Does ordering matter for every event?

It matters wherever two events touch the same field or resource close together — a payment.failed then a payment.succeeded, or two rapid account.updated events. For independent events it rarely bites, but guarding on created is cheap insurance across the board.

Funding for UK limited companies

Credicorp lends to your company, not to you personally — short-term working capital with no personal guarantee. See what your business could access.