Messaging

Message delivery errors

What a failed WhatsApp send looks like on the message record, the stable codes it maps to, and how to troubleshoot deliverability failures like Meta 131026.

Compatibility note (July 2026). Meta errors 131026 and 131042 previously used the generic provider_error Tyxter code. They now map to meta_message_undeliverable and meta_business_eligibility_payment_issue. If your integration branched on error_code === 'provider_error', add the specific codes while continuing to read the numeric Meta code from provider_error.code.
Compatibility note (August 2026). Meta error 131049 previously used provider_error. It now maps to meta_marketing_frequency_cap, a recipient-scoped terminal result that must not pause the sending number. Continue reading the numeric Meta code from provider_error.code when handling older records.
Compatibility note (August 2026). Meta error 131037 previously used provider_error. It now maps to meta_display_name_not_approved. Continue reading the numeric Meta code from provider_error.code when handling older records.
Compatibility note (August 2026). Historical Meta 132001 rows may carry template_param_mismatch; new receipts carry template_not_approved. Continue reading the numeric Meta code from provider_error.code when handling older records.

Where failures surface

When a WhatsApp send fails, the message record carries three fields, readable at GET /v1/messages/{message_id}:

{
  "status":        "failed",
  "error_code":    "meta_message_undeliverable",
  "error_message": "Message undeliverable: Message Undeliverable. Retry addressing the recipient by phone number instead of a business-scoped user id, and remove links from the message if you are sending to one.",
  "provider_error": {
    "code":       131026,
    "message":    "Message Undeliverable",
    "error_data": { "details": "Message Undeliverable." },
    "fbtrace_id": "A..."
  }
}

error_code is a stable, registered Tyxter code — safe to branch on. error_message is human-readable and may change; for a mapped code it ends with the next step to take. provider_error is the raw error object from Meta, preserved verbatim for diagnosis. Failures reach you two ways: rejected inline when you create the message, or reported later by Meta and delivered to your webhook endpoint as a message.failed event. Both paths record the same fields on the message, and the message.failed payload carries error_code, error_message and provider_error inline — so a webhook receiver does not have to call back to learn why the send failed.

The bounded mapping

error_code is never a raw provider string. Known Meta failure codes map to specific Tyxter codes; everything else maps to provider_error with the Meta detail preserved:

Meta codeTyxter error_codeWhat to do
190provider_connection_unauthorizedThe stored WhatsApp connection credential is expired or revoked. Every failed message includes an organization-targeted /connect-whatsapp?organization_id=… link to re-run Embedded Signup; retrying before reconnecting cannot succeed.
131047service_window_requiredFree-form message outside the 24-hour customer service window (opened by an inbound message from the recipient). Send an approved template instead — GET /v1/templates.
131008 (template messages) / 132000 / 132007template_param_mismatchThis row is the asynchronous fallback: Meta rejected an already accepted send because a required template parameter is missing, the template name, language, or parameters no longer match the approved version, or the provider behavior is not modeled. Meta 131008 maps here only for template messages; for other message types it remains provider_error. Compare the send with the approved version before retrying. Modeled BODY/text-HEADER variables, media headers, and dynamic URL-button parameters instead fail synchronously with 400 template_param_mismatch before a message exists, so they do not appear in this delivery-error table.
132001template_not_approvedMeta 132001 maps to template_not_approved: Meta could not resolve the exact template name and language as approved in the sending WhatsApp Business Account. Check that exact name and language, then confirm approval before sending again. This terminal receipt does not identify why Meta could not resolve the template; the raw Meta code remains in provider_error.code.
131042meta_business_eligibility_payment_issueMeta blocked the send over the business's billing setup. Complete the business tax information and full address, then add or update the payment card in the billing section of Meta Business settings. Every send from the account fails until this clears, so it is worth alerting on.
131037meta_display_name_not_approvedA WhatsApp-provided (+1 555) number needs a display name. In WhatsApp Manager, go to Phone numbers and set the display name, then wait for Meta to approve it before sending again. This terminal receipt does not pause the sending number.
131053meta_media_download_failedMeta could not fetch, or could not use, the media for the send — the host was unreachable or too slow, returned an error, or served a file whose real content did not match the type it claimed. When the send carried a link you control, fix that link: it must be anonymously reachable and serve the correct type, and retrying the same link usually fails the same way. The most reliable fix is to stop sending links — upload the file at POST /v1/media/uploads and send it by asset_id. This code can also mean the bytes themselves are the problem, including for media Tyxter hosts: Tyxter validates the mime_type you declare, not the bytes you send, so a HEIC photo saved as .jpg and declared image/jpeg passes upload and still fails here. Two cases can no longer reach this code. Media uploaded through POST /v1/media/uploads or sent inline on POST /v1/messages has its declared mime_type checked against WhatsApp's accepted set up front and rejected with invalid_media_mime_type instead. And a send carrying a media.link is no longer handed to Meta as a link at all — Tyxter retrieves the file itself and sends the uploaded copy, so a URL Tyxter cannot retrieve reports media_link_fetch_failed and a URL serving an unaccepted type reports invalid_media_mime_type, both before the send is submitted (see below). What still reaches 131053 is media Meta receives but cannot use, and the narrow link cases Tyxter does not retrieve.
131026meta_message_undeliverableMeta could not deliver to that recipient. Retry addressing them by phone number rather than a business-scoped user id, and remove links from the message if you are sending to one. Recipient-specific and not transient — see the section below.
131062meta_business_scoped_id_mismatchA business-scoped user id issued under one business portfolio was used to send from another. These ids belong to a single portfolio — send using the id issued by the portfolio that owns this phone number.
130472meta_marketing_experiment_holdoutThe recipient is in Meta's marketing-message holdout group, so Meta withholds marketing sends to them. Expected loss, not a fault to fix — utility, service, and authentication messages are not affected.
131049meta_marketing_frequency_capMeta withheld this marketing message because the recipient recently received too many marketing messages. Do not retry it today. This is scoped to the recipient and does not pause the sending number; utility, service, and authentication messages to the same recipient are not affected.
anything elseprovider_errorMeta accepted the request shape but reported a failure Tyxter has no specific mapping for. Interpret the numeric code inside provider_error, which is always preserved.

