Chapter 1 · The scenario
Example: Fee receipts that arrived after the question
A Bhubaneswar coaching institute sends each new student three messages: a welcome text, a fee receipt as a PDF and a reply-button question about batch timings. Its sender used 20 parallel workers, and roughly one student in ten saw the batch question before the receipt — some replied 'what fee?' before the PDF appeared. The team moved the welcome line into the document's caption, gave each student a single lane, and released the question only when the receipt reported delivered, with a two-minute timeout. Misordered threads stopped, and the send time for a 3,000-student intake barely changed, because different students still went out in parallel.
Chapter 2 · What is outbound message ordering?
Outbound message ordering is the order in which messages a business sends reach the user's chat. Meta's documentation states that the order in which messages are delivered is not guaranteed to match the order of your API requests. Each call to the messages endpoint is processed on its own, so three messages fired a few milliseconds apart — a greeting, an invoice PDF and a payment question — can land as question, greeting, invoice.
The causes sit on both sides. In your system, parallel workers, connection pools and retries mean the second request can reach Meta before the first, or the first can be retried after the second succeeded. On the platform side, messages are not all equal: a media message referenced by a link has to be fetched before it can go out, while a short text has nothing to wait for. A successful HTTP response for each call says only that the request was accepted, not where it will sit in the chat.
Meta's guidance for controlling sequence is to wait until one message's Message Status reaches delivered on the Statuses Webhook before sending the next. That is a per-recipient concern: only the next message to the same user needs to wait, while messages to everyone else keep flowing up to the number's Throughput. It is the mirror image of Webhook Event Ordering, which is about inbound events — statuses and customer replies — reaching your server out of order.
Key takeaways
- Outbound message ordering is the sequence in which your messages reach a user, which WhatsApp does not guarantee to match your API calls.
- Meta's advice: when order matters, wait for the previous message's delivered status before sending the next one.
- Parallel workers, retries and media that takes longer to process are the usual reasons a later message overtakes an earlier one.
- Sequence per recipient, not globally: one lane per user keeps order while other users' messages flow in parallel.
- Often the best fix is fewer messages — a caption on the document, the question inside the same interactive message.
Chapter 3 · How it unfolds
How does outbound message ordering work?
Combine where you can
Send one message instead of three: a caption on the image or document, a header and body on the interactive message. A single message cannot arrive out of order.
Give each recipient a lane
Partition the Message Queue by the user's WhatsApp ID so only one message per user is in flight, while different users are still sent in parallel.
Send, then wait for a status
After the API returns the WhatsApp Message ID (wamid), hold that user's next message until a
delivered(orread) status arrives — Meta's recommendation — orsentwhere speed matters more than certainty.Bound the wait
A phone that is offline may not report delivered for hours. After a timeout, decide per flow: send anyway, collapse the remaining messages into one, or wait for the customer's next message.
Stop the lane on failure
If a message fails, pause that user's lane and decide whether the rest still make sense, instead of delivering step three of a sequence whose step two never arrived.
Chapter 4 · In code
Outbound message ordering in code
// The next message to a user waits for the previous one's delivered (or read) status.const lanes = new Map(); // recipient -> tail of that user's promise chainconst waiting = new Map(); // wamid -> callback from the statuses webhookexport function sendInOrder(to, message) {const prev = lanes.get(to) ?? Promise.resolve();const next = prev.then(async () => {const wamid = await send(to, message);await waitForStatus(wamid, 120000); // Meta: confirm delivered before the next send});lanes.set(to, next.catch(() => {})); // a failure must not jam the lane foreverreturn next;}async function send(to, message) {const res = await fetch("https://graph.facebook.com/v26.0/" + process.env.PHONE_NUMBER_ID + "/messages", {method: "POST",headers: { Authorization: "Bearer " + process.env.ACCESS_TOKEN, "Content-Type": "application/json" },body: JSON.stringify({ messaging_product: "whatsapp", to, ...message }),});const data = await res.json();if (!res.ok) throw new Error(data.error?.message ?? "send failed");return data.messages[0].id; // accepted, not yet delivered}function waitForStatus(wamid, ms) {return new Promise((resolve, reject) => {const done = (fn) => { clearTimeout(timer); waiting.delete(wamid); fn(); };const timer = setTimeout(() => done(resolve), ms); // offline phone: stop waiting, apply your rulewaiting.set(wamid, (status) => {if (status === "delivered" || status === "read") done(resolve);if (status === "failed") done(() => reject(new Error("failed: " + wamid)));});});}// Call from your statuses webhook handler for every item in value.statuses.export function onStatus(s) {waiting.get(s.id)?.(s.status);}
In-memory for clarity. In production, keep lanes in a queue partitioned by recipient and record statuses that arrive before the wait begins.
Chapter 5 · Where it goes wrong
Common mistakes with outbound message ordering
Sequencing globally
One queue with a single worker keeps order but caps the whole number at one message at a time. Sequence per recipient and parallelise across recipients.
Using the 200 response as the go signal
The response only means accepted. Two accepted requests can still be delivered in either order, so wait for a status before releasing the next one.
Waiting forever for delivered
An offline phone can hold a lane for hours. Always set a timeout and a rule for what happens to the rest of the sequence.
Retrying out of turn
A retried message one, sent after message two succeeded, arrives last. Retry inside the lane, before the next message is released.
Firing long bursts at one user
Rapid sequences to a single user also run into the Pair Rate Limit. Fewer, richer messages solve both the ordering and the throttling problem.
Chapter 6 · The facts
Outbound message ordering: key facts and limits
| Delivery order | Not guaranteed to match API request order (Meta) |
|---|---|
| Meta's fix | Confirm delivered before the next send |
| Sequence scope | Per recipient, not per business number |
| Send response | Means accepted — says nothing about order |
| Default throughput | Up to 80 messages per second per number |
| Same-user bursts | Throttled by the pair rate limit (error 131056) |
| Inbound counterpart | Webhook event ordering |
Chapter 7 · Why it matters
Why does outbound message ordering matter?
Order carries meaning in a conversation. A payment link that arrives before the invoice it pays, an answer above its question, or 'here is your boarding pass' after the pass itself makes a business look broken, and in support flows a misordered reply can push the customer into the wrong action. Getting order right is cheap when it is designed into the sender from the start, and painful to retrofit once bots, agents and campaign jobs all fire messages from many workers at once.
Chapter 8 · What good teams do
Best practices for outbound message ordering
- Collapse multi-part replies into one message whenever the format allows it.
- Key lanes by the recipient's WhatsApp ID (or business-scoped user ID) so all workers share them.
- Log send time and delivered time per wamid; a widening gap is your early warning.
- Gate fast bot replies on sent, and order-critical sequences on delivered.
Chapter 9 · Outbound Message Ordering FAQs