Sending a WhatsApp message
Send template-based WhatsApp messages with the MailerSend WhatsApp API. Learn about senders, recipients, personalization and delivery tracking.
This endpoint allows you to send WhatsApp messages through the MailerSend WhatsApp API.
MailerSend sends WhatsApp messages through the WhatsApp Business Platform (Cloud API). Every business-initiated WhatsApp message must use a template that has been approved by Meta, so the API takes a template and the values for its variables instead of free-form message content.
Before you start
| Requirement | Details |
|---|---|
| WhatsApp add-on | WhatsApp is a paid add-on. Enable it in your MailerSend account before sending. |
| Connected sender | Connect your WhatsApp Business Account (WABA) in WhatsApp → Phone numbers. The sender must stay connected for sending to work. |
| Approved template | Create a template and submit it to Meta. Only templates with the approved status can be sent. Approval takes from a few minutes up to 48 hours. |
| API token | Use a token with the whatsapp_full scope. See token scopes. |
Templates and senders are managed in your MailerSend account. There are no API endpoints for creating senders or templates yet.
Send a WhatsApp message
This endpoint sends an asynchronous WhatsApp message to up to 10 recipients. It returns the ID of the created message in the response body and in the X-Message-Id header.
Send a WhatsApp message using this POST request:
POST https://api.mailersend.com/v1/whatsapp/sendRequest Body
{
"from": "15550001234",
"to": [
"+48600000001",
"+48600000002"
],
"template_id": "23zxk54v6gjy6v7m",
"personalization": [
{
"to": "+48600000001",
"data": {
"header": [
"Order #12345"
],
"body": [
"John",
"December 31, 2026"
],
"buttons": [
"orders/12345"
]
}
},
{
"to": "+48600000002",
"data": {
"header": [
"Order #12346"
],
"body": [
"Jane",
"January 4, 2027"
],
"buttons": [
"orders/12346"
]
}
}
]
}curl --request POST \
--url https://api.mailersend.com/v1/whatsapp/send \
--header 'Authorization: Bearer API_KEY_HERE' \
--header 'Content-Type: application/json' \
--data '{
"from": "15550001234",
"to": ["+48600000001"],
"template_id": "23zxk54v6gjy6v7m",
"personalization": [
{
"to": "+48600000001",
"data": {
"body": ["John", "December 31, 2026"]
}
}
]
}'Request parameters
JSON parameters are provided in dot notation
| JSON parameter | Type | Required | Limitations | Details |
|---|---|---|---|---|
from | string | yes | The sender to send from. Must be one of your connected WhatsApp senders. See Sender identifiers. | |
to | string[] | yes | min:1, max:10 | The recipients of the message. Each recipient must be unique within the request. |
to.* | string | yes | A phone number in E.164 format, or a BSUID taken from an inbound message. WhatsApp usernames cannot be used as recipients. See Recipients. | |
template_id | string | yes | The ID of an approved template that belongs to the sender in from. You can find it in WhatsApp → Templates. | |
personalization | object[] | no | max:10 | The values for the template's variables, per recipient. See Personalization. |
personalization.*.to | string | yes | The recipient the values apply to. Must be one of the values in to. | |
personalization.*.data | object | yes | Object containing the header, body and buttons sections. | |
personalization.*.data.header | string[] | no | Max 60 characters per value | Values for the variables in the template header, in order. |
personalization.*.data.body | string[] | no | Max 1024 characters per value | Values for the variables in the template body, in order. |
personalization.*.data.buttons | string[] | no | Max 2000 characters per value | Values for the variables in the template's URL buttons, in order. |
Sender identifiers
The from field accepts two forms of identifier. Both are scoped to your account and only resolve to a sender that is currently connected. You can find both in WhatsApp → Phone numbers.
| Form | Example | Details |
|---|---|---|
| Phone number | 15550001234 | The sender's phone number in E.164 format. |
| MailerSend sender ID | 3enl6x27wmrxrl2v | Also returned as data.sender.id in every webhook payload. |
A sender connected with a Meta virtual number has no phone number, so it can only be addressed by its MailerSend sender ID.
The MailerSend sender ID is stable across disconnecting and reconnecting a sender, which is not true of the phone number. Use it if you need a durable reference in your integration.
Recipients
Recipients can be addressed in two ways:
- A phone number in E.164 format, for example
+48600000001. Spaces, dashes, brackets, dots and the leading+are removed before the number is validated, so+48 600 000 001and48600000001are the same recipient. - A BSUID (Business-Scoped User ID), for example
US.13491208655302741918. Meta provides it in the payload of an inbound message, and it lets you reply to a user who contacted you without their phone number.
WhatsApp usernames cannot be used as recipients. Meta does not allow looking up a username, so use the recipient's phone number or the BSUID from their inbound message instead.
Personalization
Template variables are filled per recipient. Each entry in personalization targets one recipient from to and carries the values for the template's header, body and buttons sections.
Values are positional: the first value in the array replaces {{1}}, the second replaces {{2}}, and so on. Only variables in URL buttons are personalized, so the buttons array holds the URL parameters in the order the buttons appear in the template.
{
"to": "+48600000001",
"data": {
"header": ["Order #12345"],
"body": ["John", "December 31, 2026"],
"buttons": ["orders/12345"]
}
}The number of values in each section must match the number of variables the template has in that section, for every recipient in to. A mismatch fails the whole request with a 422 response instead of failing per recipient after the message was queued:
{
"message": "The given data was invalid.",
"errors": {
"personalization.0.data.body": [
"The template expects 2 body variable(s) for 48600000001, 1 given."
]
}
}Authentication templates always expect exactly one body variable — the verification code. It is copied into the template's copy code button automatically, so leave buttons empty for them.
Objects with named keys, such as "body": {"name": "John", "date": "December 31, 2026"}, are also accepted. The values are used in the order they appear in the object, so a positional array is the safer choice.
Sandbox
The sandbox lets you test your integration without a connected WABA or an approved template. Activate it in WhatsApp → Sandbox, where you connect your own phone number to the MailerSend sandbox number and get the sandbox templates.
To send a sandbox message, use the sandbox number as the from value and one of the sandbox templates as the template_id. Sandbox sends are limited to 1 recipient per request and 20 messages per day, the sandbox connection expires after 1 hour, and sandbox messages are not counted against your monthly WhatsApp message cap.
Responses
Sending queued
Response Code: 202 Accepted
Response Headers:
content-type: application/json
X-Message-Id: 67f91abd69f79df391e9d78d{
"data": {
"id": "67f91abd69f79df391e9d78d",
"created_at": "2026-08-31 07:24:20"
}
}The message is accepted and queued. Each recipient is processed individually, and the delivery result for each one is reported through webhooks.
Missing permissions
Response Code: 403 Forbidden
Response Headers:
content-type: application/json{
"message": "The custom API token you're using doesn't have the required permissions. #MS40301"
}The token is missing the whatsapp_full scope.
No connected sender
Response Code: 403 Forbidden
Response Headers:
content-type: application/json{
"message": "This action is unauthorized."
}Your account has no connected WhatsApp sender, or the account is suspended.
Validation error
Response Code: 422 Unprocessable Entity
Response Headers:
content-type: application/json{
"message": "The given data was invalid.",
"errors": {
"from": [
"The from does not match any of your connected WhatsApp senders."
],
"template_id": [
"The selected template cannot be used for sending because it is in review. Only approved templates can be sent."
]
}
}See - Validation errors
Message rejected
Response Code: 422 Unprocessable Entity
Response Headers:
content-type: application/json{
"error": "Failed to send WhatsApp message",
"message": "We could not accept this WhatsApp message. Check that the sender is still connected and try again."
}The request was valid but the message could not be accepted, most commonly because the sender was disconnected between your request and the send.
Common validation errors
| Error | Cause |
|---|---|
The from does not match any of your connected WhatsApp senders. | The sender identifier is unknown, or the sender has been disconnected. |
The to.0 must be a valid phone number or BSUID. | A recipient is not a valid E.164 phone number or BSUID. |
The to.0 cannot be a WhatsApp username. | Usernames cannot be used as recipients. Use a phone number or a BSUID. |
Maximum 10 recipients allowed. | Split the request into batches of up to 10 recipients. |
Each recipient can only be listed once per request. | The same recipient appears more than once in to. |
The selected template does not exist. | The template ID is unknown or the template was deleted. |
The selected template cannot be used for sending because it is <status>. | Only approved templates can be sent. |
The selected template belongs to a different WhatsApp sender. | Use a template that was created for the sender in from. |
WhatsApp sending has been paused. You have reached your monthly message cap. | The account has reached the message cap of its WhatsApp add-on for the current billing period. |
Tracking delivery
WhatsApp messages are sent asynchronously, so the 202 response only means the message was accepted. To learn what happened to each recipient's message, create a webhook for your sender and listen for the whatsapp.sent, whatsapp.delivered, whatsapp.read and whatsapp.failed events. Inbound messages from your recipients are delivered as whatsapp.inbound events.
Read more about WhatsApp webhooks.
Data retention
WhatsApp messages, activity and webhook logs are kept for 7 days. Store anything you need for longer in your own system, for example by consuming webhook events.
Inbound routing
Learn how to efficiently create and manage SMS inbound routes with MailerSend's API documentation and the SMS inbounds endpoint.
Webhooks
Receive real-time WhatsApp delivery events and inbound messages with MailerSend WhatsApp webhooks. See all events, payloads and how to verify signatures.