entry[0].changes[0].value — but the inner shape depends on what the user sent. This page is the complete reference for every incoming message type, with field descriptions for each.
Envelope Structure
Every incoming message arrives wrapped in this envelope:{
"object": "whatsapp_business_account",
"entry": [{
"id": "WABA_ID_123456",
"changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "905321234567",
"phone_number_id": "PHONE_NUMBER_ID_123"
},
"contacts": [{
"profile": { "name": "John Smith" },
"wa_id": "905399876543"
}],
"messages": [{ "...message object..." }]
}
}]
}]
}
| Field | Type | Description |
|---|---|---|
entry[0].id | string | Your WhatsApp Business Account (WABA) ID |
value.metadata.phone_number_id | string | Meta ID of the phone number that received the message |
value.metadata.display_phone_number | string | Human-readable phone number |
value.contacts[0].wa_id | string | Sender’s WhatsApp phone number |
value.contacts[0].profile.name | string | Sender’s WhatsApp display name |
value.messages[0] | object | The message object — structure varies by type |
Common Message Fields
Every message object, regardless of type, contains:| Field | Type | Description |
|---|---|---|
from | string | Sender’s phone number (same as contacts[0].wa_id) |
id | string | Unique incoming message ID (wamid) |
timestamp | string | Unix timestamp in seconds |
type | string | Message type — see sections below |
Message Types
Text Message
Text Message
Received when a user sends a plain text message.
{
"from": "905399876543",
"id": "wamid.HBgNOTA1Mzk5ODc2NTQzFQIAERgSM0Y3RDg5NjREOUNBMkFFNEE3AA==",
"timestamp": "1711900000",
"type": "text",
"text": {
"body": "Hello, I'd like to know the status of my order."
}
}
| Field | Type | Description |
|---|---|---|
text.body | string | The text content of the message |
Image Message
Image Message
Received when a user sends a photo. The image content must be downloaded via the Media API using
image.id.{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900050",
"type": "image",
"image": {
"caption": "Here is the damaged item",
"mime_type": "image/jpeg",
"sha256": "abc123...",
"id": "MEDIA_ID_789"
}
}
| Field | Type | Description |
|---|---|---|
image.id | string | Media ID — use this to download the image via the Media API |
image.mime_type | string | image/jpeg or image/png |
image.sha256 | string | SHA-256 hash for integrity verification |
image.caption | string | Caption written by the user (optional) |
See the Media page for how to download the file using
image.id.Audio Message
Audio Message
Received when a user sends an audio file or records a voice note.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900100",
"type": "audio",
"audio": {
"mime_type": "audio/ogg; codecs=opus",
"sha256": "def456...",
"id": "MEDIA_ID_456",
"voice": true
}
}
| Field | Type | Description |
|---|---|---|
audio.id | string | Media ID for downloading the audio |
audio.voice | boolean | true = user recorded a voice note; false = file upload |
audio.mime_type | string | audio/ogg; codecs=opus, audio/mp4, audio/mpeg, etc. |
audio.sha256 | string | SHA-256 hash for integrity verification |
Document Message
Document Message
Received when a user sends a PDF, Word document, or other file.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900150",
"type": "document",
"document": {
"caption": "Invoice",
"filename": "invoice_march_2025.pdf",
"mime_type": "application/pdf",
"sha256": "ghi789...",
"id": "MEDIA_ID_321"
}
}
| Field | Type | Description |
|---|---|---|
document.id | string | Media ID for downloading the document |
document.filename | string | Original filename as uploaded by the user |
document.mime_type | string | MIME type of the file |
document.caption | string | Caption written by the user (optional) |
Video Message
Video Message
Received when a user sends a video file.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900200",
"type": "video",
"video": {
"caption": "Screen recording of the issue",
"mime_type": "video/mp4",
"sha256": "jkl012...",
"id": "MEDIA_ID_654"
}
}
| Field | Type | Description |
|---|---|---|
video.id | string | Media ID for downloading the video |
video.mime_type | string | video/mp4 or video/3gp |
video.caption | string | Caption written by the user (optional) |
Sticker Message
Sticker Message
Received when a user sends a WhatsApp sticker.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900250",
"type": "sticker",
"sticker": {
"mime_type": "image/webp",
"sha256": "mno345...",
"id": "MEDIA_ID_987",
"animated": false
}
}
| Field | Type | Description |
|---|---|---|
sticker.id | string | Media ID for downloading the sticker |
sticker.animated | boolean | true = animated sticker |
sticker.mime_type | string | Always image/webp |
Location Message
Location Message
Received when a user shares their location.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900300",
"type": "location",
"location": {
"latitude": 41.0082376,
"longitude": 28.9783589,
"name": "Taksim Square",
"address": "Taksim, Istanbul"
}
}
| Field | Type | Description |
|---|---|---|
location.latitude | number | Latitude coordinate |
location.longitude | number | Longitude coordinate |
location.name | string | Place name (optional, set by user) |
location.address | string | Full address (optional) |
Contact Message
Contact Message
Received when a user shares a contact card.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900350",
"type": "contacts",
"contacts": [{
"name": {
"formatted_name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe"
},
"phones": [
{ "phone": "+905321234567", "type": "CELL" }
],
"emails": [
{ "email": "jane@example.com", "type": "WORK" }
]
}]
}
| Field | Type | Description |
|---|---|---|
contacts[].name.formatted_name | string | Full name of the shared contact |
contacts[].phones[].phone | string | Phone number |
contacts[].emails[].email | string | Email address |
Interactive — Button Reply
Interactive — Button Reply
Received when a user taps one of the quick-reply buttons you sent.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900400",
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "home_delivery",
"title": "Home Delivery"
}
}
}
| Field | Type | Description |
|---|---|---|
interactive.button_reply.id | string | The reply.id you set when sending the button message |
interactive.button_reply.title | string | The button label shown to the user |
Interactive — List Reply
Interactive — List Reply
Received when a user selects a row from the list message you sent.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900450",
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "track_order",
"title": "Track Order",
"description": "Find out where your order is"
}
}
}
| Field | Type | Description |
|---|---|---|
list_reply.id | string | The row id you set in the list sections |
list_reply.title | string | The row title |
list_reply.description | string | The row description (if set) |
Reaction Message
Reaction Message
Received when a user reacts to one of your messages with an emoji.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900500",
"type": "reaction",
"reaction": {
"message_id": "wamid.HBgNOTA1Mzk5ODc2NTQzFQIAERgSM0Y3RDg5NjREOUNBMkFFNEE3AA==",
"emoji": "👍"
}
}
| Field | Type | Description |
|---|---|---|
reaction.message_id | string | The wamid of the message that was reacted to |
reaction.emoji | string | The emoji character (empty string = reaction removed) |
Order Message
Order Message
Received when a user places an order through a WhatsApp catalog.
{
"from": "905399876543",
"id": "wamid.xxx",
"timestamp": "1711900550",
"type": "order",
"order": {
"catalog_id": "CATALOG_ID_123",
"text": "Please deliver before 6pm",
"product_items": [
{
"product_retailer_id": "SKU-001",
"quantity": 2,
"item_price": 19.99,
"currency": "USD"
}
]
}
}
| Field | Type | Description |
|---|---|---|
order.catalog_id | string | Meta catalog ID |
order.text | string | Note from the user (optional) |
order.product_items[].product_retailer_id | string | Your product SKU/ID |
order.product_items[].quantity | number | Ordered quantity |
order.product_items[].item_price | number | Unit price at time of order |
order.product_items[].currency | string | ISO 4217 currency code |
Parsing Incoming Messages
function processWebhook(body) {
const changes = body?.entry?.[0]?.changes ?? [];
for (const change of changes) {
const value = change.value;
const contact = value?.contacts?.[0];
for (const msg of value?.messages ?? []) {
const from = msg.from;
const name = contact?.profile?.name ?? 'Unknown';
switch (msg.type) {
case 'text':
console.log(`[TEXT] ${name} (${from}): ${msg.text.body}`);
break;
case 'image':
case 'audio':
case 'document':
case 'video':
case 'sticker':
console.log(`[${msg.type.toUpperCase()}] ${name} sent media: ${msg[msg.type].id}`);
// Download via Media API using msg[msg.type].id
break;
case 'location':
console.log(`[LOCATION] ${name}: ${msg.location.latitude}, ${msg.location.longitude}`);
break;
case 'interactive':
if (msg.interactive.type === 'button_reply') {
console.log(`[BUTTON] ${name} tapped: ${msg.interactive.button_reply.id}`);
} else if (msg.interactive.type === 'list_reply') {
console.log(`[LIST] ${name} selected: ${msg.interactive.list_reply.id}`);
}
break;
case 'reaction':
console.log(`[REACTION] ${name} reacted ${msg.reaction.emoji} to ${msg.reaction.message_id}`);
break;
default:
console.log(`[UNHANDLED] type: ${msg.type}`);
}
}
}
}
def process_webhook(body):
changes = body.get("entry", [{}])[0].get("changes", [])
for change in changes:
value = change.get("value", {})
contact = (value.get("contacts") or [{}])[0]
name = contact.get("profile", {}).get("name", "Unknown")
for msg in value.get("messages", []):
from_num = msg["from"]
msg_type = msg["type"]
if msg_type == "text":
print(f"[TEXT] {name} ({from_num}): {msg['text']['body']}")
elif msg_type in ("image", "audio", "document", "video", "sticker"):
media_id = msg[msg_type]["id"]
print(f"[{msg_type.upper()}] {name} sent media: {media_id}")
# Download via Media API using media_id
elif msg_type == "location":
loc = msg["location"]
print(f"[LOCATION] {name}: {loc['latitude']}, {loc['longitude']}")
elif msg_type == "interactive":
if msg["interactive"]["type"] == "button_reply":
btn_id = msg["interactive"]["button_reply"]["id"]
print(f"[BUTTON] {name} tapped: {btn_id}")
elif msg["interactive"]["type"] == "list_reply":
row_id = msg["interactive"]["list_reply"]["id"]
print(f"[LIST] {name} selected: {row_id}")
elif msg_type == "reaction":
emoji = msg["reaction"]["emoji"]
msg_id = msg["reaction"]["message_id"]
print(f"[REACTION] {name} reacted {emoji} to {msg_id}")
else:
print(f"[UNHANDLED] type: {msg_type}")
Tips
Always handle the
default / else case for unknown type values. Meta regularly introduces new message types (polls, payment events, etc.) and unhandled types will otherwise cause silent failures in your pipeline.For media messages,
image.id, audio.id, etc. are temporary Media IDs that expire after 30 days. Download and store the file on your own infrastructure promptly.The same incoming message may be delivered more than once due to network retries. Use
messages[0].id (the wamid) as an idempotency key in your database before processing.