WABA send-capability blocks

A fresh stored WABA block terminates a queued send before Meta is called, with a stable receipt and provider_error: null. When a Meta send error instead causes Tyxter to confirm that the connected WABA is blocked, that reactive receipt is also terminal and is not retried unchanged. Meta health code 141006 records meta_payment_method_required: add a payment method in WhatsApp Manager → Billing & payments. 141008 records meta_waba_inactive: reactivate the WABA in WhatsApp Manager or ask Meta support. 141011 records meta_messaging_permission_missing: reconnect and grant the requested WhatsApp messaging permissions, then ask support if it persists. If Meta reports more than one of these diagnostics, the primary remediation is deterministic: payment, inactive WABA, then messaging permission.

A reactive receipt retains the original bounded send failure in provider_error, even when its provider code was generic; the pre-send stored-block receipt has provider_error: null. The capability observation supplies the stable error_codein both cases. This does not suspend the connection or phone number, and inbound messages remain available. After the account is fixed, a positive Meta health observation clears that WABA's capability evidence so a later send can continue. Read the connection's optional waba_send_capabilities entry matching the sender waba_id for the latest trusted diagnosis. The singular send_capability, send_block_codes, and send_capability_observed_atfields are the compatibility view for the connection's primary WABA.

Failures that never reach Meta

Seven codes are stamped before any provider call, so a message carrying one has no provider_error to read — nothing was sent and nothing was billed. They are as stable and as safe to branch on as the mapped codes above.

TTS render failures

TTS audio is rendered before WhatsApp is called. tts_provider_errormeans the configured speech provider rejected or could not complete the render after retries; the message's provider_error carries a bounded provider name and, when available, status_code. tts_render_failed means rendering failed outside a provider response, such as storage or unexpected processing failure. In both cases the message never reached WhatsApp. Use the message trace_id when reporting a repeated tts_render_failed.

Media links Tyxter retrieves

When a media message carries message.media.link, Tyxter retrieves that file and uploads it to WhatsApp, then sends by the uploaded media id instead of passing your URL along. Nothing about the request changes: you still send media.link, GET /v1/messages/{message_id}still shows the message you created, and no new field appears. What changes is which failures you can see. Meta's own weblink fetch was the single largest source of 131053, and it gave you nothing to act on beyond the word "failed"; a retrieval Tyxter performs can say exactly what went wrong, and it lets the accepted-type and size rules that already apply to uploads apply to links too. A broadcast also retrieves and uploads the file once per sending phone number rather than once per recipient, so your origin is not hit thousands of times.

media_link_fetch_failed is the stable code for that retrieval failing: Tyxter could not get the bytes. The host did not resolve, refused the connection, or was rejected as a private or internal address; the request timed out; the URL answered a non-2xx status; it redirected more than three times; or it returned an empty body. Transient causes (DNS blips, timeouts, connection resets, 429 and 5xx answers) are retried automatically and stamp the code only once the attempts are exhausted; the rest fail the message on the first attempt, because retrying an unreachable or forbidden URL only repeats the same answer. To fix it, make the URL publicly reachable without authentication — https is strongly recommended and plain http is also accepted — serve the file bytes directly rather than an HTML preview or a login page, and answer within 30 seconds. The most reliable fix is to skip URLs entirely — upload the file once at POST /v1/media/uploads and send it by asset_id.

