WhatsApp Cloud API
22% of Our WhatsApp Template Messages Failed. Here Is Every Error Code.
Four months of production Cloud API logs — 4,609 outbound messages, 1,027 failures, and the real distribution of error codes 131049, 131026, 130472 and 131008. What each one means and which are actually your fault.
Padmesh Bhavsar · Founder, InboundSage · 8 September 2026 · 11 min read
Meta publishes a list of WhatsApp Cloud API error codes. It is accurate and almost useless, because it tells you what each code is called and not which ones you will actually hit, how often, or what to do about them.
So here is the other half. Every outbound message our platform has sent since May, grouped by what came back.
4,609 outbound messages. 1,027 failed. 22.3%.
That number is worse than it sounds and better than it looks, and the difference between those two readings is the whole point of this post.
The headline split
| Status | Messages | Share |
|---|---|---|
| Read | 2,701 | 58.6% |
| Delivered (not yet read) | 801 | 17.4% |
| Sent (no delivery receipt yet) | 80 | 1.7% |
| Failed | 1,027 | 22.3% |
Of the messages that were delivered, 75% were read. That is the number people quote when they sell you WhatsApp, and it is real. The 22% that never arrived is the number nobody quotes.
Almost all of it is template sends
Split the same messages by type and the failures collapse into one column:
| Message type | Sent | Failed | Failure rate |
|---|---|---|---|
| Template | 3,827 | 1,022 | 26.7% |
| Interactive | 444 | 1 | 0.2% |
| Text | 330 | 4 | 1.2% |
| Image | 7 | 0 | 0% |
| Document | 1 | 0 | 0% |
Session messages — replies inside the 24-hour customer service window — essentially do not fail. 5 failures out of 782 sends, 0.6%.
Template messages, the ones you send to start a conversation, fail more than one time in four.
This is the single most useful thing in our logs. If you are debugging "WhatsApp API not working", the shape of the problem is almost certainly not your HTTP client, your token, or your webhook. It is that business-initiated messages are governed by an entirely different set of rules from replies, and those rules reject a lot of sends.
The full error distribution
Every failure code we have recorded, in order:
| Code | Meta's title | Count | Share of failures |
|---|---|---|---|
| 131049 | This message was not delivered to maintain healthy ecosystem engagement | 593 | 57.7% |
| 131026 | Message undeliverable | 260 | 25.3% |
| 130472 | User's number is part of an experiment | 59 | 5.7% |
| 131008 | Required parameter is missing | 45 | 4.4% |
| 132001 | Template name does not exist in the translation | 15 | 1.5% |
| 132000 | Number of parameters does not match the expected number | 15 | 1.5% |
| 131053 | Media upload error | 8 | 0.8% |
| 131042 | Business eligibility payment issue | 6 | 0.6% |
| 132005 | Translated text too long | 5 | 0.5% |
| 131047 | Re-engagement message | 5 | 0.5% |
| 132012 | Parameter format does not match the created template | 3 | 0.3% |
| — | internal error or no code recorded | 13 | 1.3% |
Now group them by who can actually fix them:
- Meta's decision, not yours — 131049, 130472, 131047: 657 failures, 64%.
- The recipient — 131026: 260 failures, 25%.
- Your code or your template — 131008, 132000, 132001, 132005, 132012, 131053: 91 failures, 8.9%.
- Your billing — 131042: 6 failures.
- Ours, internally, or unrecorded — 13 failures that never reached Meta or never had a code stored.
Only about 9% of our failures were bugs we could fix by writing better code. That is the honest read, and it cuts both ways: it means most of your effort should go into who you send to and why, not into your integration.
131049 — the one that will dominate your logs
This message was not delivered to maintain healthy ecosystem engagement.
593 failures. More than half of everything that went wrong.
This is Meta's per-user marketing frequency cap. It is not a rate limit on your number, it is a limit on how much marketing-category messaging that individual person is willing to absorb, inferred from their behaviour — blocks, reports, and how much marketing they are already receiving from everyone else.
Three consequences that are not obvious:
- It is per recipient, not per business. The same broadcast can fail for one contact and succeed for the next, at the same second.
- It applies to marketing templates. Utility and authentication categories are not subject to it in the same way, which is a strong argument for classifying your templates honestly rather than tagging everything as marketing.
- Retrying does not help. It is a decision about a person, not a transient error. Retrying the same marketing template at the same recipient is how you convert one failure into a pattern.
Here is the actual payload, redacted:
{
"id": "wamid.HBgMOTE5…",
"status": "failed",
"timestamp": "1780457431",
"recipient_id": "9199…",
"errors": [
{
"code": 131049,
"title": "This message was not delivered to maintain healthy ecosystem engagement.",
"message": "This message was not delivered to maintain healthy ecosystem engagement.",
"error_data": {
"details": "In order to maintain a healthy ecosystem engagement, the message failed to be delivered."
}
}
]
}
What actually reduces it: fewer marketing templates and more utility ones; sending to people who have recently interacted rather than to a list; and spreading a broadcast out instead of firing it at once. What does not reduce it: changing your template text, buying a new number, or retrying.
131026 — "Message undeliverable"
260 failures, a quarter of the total. Meta's title is unhelpfully vague; in practice it is one of:
- The number does not have a WhatsApp account.
- The number is wrong — a landline, a 10-digit number sent without the country code, a typo.
- The recipient has an old WhatsApp version or has never accepted the terms.
- The recipient has blocked your business.
This one is a data quality problem, not an API problem. Ours came overwhelmingly from imported contact lists rather than from people who had messaged us first. The fix is upstream: validate at capture, store numbers in E.164 with the country code, and mark a contact as unreachable after a 131026 rather than including them in the next broadcast — otherwise you pay the same failure every time forever.
130472 — "User's number is part of an experiment"
59 failures, and the one that causes the most confused support tickets, because it sounds like something is wrong with your account.
Nothing is wrong with your account. Meta runs holdout experiments in which a slice of users are excluded from receiving business marketing messages, so that the effect of those messages can be measured. Your recipient is in that slice.
{
"code": 130472,
"title": "User's number is part of an experiment",
"error_data": {
"details": "Failed to send message because this user's phone number is part of an experiment"
}
}
There is no fix and no appeal. Treat it as a soft failure, do not count it against your list quality, and — importantly — do not let it silently retry, because it will fail identically.
131008 — this one really is your fault
45 failures, and every one of them was our bug. The error_data.details field tells you exactly which:
{
"error": {
"code": 131008,
"type": "OAuthException",
"message": "(#131008) Required parameter is missing",
"error_data": {
"details": "buttons: Button at index 0 of type Url requires a parameter",
"messaging_product": "whatsapp"
}
}
}
A template with a dynamic URL button needs a button component in the send payload carrying the URL suffix. We had templates approved with dynamic buttons and a send path that only filled the body parameters. Every send failed until the button component was added.
The general lesson: error_data.details is the field that matters. The top-level message is a category. The details string names the exact component. If you log only the code and the title, you have thrown away the only part that tells you what to change.
132000, 132001, 132005, 132012 — template mismatches
38 failures between them, all the same class of problem: the send payload and the approved template disagree.
- 132000 — parameter count mismatch. You approved a template with three
{{n}}placeholders and sent two. - 132001 — the template name or language does not exist. Usually
enversusen_US: the template was approved in one and the send asked for the other. - 132005 — a filled parameter made the rendered text longer than the limit.
- 132012 — the parameter format does not match, typically a currency or date-time parameter sent as a plain string.
All four are preventable by validating against the template definition before the send rather than discovering it from Meta. If your platform does not do that for you, fetch the template list once a day and check parameter counts locally.
HTTP 200 does not mean delivered
This is the structural point that catches people building their first integration, and it is why our numbers look the way they do.
When you POST to /messages, a success returns a wamid and HTTP 200. That means Meta accepted your request, nothing more. The verdict comes later, on the status webhook, as a separate event with "status": "failed".
From our logs, that verdict arrives fast — but not synchronously:
| Code | Median time to failure webhook | 90th percentile |
|---|---|---|
| 131026 | 1.1 s | 3.5 s |
| 131049 | 2.2 s | 3.2 s |
| 130472 | 2.8 s | 3.6 s |
A couple of seconds, consistently. Which means an integration that treats HTTP 200 as "sent successfully" and never processes status webhooks will report a 100% success rate while a fifth of its messages are failing. If you have ever wondered why your own dashboard disagrees with what customers tell you, this is usually the reason.
If you are building directly on the Cloud API, you need the webhook receiver before you need anything else.
What our failure rate did over time
| Month | Outbound | Failed | Rate |
|---|---|---|---|
| June | 2,047 | 517 | 25.3% |
| July | 780 | 72 | 9.2% |
| August | 1,591 | 404 | 25.4% |
| September (to date) | 171 | 27 | 15.8% |
July was not a better month of engineering. July was a month of mostly transactional, utility-category sending to people who had recently interacted. June and August contained list-based marketing broadcasts.
The failure rate is a function of who you send to and under which category — not of how good your code is. Same platform, same number, same templates, 9.2% versus 25.4%.
The short version
- Session replies almost never fail. Business-initiated templates fail about a quarter of the time.
- More than half of all failures are 131049, a per-recipient marketing cap you cannot code around.
- A quarter are 131026, which is a contact-data problem you should fix at capture.
- Fewer than one in ten are bugs in your integration — but those are concentrated in template parameter handling, and
error_data.detailsnames the exact one. - Never treat HTTP 200 as delivered. Process the status webhook or your metrics are fiction.
FAQ
What does WhatsApp error 131049 mean?
It means Meta declined to deliver a marketing-category message to that specific recipient in order to limit how much marketing that person receives. It is a per-user decision based on their behaviour, not a limit on your number, and it was 57.7% of all our send failures. Retrying does not help; sending fewer marketing templates and more utility ones does.
Why does WhatsApp error 131026 keep happening?
131026 means the message is undeliverable to that number — usually there is no WhatsApp account on it, the number is malformed or missing a country code, or the recipient has blocked the business. It is a contact-data problem rather than an API problem. Mark the contact unreachable after a 131026 instead of re-sending to it in the next broadcast.
What is WhatsApp error 130472?
Meta excludes a random slice of users from receiving business marketing messages so it can measure the effect of those messages. If your recipient is in that holdout group, the send fails with 130472. Nothing is wrong with your account and there is no fix — treat it as a soft failure and do not retry.
Does an HTTP 200 from the WhatsApp Cloud API mean the message was delivered?
No. A 200 with a wamid means Meta accepted the request. The delivery verdict arrives afterwards on the status webhook, typically within one to three seconds in our logs. An integration that does not process status webhooks will report success for messages that failed.
What is a normal WhatsApp template failure rate?
Ours ranged from 9.2% to 25.4% across four months on the same platform and number — the variable was audience and template category, not code. Broadcast-heavy months to imported lists ran around 25%; utility messaging to recently active contacts ran near 9%.
Which WhatsApp API errors should I retry?
Very few. 131049, 130472 and 131026 are decisions about the recipient and will fail identically on retry. Genuine transient conditions — rate limiting and 5xx responses — are worth a backoff retry. Retrying a per-recipient rejection just multiplies the same failure.
Figures are aggregate counts from 5,985 messages logged on the InboundSage platform between 20 May and 8 September 2026, across our own and pilot accounts. No customer, contact or message content is published; sample payloads are redacted. Error-code definitions are Meta's; the interpretation and the counts are ours.
Send on WhatsApp without guessing
InboundSage is a Verified Meta Tech Provider platform: templates, broadcasts, a team inbox and a developer API, on your own WhatsApp Business number. Every send is logged with the exact Cloud API response, so a failure tells you which failure.