There is an uncomfortable moment almost everyone hits the first time they automate WhatsApp: the dashboard says the message was sent, the API returned 200, there is a message id, and the customer swears nothing arrived.
It is not a bug in your integration. It is the 24-hour window, and it is worth understanding before you build anything on top of it.
What the 24-hour window actually is
WhatsApp separates two situations that look identical from a business point of view and are not identical to the platform:
- You are replying to someone. The contact wrote to you and you answer. WhatsApp calls this a service conversation and lets you send free-form text: whatever you like, however you like.
- You are starting the conversation. The contact has not written, or wrote a while ago. Here WhatsApp does not let you improvise.
The boundary between the two is a 24-hour window counted from the contact's last inbound message. Inside it, free text. Outside it, only a template Meta has approved in advance.
The word inbound matters: the window is not opened by your message, it is opened by theirs. And every message they send resets it.
The 200 that means nothing
Here is the trap that costs people whole afternoons.
When you send outside the window, Meta's API does not reject you. It answers 200, hands you a wamid, and everything looks fine. The message has been accepted, which is not the same as delivered.
The real verdict arrives afterwards, by webhook, as status updates:
sent β delivered β read
β¦or:
failed β 131047 "Re-engagement message"
That 131047 is the platform telling you: I accepted your message, but more than 24 hours have passed since that person wrote to you, so I dropped it.
If your system treats the 200 as success, your dashboard will report 500 messages sent when 120 arrived. And you will not know, because nothing visibly failed.
The rule: a
200from Meta means accepted for processing. The only status that means delivered isdelivered, and it arrives later, by webhook. Any metric built on the200is inflated.
So how do you reach a cold contact
With an approved template. That is the only route.
A template is a message you register with Meta in advance, a reviewer approves, and which can contain variables ({{1}}, {{2}}β¦) you fill in at send time. Outside the 24-hour window an approved template gets through; nothing else does.
This has a design consequence people tend to discover late: any automation that writes to someone who has never written to you must use a template, always. A "new lead" flow that sends a free-text welcome is doomed by definition: a contact who has never written to you has opened no window.
The dynamic URL button trap
One specific case deserves a warning, because Meta's template editor will happily let you build it and then it cannot be sent.
If you register a template with a URL button and put a variable inside the address, something like https://yourdomain.com/order/{{1}}, that button is marked dynamic. From then on, every send is required to include a button component carrying that value. If your automation tool has no field to fill it in, Meta answers:
131008 Required parameter is missing
Every single time. The template is approved, it looks perfect in the editor, and it can never be sent.
Unless you control the send at a low level, the advice is simple: use static URL buttons. Put the variable in the message body, not inside the link.
The three rules, together
| Situation | What you can send | What happens if you get it wrong |
|---|---|---|
| Within 24 h of their last message | Free text | Nothing |
| Outside the window | Approved template only | 200, then 131047 |
| Template with a dynamic URL button | Nothing, in practice | 131008 on every attempt |
What your tooling should do about it
If you are evaluating a messaging platform or building your own, these are the questions that separate a serious integration from one that will bite you:
- Does it know the window state before sending? Deciding afterwards is too late.
- Does it skip the send and tell you why, rather than firing anyway and letting Meta drop it silently?
- Does it store the real status that arrives by webhook, or does it keep the initial
200? - Does it catch an unsendable template, the dynamic button case, before trying?
The safest default is to mark a send that cannot arrive as skipped, with the reason recorded, rather than counting it as sent. A dashboard reporting green while messages are being dropped is worse than one reporting red: a red number gets investigated, while a green one lets a business lose customers for months without anyone noticing.
Summary
- The 24-hour window is opened by the contact's message, not yours.
- Outside it, only an approved template gets through.
- A
200is accepted, not delivered. The real status arrives by webhook. 131047= outside the window.131008= missing dynamic button parameter.- Any automation that makes first contact needs a template, no exceptions.
If your WhatsApp campaigns report perfect delivery and strange conversion, start here: you are very likely measuring acceptances, not deliveries.


