Skip to main content
Version: Latest

21. Webhook Payload Catalog

Every payload shape below is Meta's own published WhatsApp Cloud API webhook format. For each, this catalog states exactly what WhatsAppWebhookParser extracts today, verified directly against Core/Services/WhatsAppWebhookParser.cs — not assumed from Meta's schema alone. RawPayloadJson (the complete, unmodified webhook JSON) is stored on every parsed message regardless of type, so no data is ever lost — but only the fields called out as "Extracted" below are available as structured WhatsAppWebhookMessageDto properties for your code to read without re-parsing the raw JSON yourself.

Parser mechanism (applies to every example below): the parser has exactly one type-specific branch (text). Every other type falls through to one generic branch that looks for a JSON property named literally after the type string and, if found, reads id/mime_type/sha256 from it. This means media types (whose JSON object genuinely has those three keys) are extracted correctly "by coincidence of shape," while non-media types (location, contacts, button, interactive, reaction, system) are not extracted at all — this is a real, verified characteristic of the current implementation, not a documentation gap.

21.1 Message Received — Text

Extracted: TextBody (the only type-specific field extraction in the parser).

{
"object": "whatsapp_business_account",
"entry": [{
"id": "2091544401399393",
"changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": { "display_phone_number": "966501234567", "phone_number_id": "1088505757690326" },
"contacts": [{ "profile": { "name": "Ahmed Al-Otaibi" }, "wa_id": "966501112222" }],
"messages": [{
"from": "966501112222", "id": "wamid.HBgLOTY2NTAxMTEyMjIyFQIAEhggMTIzNA==",
"timestamp": "1751500000", "type": "text",
"text": { "body": "Hello, I have a question about my order" }
}]
}
}]
}]
}

21.2 Image

Extracted: MediaId, MediaMimeType, MediaSha256 (via the generic media-shape branch).

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500001", "type": "image",
"image": { "id": "1234567890123456", "mime_type": "image/jpeg", "sha256": "a1b2c3...", "caption": "Payment receipt" }
}]

Note: caption exists in Meta's payload but has no dedicated DTO field — it is not extracted.

21.3 Audio

Extracted: MediaId, MediaMimeType, MediaSha256.

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500002", "type": "audio",
"audio": { "id": "1234567890123457", "mime_type": "audio/ogg; codecs=opus", "sha256": "b2c3d4..." }
}]

21.4 Document

Extracted: MediaId, MediaMimeType, MediaSha256.

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500003", "type": "document",
"document": { "id": "1234567890123458", "mime_type": "application/pdf", "sha256": "c3d4e5...", "filename": "contract.pdf" }
}]

Note: filename exists in Meta's payload but has no dedicated DTO field — it is not extracted.

21.5 Video

Extracted: MediaId, MediaMimeType, MediaSha256.

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500004", "type": "video",
"video": { "id": "1234567890123459", "mime_type": "video/mp4", "sha256": "d4e5f6..." }
}]

21.6 Sticker

Extracted: MediaId, MediaMimeType, MediaSha256 (Meta's sticker object happens to share the same id/mime_type/sha256 shape).

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500005", "type": "sticker",
"sticker": { "id": "1234567890123460", "mime_type": "image/webp", "sha256": "e5f6a7...", "animated": false }
}]

21.7 Location

Extracted: nothing beyond MessageId/From/Type/Timestamp/ProfileNameno latitude/longitude capture exists anywhere in the parser or DTO. The full payload survives only in RawPayloadJson.

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500006", "type": "location",
"location": { "latitude": 24.7136, "longitude": 46.6753, "name": "Riyadh Office", "address": "King Fahd Rd, Riyadh" }
}]

21.8 Contacts (a message that IS a shared contact card)

Extracted: nothing beyond the base fields — no contact-card-specific extraction exists. This is a distinct concept from the webhook-level value.contacts array (which is parsed, to resolve ProfileName — see §8.7).

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500007", "type": "contacts",
"contacts": [{ "name": { "formatted_name": "Sara Al-Harbi" }, "phones": [{ "phone": "+966502223333", "type": "CELL" }] }]
}]

21.9 Template Status Update (message_template_status_update)

This is a separate top-level webhook field (sibling of messages), not a message. It is logged onlyLogTemplateStatusChange reads message_template_name and event for a log line and produces no DTO; it is never added to InboundMessages or StatusUpdates and is not persisted anywhere.

{
"object": "whatsapp_business_account",
"entry": [{ "id": "2091544401399393", "changes": [{
"field": "message_template_status_update",
"value": { "message_template_id": 123456, "message_template_name": "send_document_invoice", "message_template_language": "ar", "event": "APPROVED", "reason": null }
}] }]
}

21.10 Message Status — Delivery

Extracted into WhatsAppWebhookStatusDto: MessageId, RecipientId, Status ("delivered"), TimestampUnix.

"statuses": [{
"id": "wamid.HBgLxx", "recipient_id": "966501112222", "status": "delivered",
"timestamp": "1751500010", "conversation": { "id": "conv-abc", "origin": { "type": "business_initiated" } }
}]

21.11 Message Status — Read

"statuses": [{ "id": "wamid.HBgLxx", "recipient_id": "966501112222", "status": "read", "timestamp": "1751500020" }]

21.12 Message Status — Failed

Extracted: Status ("failed"), plus ErrorCode/ErrorMessage from only the first element of errors[]title and error_data.details are present in Meta's payload but have no DTO field and are not extracted; a second or later error in a multi-error array is silently dropped.

"statuses": [{
"id": "wamid.HBgLxx", "recipient_id": "966501112222", "status": "failed", "timestamp": "1751500030",
"errors": [{ "code": 131026, "title": "Message undeliverable", "message": "Recipient phone number not in allowed list", "error_data": { "details": "..." } }]
}]

See §22 Meta Error Reference for what error code 131026 means and how NotificationFailureClassifier handles it.

21.13 Reaction

Extracted: nothing beyond the base fields — Meta's reaction object (message_id, emoji) has no matching DTO field; the emoji and the message it reacted to are recoverable only from RawPayloadJson.

"messages": [{
"from": "966501112222", "id": "wamid.HBgLxx", "timestamp": "1751500040", "type": "reaction",
"reaction": { "message_id": "wamid.HBgLyy", "emoji": "👍" }
}]