Tracking WhatsApp API Message Statuses: Webhooks, Error Codes, and Costs

·5 min read
Tracking WhatsApp API Message Statuses: Webhooks, Error Codes, and Costs

When you make a POST request to Meta's Cloud API endpoint to send a message, your server receives an immediate HTTP 200 OK response. This response contains a message ID, which usually looks like wamid.HBgLOTIzMDAxMjM0NTY3FQIAERgSQ0RGM0U1RjY3ODkwMTIzNDU2AA==.

Do not celebrate yet.

That HTTP 200 response only confirms that Meta accepted your payload. It does not mean the message actually left Meta's servers. It does not mean the user's phone is switched on, and it certainly does not mean the message landed in their chat app. To know what actually happened to your message, you must listen to Meta's asynchronous webhook events and decode their status codes.

The Financial Impact of Status Transitions

Meta does not charge you per individual message sent. Instead, they charge you per 24-hour conversation window. Understanding exactly when this billing window triggers is critical for managing your budget.

The 24-hour conversation charge is not triggered when you get an HTTP 200 response. It does not even trigger when the message status is marked as sent. It triggers only when the message status transitions to delivered.

Consider this scenario. You send a marketing template to a customer in Lahore. Their phone has been switched off for three days because they are traveling in an area with no mobile coverage. The message stays in the sent status on Meta's servers. You are not billed for this message.

If the phone remains offline for thirty days, the message expires. You never pay a single paisa or paisa-equivalent for it. However, the millisecond that phone connects to a network and the status changes to delivered, Meta triggers the billing event. The 24-hour conversation window opens, and your credit card on file in your Meta Business Suite is charged.

Because of this behavior, monitoring your delivery statuses is the only way to calculate your actual ROI. If you notice a massive gap between sent and delivered statuses in your database, you have a list hygiene problem. You are wasting system resources sending messages to inactive numbers. To understand how these costs are structured without markups, you can read The Truth About WhatsApp API Pricing Without Per-Message Charges.

The Core Statuses: Sent, Delivered, and Read

Every message you send moves through a specific pipeline of status events. Your webhook receiver will get a separate POST request from Meta for each step in this pipeline.

StatusWhat it Actually MeansActionable Insight
sentMeta has processed the message and dispatched it to the cellular/data networks.The message is in transit. The recipient's device is currently offline.
deliveredThe message has been successfully downloaded by the recipient's WhatsApp application.The billing window starts now. The user has received a push notification.
readThe user has opened the chat or viewed the message preview.Engagement is confirmed. Note that users can disable read receipts in their privacy settings.
failedThe message could not be processed or delivered.Check the accompanying error code. Do not retry sending without fixing the root cause.

In Pakistan and India, the time gap between sent and delivered can stretch to hours. Frequent power outages, mobile data suspensions during public holidays, and users turning off secondary SIM cards to conserve battery mean your delivery rate will rarely hit 100%. If a message remains stuck in the sent state for more than 24 hours, it is highly likely the recipient's phone is inactive.

Decoding the Most Common Meta Error Codes

When a message fails, Meta returns a failed status payload containing a specific error code. If you ignore these codes and continue blasting messages to failing numbers, Meta will lower your phone number's quality rating. This can lead to your daily sending limits being restricted.

Code 131026: Message Undeliverable

This is the most common error code you will face. It means the WhatsApp network could not reach the phone. There are three common reasons for this:

  • The phone number is not registered on WhatsApp.
  • The user has blocked your business number.
  • The user's phone has been completely offline for more than 30 days.

Do not immediately retry sending to a number that returns error 131026. Flag this contact in your CRM. If you keep hitting undeliverable numbers, Meta's automated spam filters will flag your account.

Code 131047: Rate Limit Exceeded

Meta limits the number of messages you can send per second and per day. By default, the Cloud API allows 80 messages per second. However, your daily sending limit depends on your phone number tier. New, unverified accounts start at Tier 1, which limits you to 1,000 unique recipients in a rolling 24-hour window. If you exceed this, Meta will reject your API calls with error 131047. To handle these spikes, you need a robust queue system. Learn how to design your database and application architecture to prevent this by reading Why Your WhatsApp API Calls Are Failing: Rate Limits, Queues, and Production-Ready Architecture.

Code 132001: Template Parameter Mismatch

This is a developer error. It occurs when your API payload does not match the approved template structure. For example, if your template has three variables ({{1}}, {{2}}, and {{3}}), and your code only sends values for two of them, the message fails instantly. Always validate your payload array against your template parameters before hitting the Meta API.

How to Build a Reliable Webhook Receiver

You cannot poll Meta's API to ask if a message was delivered. You must set up a webhook receiver that listens for incoming POST requests from Meta. If you are sending messages using PHP, you can see how to structure your initial requests in our guide on how to Send WhatsApp Messages with PHP cURL: A Practical Guide.

Once your messages are out, Meta will send status update payloads to your webhook. Here is what a typical delivered status payload looks like:

{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "1098374657382",
      "changes": [
        {
          "value": {
            "messaging_product": "whatsapp",
            "metadata": {
              "display_phone_number": "923001234567",
              "phone_number_id": "9876543210"
            },
            "statuses": [
              {
                "id": "wamid.HBgLOTIzMDAxMjM0NTY3FQIAERgSQ0RGM0U1RjY3ODkwMTIzNDU2AA==",
                "status": "delivered",
                "timestamp": "1710312345",
                "recipient_id": "923009876543",
                "conversation": {
                  "id": "a1b2c3d4e5f6",
                  "origin": {
                    "type": "utility"
                  }
                },
                "pricing": {
                  "billable": true,
                  "pricing_model": "CBP",
                  "category": "utility"
                }
              }
            ]
          },
          "field": "messages"
        }
      ]
    }
  ]
}

To process this payload reliably, your code must follow two strict rules.

The 3-Second Rule

Your webhook server must respond with an HTTP 200 OK status within 3 seconds of receiving Meta's request. If you do not, Meta assumes your server is down. They will retry sending the same status update payload over and over with an exponential backoff. If your server continues to time out, Meta will automatically disable your webhook subscription.

Do not run slow database queries, send emails, or execute complex business logic inside the script that receives the webhook. Instead, read the raw input, validate the JSON, push the payload to a queue like Redis, RabbitMQ, or a database-backed