MailerSend
WhatsApp API

Webhooks

Receive real-time WhatsApp delivery events and inbound messages with MailerSend WhatsApp webhooks. See all events, payloads and how to verify signatures.

WhatsApp webhooks let you subscribe to real-time notifications about the WhatsApp messages you send, and about the messages your recipients send back to you.

Because WhatsApp messages are sent asynchronously, webhooks are the only way to learn what happened to a message after it was accepted with a 202 response.

WhatsApp webhooks overview

Setup

WhatsApp webhooks are created in your MailerSend account under WhatsApp → Phone numbers → your sender → Webhooks. There are no API endpoints for managing WhatsApp webhooks yet.

Each webhook belongs to a single sender and only receives events for messages sent from that sender, or received by it. You choose which events the webhook subscribes to when you create it.

Every webhook has its own signing secret, which you can reveal in the webhook's details.

Available events

These are all the events you can listen to and send a notification for.

EventDescription
whatsapp.sentFired when your message has been accepted by WhatsApp and sent from our sending servers.
whatsapp.deliveredFired when your message is delivered to the recipient's device.
whatsapp.readFired when the recipient opens your message.
whatsapp.failedFired when your message could not be delivered. The payload includes the WhatsApp error code and description.
whatsapp.inboundFired when someone sends a message to your WhatsApp sender.

Payload example

Our responses contain fat payloads, including the information about the event-related object, so there is no need to make an additional API request.

An example of the whatsapp.delivered event:

{
  "type": "whatsapp.delivered",
  "webhook_id": "7z3m5jgrogdpyo6n",
  "created_at": "2026-08-31T07:24:20.577080Z",
  "data": {
    "id": "62f114f7165fe0d8db0288e2",
    "whatsapp_account_id": "3enl6x27wmrxrl2v",
    "whatsapp_message_id": "67f91abd69f79df391e9d78d",
    "whatsapp_item_id": "67f91abe2202f3705540234a",
    "type": "delivered",
    "template_name": "Order confirmation",
    "template_id": "23zxk54v6gjy6v7m",
    "from": "15550001234",
    "to": "48600000001",
    "sender": {
      "id": "3enl6x27wmrxrl2v",
      "display_name": "Shop Brand",
      "phone_number": "15550001234",
      "username": "shopbrand"
    },
    "meta": {
      "whatsapp_id": "wamid.HBgLMzcwNjMzMjI3OTcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA"
    }
  }
}

Payload fields

FieldTypeDetails
typestringThe event that triggered the webhook.
webhook_idstringThe ID of the webhook that delivered the event.
created_atstringWhen the event was created, in ISO 8601 format.
data.idstringThe ID of the activity record for this event.
data.whatsapp_account_idstringThe MailerSend ID of the sender the message was sent from.
data.whatsapp_message_idstringThe ID returned when the message was sent. One message can have up to 10 recipients.
data.whatsapp_item_idstringThe ID of the message for this single recipient. Use it to follow one recipient through all events.
data.typestringThe status: sent, delivered, read or failed.
data.template_namestringThe name of the template that was sent.
data.template_idstringThe ID of the template that was sent.
data.fromstringThe sender's phone number or username. Display only — it is an empty string for a sender connected with a Meta virtual number.
data.tostringThe recipient's phone number or BSUID.
data.senderobjectThe sender the message was sent from.
data.sender.idstringThe MailerSend sender ID. Same value as data.whatsapp_account_id.
data.sender.display_namestringThe display name shown to recipients.
data.sender.phone_numberstring|nullnull for a sender connected with a Meta virtual number.
data.sender.usernamestring|nullnull if the sender has not claimed a WhatsApp username.
data.metaobject|nullExtra information about the event. Carries whatsapp_id on success and the error details on failure.

Use data.whatsapp_account_id or data.sender.id to group and join events by sender. data.from is display only and can be empty.

Failed messages

The whatsapp.failed event carries the error reported by WhatsApp in data.meta, together with a human-readable title and description so you can act on it without looking the code up.

{
  "type": "whatsapp.failed",
  "webhook_id": "7z3m5jgrogdpyo6n",
  "created_at": "2026-08-31T07:24:20.577080Z",
  "data": {
    "id": "62f114f7165fe0d8db0288e2",
    "whatsapp_account_id": "3enl6x27wmrxrl2v",
    "whatsapp_message_id": "67f91abd69f79df391e9d78d",
    "whatsapp_item_id": "67f91abe2202f3705540234a",
    "type": "failed",
    "template_name": "Order confirmation",
    "template_id": "23zxk54v6gjy6v7m",
    "from": "15550001234",
    "to": "48600000001",
    "sender": {
      "id": "3enl6x27wmrxrl2v",
      "display_name": "Shop Brand",
      "phone_number": "15550001234",
      "username": "shopbrand"
    },
    "meta": {
      "error_code": 131026,
      "error_message": "Message undeliverable",
      "error_title": "Message Undeliverable",
      "error_description": "Unable to deliver the message. Common reasons include the recipient's phone number being invalid, not registered on WhatsApp, or otherwise unable to receive messages."
    }
  }
}
FieldTypeDetails
meta.error_codeintegerThe WhatsApp Cloud API error code.
meta.error_messagestringThe raw message returned by WhatsApp.
meta.error_titlestring|nullA short name for the error.
meta.error_descriptionstring|nullWhat the error means and what to do about it.

