1.3 How a message gets priced

The price of a message is assigned after delivery, by the status webhook, and not at the moment you send it.

6 min

The core idea

When you call the send API, you don't yet know what the message will cost. Meta assigns the price later, on the delivery status webhook.

One message, end to end

Vera Market sends an order-shipped template to a customer in Mexico.

TimeWhat happensWhat you know about the cost
09:14:07The send call returns a message IDNothing
09:14:08Meta delivers the messageNothing
09:14:09A status webhook arrives with a pricing objectThe charge

Two seconds, and for most messages that's the whole story. But the price only appears in the third row. Anything you logged at 09:14:07 has no cost on it.

Sometimes the gap is much longer, and occasionally the third row never arrives at all.

Reading the pricing object

"pricing": {
  "billable": true,
  "pricing_model": "PMP",
  "type": "regular",
  "category": "utility"
}

Three fields matter, and one of them is on the way out.

type

This is the authoritative field. regular means the message is billable. There are also explicitly free types:

  • free_customer_service: a service message inside the window. Free today; this is the type that October 2026 retires.
  • free_entry_point: a conversation that began from a qualifying entry point, such as a click-to-WhatsApp ad.

category

Which of the categories from Chapter 1.1 applies. Combined with the recipient's country, this selects the rate.

billable

A legacy boolean that Meta is deprecating. Where type is present, type wins. Don't build new logic on billable. Read it only as a fallback for payloads that predate type.

Why this matters in practice

Your send log isn't your bill. Counting API calls tells you how many messages you sent, not what they cost. Any cost report has to be built from status webhooks. If you drop status webhooks, you lose billing data, not just delivery data.

If you bill through Kapso

Kapso builds that report from status webhooks already, and breaks the spend down by category, so you don't have to write it.

What it can't do is reach into your systems. If you attribute cost per customer, per campaign or per conversation in your own database, that part is still yours, and it still has to be keyed on the status webhook rather than the send.

The rate is the one in effect when the status arrives, not when you sent. For a message that sends at 23:58 and gets its status at 00:04, that distinction is normally invisible, but on a day when rates change it's the boundary that decides which rate applies.

Late statuses leave messages unpriced. A message with no status webhook has been delivered but has no price attached to it. Any system that tracks WhatsApp spend needs a policy for these, not just a happy path.

A missing pricing object means unknown, not free. Treating unknown as free is how you end up under-forecasting for a month and then getting a correction.

What to build on

Before the status webhook arrives, the cost of a message is unknown. Cost reporting built on status webhooks reconciles with Meta's invoice. Cost reporting built on send calls doesn't.