What is webhook event ordering?
Webhook event ordering is the work of putting WhatsApp events back into the order they happened, because Meta does not guarantee the order in which webhooks reach you. Each event is delivered on its own, a delivery that fails is retried later under the webhook retry policy while newer events carry on, and events about the same message come from different parts of the platform. Arrival order is therefore not event order: a read status can arrive before the delivered for the same WhatsApp message ID (wamid), a customer's reply can land before the read status of the message they are answering, and a retried event from an hour ago can turn up behind dozens of newer ones.
Two signals let you rebuild the true sequence. Every message and status carries a timestamp — Unix seconds, sent as a string — recording when the event was triggered on Meta's side rather than when it reached you; with one-second resolution, ties are common. And statuses have a natural precedence: sent, then delivered, then read (and played for voice messages), with failed replacing only a message that never got further than sent. Meta's reference also notes that no delivered webhook is sent when a message is read immediately, because the read implies delivery — so a missing delivered is normal.
The working rule is to merge, never overwrite: store each event, derive the current message status, and only ever move a message forward. That is different from webhook deduplication, which removes repeats of the same event; ordering puts distinct events into the right sequence. It also differs from outbound message ordering, which is about the messages you send reaching the customer in the order you intended.
Key takeaways
- Webhook event ordering is rebuilding the true sequence of WhatsApp events when they reach your server out of order.
- Meta does not guarantee delivery order, and a retry can deliver an old event hours or days after newer ones.
- Rank statuses sent, delivered, read, played, and only ever move a message forward; failed replaces only sent.
- No delivered webhook is sent when a message is read immediately, because the read implies delivery.
- Sequence by the event's own timestamp (Unix seconds, sent as a string), not by when it reached your server.
Common mix-ups
Common mistakes with webhook event ordering
Last write wins
Writing the most recently arrived status turns read back into delivered whenever a retry lands late. Compare ranks before changing the status.
Sorting by arrival time
Received-at reflects your network and Meta's retries, not the customer. Sequence by the event's own timestamp, with rank as the tie-breaker.
Expecting delivered before every read
Meta skips the delivered webhook when a message is read immediately. Reports that require both undercount deliveries; infer delivery from the read.
Processing one customer in parallel
Two workers handling the same customer's messages at once can answer them out of order. Partition by customer so their events are processed one at a time.
How does webhook event ordering work?
- Step 1
Store events, derive state
Keep an append-only record of each event from the statuses webhook and every inbound message, then compute the current state from it. You can always recompute; you cannot undo an overwrite.
- Step 2
Rank statuses and only move forward
Give each status a rank — sent 10, failed 15, delivered 20, read 30, played 40 — and change the stored status only when the incoming rank is higher. A late delivered after a read updates nothing but its timestamp.
- Step 3
Fill gaps from stronger evidence
A read implies delivery. Set
delivered_atprovisionally from the read and let a later, earlier-timestamped delivered correct it by keeping the earliest time seen. - Step 4
Sequence inbound messages per customer
Process one customer's events in timestamp order — partition queues by customer ID — and use
context.idon a reply to link it to the message it answers. - Step 5
Treat very late events as history
Retries can arrive hours or days later. Record them in the right place on the timeline, but do not fire time-sensitive side effects, such as an auto-reply, for a conversation that has moved on.
Webhook event ordering in code
const RANK = { sent: 10, failed: 15, delivered: 20, read: 30, played: 40 };export function mergeStatus(current, event) {const at = Number(event.timestamp) * 1000; // Unix seconds, sent as a stringconst next = { ...current };if (["delivered", "read", "played"].includes(event.status))next.deliveredAt = Math.min(current.deliveredAt ?? Infinity, at); // read implies deliveredif (["read", "played"].includes(event.status))next.readAt = Math.min(current.readAt ?? Infinity, at);if ((RANK[event.status] ?? 0) > (RANK[current.status] ?? 0)) {next.status = event.status; // only ever move forwardnext.statusAt = at;}return next;}
Higher rank wins the status; the earliest timestamp wins each milestone.
Example: Flickering ticks at a Bengaluru grocery app
A Bengaluru grocery-delivery app's agents complained that order-update messages showed blue ticks, then grey ones a moment later. The status handler wrote whatever arrived last, and delivered webhooks retried after brief endpoint hiccups were landing after the read. The team added a status rank and stored the earliest timestamp per status, so a late delivered now only corrects delivered_at. The flicker stopped, and the weekly read-rate report rose by a few points because reads were no longer being overwritten (figures illustrative).
Webhook event ordering: key facts and limits
- Delivery order
- Not guaranteed by Meta
- Event timestamp
- Unix seconds, as a string
- Status precedence
- sent → delivered → read → played
- failed
- Replaces sent only
- Delivered when read instantly
- Not sent; the read implies it
- Latest a retry can arrive
- Up to 7 days after the event
Why the difference matters
Why does webhook event ordering matter?
Order mistakes are visible to everyone. Agents watch a read receipt turn back into a delivery receipt — blue ticks going grey — read-rate reports undercount because a late delivered overwrote a read, SLA timers start from the wrong moment, and a chatbot answers an older question after a newer one has been handled. None of it throws an error, so it surfaces as distrust in the numbers. A small amount of sequencing logic — a rank per status and a timestamp per event — makes every dashboard and conversation view consistent however the webhooks arrive.
Webhook Event Ordering FAQs
Does Meta guarantee webhook order?+
Why did I get read but never delivered?+
Is webhook ordering the same as deduplication?+
Can I rely on timestamps alone?+
Rules of thumb
Best practices for webhook event ordering
- Keep both the event timestamp and the received-at time; the gap between them is your best measure of webhook lag.
- Run ordering after deduplication, so repeats never reach the merge logic.
- Test with a shuffled replay of real payloads — if the final state differs from the in-order replay, the merge logic has a bug.