See Meta's error code reference for the full list of codes.

Inbound messages

The whatsapp.inbound event is fired when someone sends a message to your WhatsApp sender, including replies to your template messages and button taps.

{
  "type": "whatsapp.inbound",
  "webhook_id": "7z3m5jgrogdpyo6n",
  "created_at": "2026-08-31T07:24:20.577080Z",
  "data": {
    "id": "62f114f7165fe0d8db0288e2",
    "whatsapp_account_id": "3enl6x27wmrxrl2v",
    "whatsapp_inbound_message_id": "62f114f7165fe0d8db0288e2",
    "meta_message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA",
    "from": "48600000001",
    "to": "15550001234",
    "sender": {
      "id": "3enl6x27wmrxrl2v",
      "display_name": "Shop Brand",
      "phone_number": "15550001234",
      "username": "shopbrand"
    },
    "received_at": "2026-08-31T07:24:19.000000Z",
    "type": "text",
    "text": {
      "body": "Hello, I would like to check my order status."
    },
    "context": {
      "message_id": "wamid.HBgLMzcwNjMzMjI3OTcVAgARGBIwREE3N0EwN0EyNjhENjEwOEMA"
    }
  }
}
FieldTypeDetails
data.idstringThe ID of the inbound message.
data.whatsapp_inbound_message_idstringSame value as data.id.
data.meta_message_idstringWhatsApp's own message ID.
data.fromstringThe phone number of the person who sent the message.
data.tostringYour sender's phone number. Empty for a sender connected with a Meta virtual number.
data.received_atstringWhen WhatsApp received the message, in ISO 8601 format.
data.typestringThe kind of message. See the table below.
data.contextobjectPresent when the message is a reply. context.message_id is the WhatsApp ID of the message that was replied to.

The data.type field determines which additional field is present in the payload:

typeAdditional fieldContents
texttextbody with the message text.
image, audio, video, document, stickerattachmentThe downloaded media file. See Media attachments.
locationlocationlatitude, longitude, and name and address when shared.
contactscontactsThe shared contact cards, as sent by WhatsApp.
reactionreactionemoji and the message_id of the message that was reacted to.
buttonbuttontext and payload of the quick reply button that was tapped.
interactivelist_reply or interactiveid, title and description for a list reply, or the raw interactive payload for other subtypes.
order, system, unknown, unsupportedmatching the typeThe raw payload as sent by WhatsApp.

Media attachments

Media in an inbound message is downloaded and stored by MailerSend, and the payload carries a temporary signed URL to it.

{
  "type": "image",
  "attachment": {
    "url": "https://api.mailersend.com/whatsapp/inbound-message-attachment/62f114f7165fe0d8db0288e2?account=3enl6x27wmrxrl2v&expires=1790000000&signature=...",
    "status": "stored",
    "mime_type": "image/jpeg",
    "size": 102400,
    "caption": "Here is the damaged item"
  }
}
FieldTypeDetails
attachment.urlstring|nullA temporary signed URL to the file. null when the download failed.
attachment.statusstringstored when the file is available, failed when it could not be downloaded.
attachment.mime_typestringThe media type reported by WhatsApp.
attachment.sizeintegerThe file size in bytes.
attachment.captionstringThe caption, when the sender added one.
attachment.filenamestringThe original file name, for documents.
attachment.animatedbooleanPresent for stickers only.

Attachments expire

Inbound media is kept for 1 day and the signed URL expires with it. Download the file as soon as you receive the event if you need to keep it.

Security

Webhook requests made by MailerSend include a Signature header. It contains a string generated by hashing the data sent to your webhook endpoint with an individual Signing Secret. A signing secret is a random string that is generated when you create a webhook.

Verifying a signature:

// $signature - a header sent by MailerSend, please refer to your framework
// or PHP manual on how to read the Signature header

// $requestContent - please refer to your framework or PHP manual on how to read the request content

$computedSignature = hash_hmac('sha256', $requestContent, $signingSecret);
return hash_equals($signature, $computedSignature);

Testing webhooks

You can send a test event to your endpoint from the webhook's details in your MailerSend account. Test events use the same structure as real ones and are signed with your webhook's signing secret.

The webhook's log shows every delivery attempt, the response code your endpoint returned, and lets you retry a failed attempt.

Webhook failure handling

If your endpoint is unreachable or returns a non-2xx response, MailerSend automatically retries with exponential backoff for up to about 3 days. While your webhook is failing we'll send escalating email notifications. If the endpoint stays down too long, the webhook is automatically paused. You can re-enable it from your dashboard at any time.

Retrying failed webhooks

When your webhook receives a response other than a 2xx code from your endpoint URL, or if the endpoint doesn't respond within 3 seconds, it will show up as a failed attempt in the log section of your webhook. If it receives a 2xx, then it will show as a success.

If a webhook call fails, MailerSend automatically retries with exponential backoff for up to about 3 days.

Useful tools

Webhook.site or Pipedream.com are useful tools for testing webhooks quickly, seeing how it works, and inspecting what's being sent—without any coding on your side.

On this page