Send a WhatsApp message with one request to POST /v1/messaging/messages, the endpoint every messaging channel shares, with channel set to whatsapp. You need a connected sender, an approved template, and an API key with the whatsapp.send permission.

Compose a message

channelrequiredstring

whatsapp.

fromrequiredstring

The phone number of your connected WhatsApp number, in international format, such as +213555000111.

torequiredstring

The recipient's phone number in international format, with + and the country code, such as +213555123456.

template.idrequiredstring

The Template ID of an approved template of that sender's WhatsApp Business Account, such as wat_….

template.variablesoptionalobject

Values for the template's header and body variables. See Variables.

JSON
{
  "channel": "whatsapp",
  "from": "+213555000111",
  "to": "+213555123456",
  "template": {
    "id": "wat_example",
    "variables": {
      "body": {
        "1": "Amina",
        "2": "A-1234"
      }
    }
  }
}

The template decides the language, the category, and everything the recipient sees except the variable values.

Recipients

Each request sends one message to one recipient. To message several customers, send one request per recipient, each with its own idempotency key.

Write numbers in E.164 format: +, the country code, then the number without spaces, dashes, or a leading 0. 0555 12 34 56 in Algeria becomes +213555123456. The same format applies to from.

The recipient must have a WhatsApp account and must have agreed to receive your messages. WhatsApp doesn't let businesses check in advance whether a number uses WhatsApp: a message to a number without WhatsApp is accepted, then becomes failed.

Idempotency keys

Every API send requires an Idempotency-Key header. It guarantees that one operation sends at most one message, even when your request times out and you retry it.

  • Use a key that identifies the operation in your system, such as order-A-1234-shipped. Up to 200 characters.
  • Retrying with the same key and the same request returns the original message as it is now, without sending again or charging again. Its status may have moved on, for example to delivered.
  • Reusing a key for a different message returns 409 with the code whatsapp_idempotency_key_reused.
  • Keys are scoped to your project.

Store the key with the operation that triggers the message, so a retry after a crash or deploy uses the same key. See Retry safely.

Message statuses

A successful send returns 200 OK with the message, usually with the status accepted. WhatsApp then reports how far the message got.

StatusMeaningFinal
sendingThe message is being handed to WhatsApp.No
acceptedWhatsApp accepted the message. It is not delivered yet.No
sentWhatsApp sent the message to the recipient's phone.No
deliveredThe message reached the recipient's phone.No
readThe recipient opened the message.Yes
failedWhatsApp refused or could not deliver the message. failure_code and failure_reason say why.Yes
uncertainWhatsApp didn't confirm whether it accepted the message. See below.No

A status only moves forward: accepted, sent, delivered, read. Some steps can be skipped; a message can go straight from sent to read. read is reported only for recipients who share read receipts, so many messages stay delivered. A message that failed after it was delivered keeps its delivered status.

Each status has its own timestamp on the message: accepted_at, sent_at, delivered_at, read_at, and failed_at.

When a message is uncertain

If WhatsApp doesn't answer in time, the message may or may not have been sent. Nexiom Connect never resends it on its own, so the recipient never gets it twice. The response is still 200 OK, with the status uncertain.

When WhatsApp later reports a status for the message, it moves on from uncertain, and the matching webhook fires. If it stays uncertain, check with the recipient before sending it again with a new idempotency key: retrying with the same key returns the same uncertain message and sends nothing.

Handle errors

A refused request sends nothing and charges nothing, except where noted.

ResponseCodeWhat to do
400invalid_argument and validation detailsFix the request: channel, field names, the from or to format, or the Idempotency-Key header.
400whatsapp_template_variable_missing, whatsapp_template_variable_unknownSend exactly the template's variables, each with a non-empty value.
403not_allowedUse a key with the whatsapp.send permission. With the code FEATURE_NOT_AVAILABLE, WhatsApp is not turned on for your organization.
404whatsapp_number_not_found, whatsapp_sender_removedCheck the from phone number, and that the number is still connected in this project.
404whatsapp_template_not_foundCheck the Template ID, and that the template belongs to the sender's WhatsApp Business Account.
409whatsapp_idempotency_key_reusedUse a new key for a new message.
422whatsapp_number_not_readyOpen the sender's page: the number is not connected or WhatsApp is limiting it.
422whatsapp_template_not_approved, whatsapp_template_not_supportedWait for approval, or use a template Nexiom Connect can send.
422whatsapp_message_rejectedWhatsApp refused the message. The message is stored as failed with WhatsApp's reason.
422whatsapp_provider_unavailableWhatsApp is busy. Retry later with a new idempotency key; the refused message is stored as failed.
429 and 403Plan and wallet codesYour allowance or wallet needs attention. See Costs.

Every code is listed in the Send a message reference, and WhatsApp's own failure codes in Best practices and FAQ.

Follow your messages

  • API: Retrieve a message by its ID for its current status, cost, and content.
  • Webhooks: subscribe to whatsapp.message.sent, delivered, read, and failed to be notified of each change.
  • Dashboard: Messaging → Logs lists every message with its timeline and content; Messaging → Analytics shows volume and delivery rates by day.

Costs

Two parties charge for a WhatsApp message:

Charged byWhatWhere
Nexiom ConnectEach message you send, against your planYour Nexiom plan, then your Nexiom wallet
MetaWhatsApp's own fee, which depends on the template category and the recipient's countryThe payment method of your WhatsApp Business Account, in WhatsApp Manager

Your plan includes a number of WhatsApp messages each billing period. Beyond them, each message is paid from your wallet at your plan's price per message:

  • An included message counts once WhatsApp accepts it, even if it fails later.
  • A message beyond the included ones reserves its price in your wallet, and is charged only when it is delivered or read. If it fails, or is not delivered within 24 hours, nothing is charged.
  • Owners and admins are notified when 80% and 100% of the included messages are used.

Each message shows what Nexiom Connect charged for it: is_billable, cost as a decimal string such as "0.1000", and cost_currency. cost is "0.0000" for included messages and for messages that were not charged. It never includes Meta's fees.

When a send is refused for your plan or wallet, nothing is sent or charged:

ResponseCodeReturned when
429included_usage_exhaustedYour plan's included messages are used up and the plan has no paid messages.
403paid_usage_disabledThe included messages are used up and paid usage is turned off.
429usage_spend_limit_exceededThe message would go past your spending limit.
429wallet_balance_insufficientYour wallet cannot cover the message. Top it up.
429service_not_in_planYour plan doesn't include WhatsApp messages. Change your plan.

Limits

  • Requests: your organization's API requests per second depend on your plan. A request past it returns 429 with the code api_rate_limit_per_second_exceeded; wait a moment and retry with the same idempotency key.
  • Messaging limit: WhatsApp limits how many different customers your business portfolio can message in 24 hours. Past it, messages become failed. See Messaging limits.
  • Throughput: each number sends up to its throughput, usually 80 messages per second. Spread large sends over time.

Retention

Message logs and their content are kept for 30 days. After that, the message is deleted and Retrieve a message returns 404. Daily totals in Messaging → Analytics are kept beyond 30 days. To keep a longer history, store the message ID and the statuses from webhooks in your own system.

Next steps

Nexiom Connect
Open dashboard