This page describes the inbound (MO) messages and notifications you can receive from WhatsApp users through the CM.com Messaging API. Every inbound WhatsApp event is delivered to your MO webhook as an HTTP request with a JSON body. Use this page to identify each event type, understand its payload and decide how your application should respond.
For the generic behaviour of inbound messages across all channels, see Incoming messages.
Before you begin
Make sure that:
- A WhatsApp Business number is connected to your CM.com account.
- An MO webhook is configured for that number, and your endpoint responds with a
2xxstatus code. - The
messageContextfield is enabled on your MO webhook. You need this field to correlate replies with the original message. If your payloads do not containmessageContext, contact CM.com Support to have it enabled.
Meta regularly extends the WhatsApp platform. Parse payloads tolerantly: ignore fields you do not recognise and do not fail on null values, so that new attributes do not break your integration.
Payload structure
All inbound WhatsApp events share the same envelope. Channel-specific data is placed in message.custom.
| Field | Type | Description |
|---|---|---|
reference | string | Unique identifier of the inbound message. For WhatsApp messages this is the message ID assigned by Meta (wamid.…). Store it if you need to correlate replies, deletions or errors. |
messageContext | string | Reference of the message the user replied to. Empty when the message is not a reply. See Replies. |
from.number | string | Phone number of the WhatsApp user, or their Business-scoped User ID if the phone number is not shared. |
from.name | string | WhatsApp profile name of the user. |
from.whatsapp | object | The user's user_id (Business-scoped User ID), and where applicable parent_user_id and username. See Business-scoped User IDs. |
to.number | string | Your WhatsApp Business number that received the message. |
message.text | string | Text content of the message. For button and list replies, this contains the title of the selected option. Empty for media messages: a caption is delivered in message.media.title. |
message.media | object | Media details (mediaUri, contentType, title). Populated for media messages only. See Media object. |
message.custom | object | WhatsApp-specific data. The contents depend on the event type; see Event types. |
message.custom.meta_received_time | string | Timestamp at which Meta received the message. |
message.custom.message_type | string | Explicit event type, where provided (for example document, location, contacts or request_welcome). |
message.error | string | Error description supplied by Meta. Empty unless the message (partially) failed. See Error notifications. |
groupings | array | Grouping values associated with the message. |
time | string | Time at which CM.com received the message, in local time (Europe/Amsterdam). |
timeUtc | string | Time at which CM.com received the message, in UTC. |
channel | string | Always WhatsApp for the events on this page. |
Event types
Use the table below to determine which kind of event you have received. Check for the indicators in the order listed; the first match identifies the event.
| Event | How to identify it |
|---|---|
| Error notification | message.error is not empty |
| Deleted message | message.custom.deletedMessage is present |
| Location message | message.custom.message_type is location |
| Contact message | message.custom.message_type is contacts |
| Welcome request | message.custom.message_type is request_welcome |
| BSUID change | message.custom.message_type is system |
| Interactive template reply | message.custom.button is present |
| Interactive message reply | message.custom.interactive.type is list_reply or button_reply |
| Flows reply | message.custom.interactive.type is nfm_reply |
| Call permission reply | message.custom.interactive.type is call_permission_reply |
| Product order | message.custom.order is present |
| Ad referral | message.referral is present |
| Marketing preference update | See Marketing preference updates |
| Media message | message.media.mediaUri is not empty |
| Text message | None of the above; message.text contains the message |
Any message can also be a reply to an earlier message. Check messageContext independently of the event type.
Messages from users
Media messages
Inbound media messages follow the same support and restrictions as outbound media. See Media messages for supported formats and size limits.
WhatsApp-specific behaviour:
| WhatsApp content | Received as |
|---|---|
| GIF | Video file with content type video/mp4 |
| Sticker | Media message with content type image/webp |
The media file itself is not included in the webhook. Download it from the URL in message.media.mediaUri.
Media object
| Field | Type | Description |
|---|---|---|
mediaUri | string | URL from which you can download the file. |
contentType | string | MIME type of the file, for example image/jpeg or application/pdf. |
title | string | The caption the user typed with the media. Empty if the user did not add a caption. |
media.title, not in message.textWhen a user sends a file with a caption, the caption is delivered in message.media.title and message.text is empty. media.title does not contain the original file name. Use contentType to determine the file type, and generate your own file name when you store the file.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUwxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "https://cdn-3.messaging.cm.com/fileproxy/files/39d2aa694f0f474cb1c464e57f26229e",
"contentType": "application/pdf",
"title": "Here is the invoice you asked for."
},
"custom": {
"meta_received_time": "2026-09-29T06:08:25",
"message_type": "document"
}
},
"groupings": ["", "", ""],
"timeUtc": "2026-09-29T06:08:26",
"channel": "WhatsApp"
}
Delete media
After you have downloaded and stored a media file, you can delete it from CM.com's servers by sending a DELETE request to the exact URL from which you retrieved it (message.media.mediaUri).
Media is hosted on the following server:
cdn-eu1.messaging.cm.com
Request
DELETE {mediaUri} HTTP/1.1
Host: cdn-eu1.messaging.cm.com
Response
A successful request returns status code 204 with an empty body. No further action is required.
HTTP/1.1 204 No Content
A deleted media file cannot be recovered. Make sure you have stored the file before you delete it.
Location messages
Users can share a location with your business. The location is provided in message.custom.location.
Only static locations are delivered. Live location updates shared by the user are not forwarded.
| Field | Type | Required | Description |
|---|---|---|---|
latitude | number | Yes | Latitude of the location. Can be negative. |
longitude | number | Yes | Longitude of the location. Can be negative. |
label | string | No | Name or description of the location. |
searchQuery | string | No | Address or search query related to the location. |
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUQxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"location": {
"latitude": 51.603802,
"longitude": 4.770821,
"label": "CM HQ",
"searchQuery": "Konijnenberg 30"
},
"meta_received_time": "2024-02-21T11:01:34",
"message_type": "location"
}
},
"groupings": ["", "", ""],
"timeUtc": "2024-02-21T11:01:35",
"channel": "WhatsApp"
}
Contact messages
Users can share one or more contact cards in a single message. The cards are provided as an array in message.custom.contacts.
The structure follows WhatsApp's contact format. If you are familiar with the vCard (VCF) format or the address book formats used by Android or iOS, the structure will look familiar.
| Field | Type | Required | Description |
|---|---|---|---|
name | object | Yes | Name of the contact (formatted_name, first_name, last_name, middle_name, prefix, suffix). |
phones | array | No | Phone numbers, each with phone, type and, if the number is on WhatsApp, wa_id. |
emails | array | No | Email addresses of the contact. |
addresses | array | No | Postal addresses (street, city, zip, country, country_code, type). |
org | object | No | Organisation details (company, department, title). |
urls | array | No | Websites of the contact. |
birthday | string | No | Birthday of the contact. |
vcard | string | No | The complete contact card as a Base64-encoded vCard. |
origin | string | No | Origin of the contact card. contact_request when the user shared their own number through a request contact info button. |
Optional fields that the user did not fill in are returned as null.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUQxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"contacts": [
{
"vcard": "QkVHSU46VkNBUkQKVkVSU0lPTjozLjAKTjpjb207Q007OzsKRk46Q00uY29tCk9SRzpDTS5jb20gRGVtbyAyClRJVExFOgpURUw7dHlwZT1Nb2JpbGU7d2FpZD0zMTc2MjAxMTU3MDorMzEgNzYgMjAxIDE1NzAKWC1XQS1CSVotTkFNRTpDTS5jb20KRU5EOlZDQVJE",
"origin": "other",
"addresses": [
{
"city": "Breda",
"country": "Netherlands",
"country_code": "NL",
"street": "Konijnenberg 30",
"type": "WORK",
"zip": "4825 BD"
}
],
"birthday": null,
"emails": null,
"name": {
"formatted_name": "CM.com",
"first_name": "CM",
"last_name": "com",
"middle_name": null,
"suffix": null,
"prefix": null
},
"org": {
"company": "CM.com",
"department": null,
"title": null
},
"phones": [
{
"phone": "+31 76 201 1570",
"type": "MOBILE",
"wa_id": "31762011570"
}
],
"urls": null
}
],
"meta_received_time": "2026-07-23T09:01:34",
"message_type": "contacts"
}
},
"groupings": ["", "", ""],
"timeUtc": "2026-07-23T09:01:35",
"channel": "WhatsApp"
}
Replies
On WhatsApp, a user can reply to a specific message. This works both for messages you sent (MT) and for messages the user sent earlier (MO). In both cases, the messageContext field of the inbound message tells you which message was replied to.
| User replies to… | messageContext contains… |
|---|---|
| A message you sent (MT) | The reference you set when sending that MT message. |
| One of their own messages (MO) | The reference (wamid.…) of that earlier MO message. |
| Nothing (not a reply) | An empty string. |
Without the messageContext field on your MO webhook you cannot tell which message was replied to. See Before you begin.
Replies to your messages (MT)
To recognise replies to a message you sent, set a reference on the outbound message.
{
"messages": {
"authentication": {
"productToken": "YOUR_PRODUCT_TOKEN"
},
"msg": [
{
"from": "CM.com",
"to": [
{ "number": "0031612345678" }
],
"allowedChannels": ["WhatsApp"],
"reference": "webinar-invite-2026-10",
"body": {
"type": "auto",
"content": "Reply to this message if you want to receive an invitation to our next webinar."
}
}
]
}
}
When the user replies to that message, messageContext contains the reference you set:
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUIxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "webinar-invite-2026-10",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "I would like an invitation to the webinar.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {}
},
"groupings": ["", "", ""],
"timeUtc": "2026-10-05T08:32:33",
"channel": "WhatsApp"
}
If you reuse the same reference for multiple messages, you cannot tell which of them the user replied to. Use a unique reference per message where you need reply tracking.
Replies to the user's own messages (MO)
To recognise replies to earlier inbound messages, store the reference of every inbound message you receive. For WhatsApp, this is the message ID assigned by Meta (wamid.…).
The user first sends a message:
{
"reference": "wamid.HBgLMTY0NjcwNDM1OTUVAgARGBI1RjQyNUE3NEYxMzAzMzQ5MkEA",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "I would like to reserve a table for four, please.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {}
},
"groupings": ["", "", ""],
"timeUtc": "2026-10-05T08:32:33",
"channel": "WhatsApp"
}
The user then replies to that message. The new message has its own reference, and messageContext contains the reference of the original message:
{
"reference": "wamid.HBgLMzQ2ODUxMjA0NzMVAgASGBYzRUIwOEU5NzhENEIzNkI5MDE2QkE3AA",
"messageContext": "wamid.HBgLMTY0NjcwNDM1OTUVAgARGBI1RjQyNUE3NEYxMzAzMzQ5MkEA",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Scratch that, a friend can't make it. Could you make it a table for three, please?",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {}
},
"groupings": ["", "", ""],
"timeUtc": "2026-10-05T08:35:12",
"channel": "WhatsApp"
}
Replying to a user's message
You can also send a message as a reply to a specific message from the user. The user then sees your message quoted against their original message in WhatsApp.
Take the reference of the inbound message:
{
"reference": "wamid.HBgLMzQ2ODUxMjA0NzMVAgASGBYzRUIwOEU5NzhENEIzNkI5MDE2QkE3AA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "I would like an invitation to the webinar.",
"custom": {},
"error": ""
},
"groupings": [],
"time": "2024-09-17 11:37:31",
"timeUtc": "2024-09-17T09:37:31",
"channel": "WhatsApp"
}
Then pass it as context.message_id in the richContent.conversation of your outbound message:
{
"messages": {
"authentication": {
"productToken": "YOUR_PRODUCT_TOKEN"
},
"msg": [
{
"from": "CM.com",
"to": [
{ "number": "0031612345678" }
],
"allowedChannels": ["WhatsApp"],
"reference": "webinar-invite-confirmation",
"body": {
"type": "auto",
"content": "Fallback text"
},
"richContent": {
"conversation": [
{
"text": "Great! We will send you the invitation soon. Thank you for your interest in our webinar.",
"context": {
"message_id": "wamid.HBgLMzQ2ODUxMjA0NzMVAgASGBYzRUIwOEU5NzhENEIzNkI5MDE2QkE3AA=="
}
}
]
}
}
]
}
}
Interactive responses
Interactive template replies
When a user taps a button in an interactive template, the inbound message contains a message.custom.button object that identifies the button.
| Field | Type | Description |
|---|---|---|
label | string | Text of the button the user tapped. |
payload | string | The payload you assigned to this button when sending the template. Assign a unique payload per button to identify it reliably. |
For quick_reply buttons, the button text is also included in message.text.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUMxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Yes, this is OK",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"button": {
"label": "Yes, this is OK",
"payload": "aGlzIHRoaXMgaXMgY29vZHNhc2phZHdpcXdlMGZoIGFTIEZISUQgV1FEV0RT"
},
"meta_received_time": "2024-02-21T11:01:34"
}
},
"groupings": ["", "", ""],
"timeUtc": "2024-02-21T11:01:35",
"channel": "WhatsApp"
}
Interactive message replies
Interactive messages are sent within the customer service window and are different from interactive templates. For how to send them, see Interactive messages.
The reply is provided in message.custom.interactive. The type field tells you which kind of interaction took place, and the title of the selected row or button is also included in message.text.
type | Triggered when… | Object | Fields |
|---|---|---|---|
list_reply | The user selects a row in a list | list_reply | id, title, description |
button_reply | The user taps a reply button | button_reply | id, title |
The id is the postback ID you assigned to the row or button when sending the message. Use it, rather than the title, to identify the user's choice.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUQxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "list-row-title",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "your-unique-postback-id",
"title": "list-row-title",
"description": "list-row-description"
},
"button_reply": null
},
"meta_received_time": "2024-02-21T11:01:34"
}
},
"groupings": ["", "", ""],
"timeUtc": "2024-02-21T11:01:35",
"channel": "WhatsApp"
}
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUQxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "button-title",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "button_reply",
"list_reply": null,
"button_reply": {
"id": "your-unique-postback-id",
"title": "button-title"
}
},
"meta_received_time": "2024-02-21T11:01:34"
}
},
"groupings": ["", "", ""],
"timeUtc": "2024-02-21T11:01:35",
"channel": "WhatsApp"
}
Flows replies
When a user completes a WhatsApp Flow, you receive an interactive message of type nfm_reply. The submitted data is in message.custom.interactive.nfm_reply.response_json.
| Field | Description |
|---|---|
nfm_reply.name | Always flow. |
response_json.flow_token | The flow_token you sent with the original Flows message. Use it to correlate the response. |
response_json.* | Every other property contains data entered by the user. The property names are those defined in your Flow, so they differ per Flow. |
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUUxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "nfm_reply",
"list_reply": null,
"button_reply": null,
"nfm_reply": {
"name": "flow",
"response_json": {
"flow_token": "your-flow-token",
"entered-field-1": "entered-value-1",
"entered-field-2": "entered-value-2"
}
}
},
"meta_received_time": "2024-02-21T11:01:34"
},
"error": ""
},
"groupings": ["", "", ""],
"timeUtc": "2024-02-21T11:01:35",
"channel": "WhatsApp"
}
Call permission replies
You receive a call permission reply when a user grants, refuses or loses permission for your business to call them. This happens when:
- The user accepts or rejects a call permission request you sent.
- The user calls your business, which grants permission automatically.
- Four consecutive business-initiated calls go unanswered, which revokes permission automatically.
The details are in message.custom.interactive.call_permission_reply.
| Field | Type | Description |
|---|---|---|
response | string | accept or reject. |
is_permanent | boolean | true if the permission does not expire. |
expiration_timestamp | string | null | For temporary permissions, the moment the permission expires. null otherwise. |
response_source | string | What triggered the update, for example user_action. |
No webhook is sent when a temporary permission expires. Use expiration_timestamp to track expiry in your own system.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUYxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Received call permission reply.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "call_permission_reply",
"list_reply": null,
"button_reply": null,
"nfm_reply": null,
"payment_method": null,
"call_permission_reply": {
"response": "accept",
"expiration_timestamp": null,
"is_permanent": true,
"response_source": "user_action"
}
},
"meta_received_time": "2026-08-04T12:58:43"
},
"error": ""
},
"groupings": ["", "", ""],
"timeUtc": "2026-08-04T12:58:44",
"channel": "WhatsApp"
}
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUYxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Received call permission reply.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "call_permission_reply",
"list_reply": null,
"button_reply": null,
"nfm_reply": null,
"payment_method": null,
"call_permission_reply": {
"response": "accept",
"expiration_timestamp": "2026-08-05T12:58:43",
"is_permanent": false,
"response_source": "user_action"
}
},
"meta_received_time": "2026-08-04T12:58:43"
},
"error": ""
},
"groupings": ["", "", ""],
"timeUtc": "2026-08-04T12:58:44",
"channel": "WhatsApp"
}
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUYxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Received call permission reply.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"interactive": {
"type": "call_permission_reply",
"list_reply": null,
"button_reply": null,
"nfm_reply": null,
"payment_method": null,
"call_permission_reply": {
"response": "reject",
"expiration_timestamp": null,
"is_permanent": false,
"response_source": "user_action"
}
},
"meta_received_time": "2026-08-04T12:58:43"
},
"error": ""
},
"groupings": ["", "", ""],
"timeUtc": "2026-08-04T12:58:44",
"channel": "WhatsApp"
}
Product orders
When a user sends you their shopping cart from a product message, you receive an order. You can use it to confirm the order and, for example, send the user a payment link. For how to send product messages, see Product messages.
The order is provided in message.custom.order.
| Field | Type | Description |
|---|---|---|
catalog_id | string | ID of the catalogue the products belong to. |
product_items | array | The products in the cart. |
product_items[].product_retailer_id | string | Your retailer ID of the product. |
product_items[].quantity | string | Quantity ordered. |
product_items[].item_price | string | Unit price of the product. |
product_items[].currency | string | ISO 4217 currency code of the price. |
text | string | Text the user added to the order, or null. |
quantity and item_price are delivered as strings. Convert them before performing calculations, and always validate prices against your own catalogue.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUcxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"order": {
"catalog_id": "123456789012345",
"product_items": [
{
"product_retailer_id": "cmcomdevelop-frisbee",
"quantity": "2",
"item_price": "15",
"currency": "EUR"
},
{
"product_retailer_id": "cmcomdevelop-coaster",
"quantity": "6",
"item_price": "7",
"currency": "EUR"
},
{
"product_retailer_id": "cmcomdevelop-mousepad",
"quantity": "1",
"item_price": "9.99",
"currency": "EUR"
}
],
"text": null
},
"meta_received_time": "2021-11-08T12:19:48"
}
},
"groupings": ["", "", ""],
"time": "2021-11-08 13:19:49",
"timeUtc": "2021-11-08T12:19:49",
"channel": "WhatsApp"
}
Notifications
Deleted messages
When a user deletes a message they sent you, you receive a notification. message.custom.deletedMessage contains the reference of the deleted message, as received in an earlier webhook.
When you receive this notification, remove the referenced message from your systems.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUgxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"deletedMessage": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUQxMjM0NTY3ODkwQUJDREVGAA==",
"meta_received_time": "2021-11-08T12:19:48"
}
},
"groupings": ["", "", ""],
"time": "2021-11-08 13:19:49",
"timeUtc": "2021-11-08T12:19:49",
"channel": "WhatsApp"
}
Error notifications
When Meta cannot (fully) deliver a message from a user, you still receive a notification so you know the user tried to contact you. The reason is provided by Meta in message.error, for example Message type is not currently supported.
Consider responding to the user, for example by asking them to send their message in a different format.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUkxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"meta_received_time": "2022-11-08T12:19:48"
},
"error": "Message type is not currently supported"
},
"groupings": ["", "", ""],
"time": "2022-11-08 13:19:49",
"timeUtc": "2022-11-08T12:19:49",
"channel": "WhatsApp"
}
Welcome requests
When this feature is activated, you receive a notification the first time a user opens a chat with your business, before they have sent a message. Use it to greet the user with a personalised welcome message, which is particularly useful for customer service and account management.
The notification is sent only if no message thread exists yet between the user and your business number. It is identified by message.custom.message_type with the value request_welcome.
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUoxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "User request welcome message",
"custom": {
"meta_received_time": "2022-11-08T12:19:48",
"message_type": "request_welcome"
}
},
"groupings": ["", "", ""],
"time": "2022-11-08 13:19:49",
"timeUtc": "2022-11-08T12:19:49",
"channel": "WhatsApp"
}
Marketing preference updates
You receive a notification when a user changes their preference for receiving marketing messages from your business:
- The user stops marketing messages (opt-out).
- The user resumes marketing messages (opt-in).
The change is described in message.text.
When a user opts out, update your contact records and stop sending them marketing templates until they opt back in.
{
"reference": "48b65fde-f73a-4c77-b600-a581807931ed",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "User requested to resume marketing messages",
"custom": {
"meta_received_time": "2025-12-05T11:00:34"
},
"error": ""
},
"time": "2025-12-05 12:00:35",
"timeUtc": "2025-12-05T11:00:35",
"channel": "WhatsApp"
}
Ad referrals (Click-to-WhatsApp)
When a user starts a conversation by clicking an ad or post with a Click-to-WhatsApp, Click-to-Facebook or Click-to-Instagram call to action, the first inbound message contains a message.referral object. It tells you which ad or post the user came from.
Use the click ID (ctwa_clid) to send conversion events back to Meta through the Meta Conversions API. Meta Ads Manager uses these events to measure and optimise your campaigns, for example to retarget users who did not convert.
The Meta Conversions API for WhatsApp is only available to businesses based outside the United Kingdom, the European Union and Japan.
| Field | Type | Description |
|---|---|---|
source_url | string | Meta URL of the ad or post. |
source_id | string | Meta ID of the ad or post. |
source_type | string | ad or post. |
headline | string | Headline of the ad or post. |
body | string | Body text of the ad or post. |
media_type | string | Type of media in the ad or post, if any: image or video. |
image_url | string | URL of the image, if the ad or post contains one. |
ctwa_clid | string | Click ID assigned by Meta. Required when sending conversion events to Meta. |
{
"reference": "wamid.HBgLMzE2MTIzNDU2NzgVAgASGBQzQUsxMjM0NTY3ODkwQUJDREVGAA==",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "John Doe"
},
"to": {
"number": "0031850123456"
},
"message": {
"text": "Hi, I'd like to know more about your offer.",
"custom": {
"meta_received_time": "2025-06-30T10:23:34"
},
"referral": {
"source_url": "https://fb.me/xxxxxxxxx",
"source_id": "120200000000000000",
"source_type": "ad",
"headline": "Summer sale",
"body": "Chat with us on WhatsApp for 20% off.",
"media_type": "image",
"image_url": "https://scontent.xx.fbcdn.net/xxxxxxxx.jpg",
"ctwa_clid": "AfezXXXXlzjkdgEtWs6Coke3jFaNpq7eUgd4H76BmKbOZFw31hIHf-7JEPkRjs_kUVIF0CMzfBkVux_XXX2ZzVPPja_GuHXXXXi9Nt0l3r5d4BZPwNQJpAiQNJ2E32nOcVDA"
}
},
"groupings": ["", "", ""],
"time": "2025-06-30 12:23:34",
"timeUtc": "2025-06-30T10:23:34",
"channel": "WhatsApp"
}