Two other codes surface on the same retrieval, and both mean Tyxter got the bytes and refused them. invalid_media_mime_type means the URL answered without a Content-Type header at all, or with one WhatsApp does not accept for that media kind — the same accepted set POST /v1/media/uploads enforces, so a URL cannot deliver a type an upload of the same file would have been refused. media_too_large means the response exceeded the per-kind size limit; the download is cancelled mid-stream rather than read to the end.

Keep media_link_fetch_failed and meta_media_download_failed apart, because the remediation differs: the first means Tyxter could not retrieve your URL and the message never reached Meta; the second means Meta could not fetch or use media handed to it. For the same reason, media_link_fetch_failed never means a provider rejected your send — no provider was contacted.

Retrieval covers the mediamessage type only, so several sends still pass a raw link through unchanged and Meta's 131053 remains the code to read for them. An interactive message header that carries a link is still delivered as a link: that header accepts only id — a media id you already hold at Meta — or link, and Tyxter does not issue Meta media ids, so there is no upload path for it through this API today. Flow message headers are text-only and never carry media at all. Template header media is always referenced by asset_id and is uploaded before the send, but a header image supplied as a raw link inside template.components bypasses this entirely and is passed through untouched. Also unretrieved: a link whose scheme is not http or https, channels whose provider has no media upload API (Instagram), and rare operational fallback modes. In sandbox, delivery is simulated and no URL is ever retrieved.

When Meta has acted against your WhatsApp account

provider_connection_suspended is the stable code for a send that never left Tyxter because Meta acted against the WhatsApp Business Account behind the connection — it was banned, rejected at account review, restricted, legacy-suspended with scheduled_for_disable, or the grant was lost because Tyxter's app was removed or the account was deleted. It is not a deliverability problem with the recipient and it is not a Meta-mapped code: the connection is suspended, so every send on it fails before any provider call and there is no provider_errorto read. This is deliberately wider than Meta's own action — a restriction or a legacy scheduled-disable suspension still leaves some sending possible at Meta, and Tyxter stops anyway rather than pushing traffic through an account Meta has already flagged.

Keep current SCHEDULE_FOR_DISABLE evidence separate: on a connected connection, nullable waba_ban_date is advisory and sends continue until Meta actually disables the account. Only an actual suspended connection receives this error.

Retrying cannot succeed until the account state changes, so treat it as an operator alert rather than a retryable failure. Read suspension_reason on the connection (GET /v1/provider-connections/{connection_id}) for the machine-readable reason, or the whatsapp channel's reason on GET /v1/provider-connections/status, which reports policy_suspended for this case. The remedy follows the reason: appeal the account at business.facebook.com/accountquality when it was banned, rejected at review, restricted, or legacy-suspended for disablement — replacing or re-authorizing the access token cannot lift any of those, because the credential is not what Meta acted on — and reconnect WhatsApp from the dashboard when the reason is app_uninstalled or account_deleted, where connecting again really is the fix.

Keep it apart from provider_connection_unauthorized, which means the stored credential expired or was revoked and re-authenticating restores sends. Its write-path twin is provider_connection_policy_suspended, the 409 a credential write (rotate, register, complete registration) gets for the same account state; see the /errors page. Sends resume on their own once Meta clears the account and the connection returns to connected.

Meta 131026 — Message Undeliverable

The most common delivery failure on production sends is Meta code 131026 ("Message Undeliverable"), which maps to meta_message_undeliverable: Meta could not deliver to that specific recipient. It is recipient-specific and it is not transient — retrying the identical send will fail again. The strongest diagnostic is comparison: when the same template and sender deliver to other recipients, your template, sender, and Tyxter configuration are all working, and the cause is one of:

To self-diagnose from Tyxter: read the sender's health at GET /v1/phone-numbers/{phone_number_id}— the response carries the Meta quality rating and messaging-tier metadata — and read the failing message's provider_error detail at GET /v1/messages/{message_id}. A 131026failure originates inside Meta's delivery network; the send left Tyxter correctly, so there is no Tyxter-side configuration to fix.

Sandbox

In sandbox, delivery is simulated and no real phone is ever contacted, so Meta deliverability codes such as 131026 occur only on production sends. To rehearse failure handling deterministically, use the sandbox scenarios. They stamp the registered codes sandbox_delivery_failure, sandbox_recipient_opted_out, and sandbox_template_rejected. These are simulated terminal outcomes, carry no provider_error, and appear on the same message read and message.failed webhook surfaces as production delivery codes. See the /sandbox guide.