Skip to main content

Receiving WhatsApp Events via Webhook

Superfone can forward the WhatsApp events that arrive on your business number — incoming customer messages, delivery/read receipts, and other account events — to an HTTPS endpoint you control. Use this to feed your own systems: an AI agent, a CRM, a ticketing tool, or custom automation.

What this is

This is an outbound webhook. Superfone receives events from Meta on your behalf and relays them to your endpoint. It runs alongside the Superfone dashboard — it does not replace it.

How it works

  1. A customer messages your WhatsApp business number.
  2. Meta delivers the event to Superfone.
  3. Superfone forwards that event to each enabled webhook endpoint configured on your account.

The payload Superfone forwards is Meta's original WhatsApp Cloud API webhook payload, relayed as-is. You therefore code against Meta's standard webhook schema, documented below. For the authoritative field-level reference, see Meta's Webhooks Components reference.

Registering your endpoint

Webhook forwarding is configured per WhatsApp account. Each entry has:

FieldTypeDescription
urlstringYour HTTPS endpoint that will receive the POST requests.
enabledbooleanWhether forwarding to this endpoint is active.
eventsstring[]Event categories (reserved for future use; all enabled events are currently forwarded).

The configuration field that holds your HTTPS endpoint is named url in the system.

How to enable — we register it for you

Just provide your HTTPS endpoint to the Superfone team, and we'll register it on your WhatsApp account so you start receiving the webhook at that endpoint. There is currently no self-serve API to manage webhook endpoints — registration is handled by Superfone.

Requirements for your endpoint:

  • Must be reachable over HTTPS.
  • Must accept an HTTP POST with a JSON body.
  • Must respond 200 quickly to acknowledge receipt.

The request Superfone sends

PropertyValue
MethodPOST
Headerx-app-name: dragonfly — the only header Superfone sets; identifies the sender
BodyMeta's raw WhatsApp webhook payload, sent as JSON (see schema)

x-app-name: dragonfly is the only header Superfone sets. Content-Type is not set deliberately — the body is simply sent as JSON. There is no payload signature (no X-Hub-Signature-256) and no hub.challenge verification handshake against your endpoint — those apply only between Meta and Superfone, not on this hop. Don't treat the request as authenticated; verify it on your side (for example, a secret token in the endpoint path).

Payload schema

Every payload uses Meta's envelope:

object: "whatsapp_business_account"
entry[]
id // your WhatsApp Business Account ID
changes[]
field // "messages" (covers both inbound messages and statuses)
value
messaging_product // "whatsapp"
metadata
display_phone_number // your business number
phone_number_id // your phone number ID
contacts[] // sender profile (present for inbound messages)
profile.name
wa_id // sender's WhatsApp number
messages[] // present for inbound customer messages
statuses[] // present for delivery/read/failed receipts

Inbound text message

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "918888888888",
"phone_number_id": "1234567890"
},
"contacts": [
{ "profile": { "name": "Rahul" }, "wa_id": "919876543210" }
],
"messages": [
{
"from": "919876543210",
"id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAEhgU...",
"timestamp": "1700000000",
"type": "text",
"text": { "body": "Hi, is this available?" }
}
]
}
}
]
}
]
}

Inbound media message

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "918888888888",
"phone_number_id": "1234567890"
},
"contacts": [
{ "profile": { "name": "Rahul" }, "wa_id": "919876543210" }
],
"messages": [
{
"from": "919876543210",
"id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"timestamp": "1700000000",
"type": "image",
"image": {
"id": "<MEDIA_ID>",
"mime_type": "image/jpeg",
"sha256": "<hash>",
"caption": "optional caption"
}
}
]
}
}
]
}
]
}
Media is a media_id, not a downloadable URL

For media messages, the payload contains a Meta media_id, not a downloadable URL. You cannot download this media_id yourself — it requires Superfone's authenticated Meta credentials, and Meta's media links expire. Document, audio, video, and sticker messages use the same shape under document / audio / video / sticker. (Delivering a directly fetchable media URL is on the roadmap.)

Status update (sent / delivered / read / failed)

{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "918888888888",
"phone_number_id": "1234567890"
},
"statuses": [
{
"id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"status": "delivered",
"timestamp": "1700000000",
"recipient_id": "919876543210",
"conversation": {
"id": "b1afb7b7d806c1c32fc190d5ff422331",
"origin": { "type": "service" }
},
"pricing": {
"billable": true,
"category": "service",
"pricing_model": "CBP"
}
}
]
}
}
]
}
]
}

status values are sent, delivered, read, failed, and deleted (the customer deleted their message). The conversation and pricing objects are delivered on most receipts. For failed, Meta includes an errors[] array — see Status object.

Field descriptions

Envelope

FieldTypeDescription
objectstringAlways "whatsapp_business_account". Identifies the payload as a WhatsApp Business Account event.
entry[]object[]One entry per WhatsApp Business Account. May contain multiple entries — always iterate.
entry[].idstringYour WhatsApp Business Account (WABA) ID.
entry[].changes[]object[]The list of changes (events) in this entry. May contain multiple changes — always iterate.
entry[].changes[].fieldstringThe change topic. For messages and status receipts this is "messages".
entry[].changes[].valueobjectThe event payload. Contains metadata, and either messages or statuses.

value object

FieldTypeDescription
messaging_productstringAlways "whatsapp".
metadata.display_phone_numberstringYour business number in display form (e.g. 918888888888).
metadata.phone_number_idstringYour business number's ID. This is the routing key — use it to tell which number the event is for.
contacts[]object[]Sender profile. Present for inbound messages; absent on status receipts.
contacts[].wa_idstringThe customer's WhatsApp number (their ID on WhatsApp).
contacts[].profile.namestringThe customer's WhatsApp profile (display) name.
messages[]object[]Present for inbound customer messages. See Message object.
statuses[]object[]Present for delivery/read/failed receipts. See Status object.

Message object

Each item in messages[] describes one inbound message.

FieldTypeDescription
idstringMessage ID (wamid). Globally unique — useful as a key to dedupe defensively if you need it.
fromstringThe sender's WhatsApp number (the customer).
timestampstringUnix epoch seconds (as a string) when Meta received the message.
typestringThe message type: text, image, document, audio, video, sticker, location, contacts, interactive, reaction, button, order, system, request_welcome, or unsupported. Tolerate types you don't handle.
<type>objectType-specific content, keyed by the value of type (e.g. a text message has a text object, an image message has an image object).
contextobjectOptional. Present when the message is a reply or forward. Contains from and the id (wamid) of the message being replied to.

Type-specific content

When type isFieldTypeDescription
texttext.bodystringThe message text.
image / video / document / audio / sticker<type>.idstringMeta media_id. Not directly downloadable — see the media limitation.
<type>.mime_typestringMIME type of the media, e.g. image/jpeg.
<type>.sha256stringSHA-256 hash of the media file.
<type>.captionstringOptional. Caption (image, video, document).
document.filenamestringOptional. Original filename (document only).
locationlocation.latitude, location.longitudenumberCoordinates shared by the customer. name and address may also be present.
reactionreaction.emojistringThe emoji reaction. reaction.message_id is the wamid being reacted to.
reaction.message_idstringThe wamid of the message the customer reacted to.
buttonbutton.text, button.payloadstringA tap on a template quick-reply button. text is the button label; payload is the configured payload.
interactiveinteractive.typestringA reply to an interactive message: button_reply or list_reply.
interactive.button_replyobjectPresent when interactive.type is button_reply. Contains id and title.
interactive.list_replyobjectPresent when interactive.type is list_reply. Contains id, title, and optional description.

Status object

Each item in statuses[] is a receipt for a message you sent.

FieldTypeDescription
idstringThe wamid of the message this receipt is for. Match it to a message you previously sent.
statusstringsent, delivered, read, failed, or deleted.
timestampstringUnix epoch seconds (as a string) when the status changed.
recipient_idstringThe customer's WhatsApp number the message was sent to.
conversationobjectUsually present. The messaging conversation this receipt belongs to. Contains id and origin.type (e.g. service, marketing, utility, authentication).
pricingobjectUsually present. Billing info for the conversation: billable (boolean), category, and pricing_model.
errors[]object[]Present only when status is failed. See Error object.
Error object

Each item in statuses[].errors[] describes why a message failed.

FieldTypeDescription
codenumberMeta error code. Look it up in Meta's error-codes reference.
titlestringShort error title, e.g. Message undeliverable.
messagestringOptional. Human-readable error message (often equal to title).
error_data.detailsstringOptional. Additional detail about the failure.
hrefstringOptional. Link to the relevant Meta error-codes documentation.

How your endpoint should behave

  1. Return 200 immediately, then process asynchronously. Superfone does not retry, so a slow or failing response means the event is lost — keep your handler fast and do the heavy work after acknowledging.
  2. Delivery is at-most-once. Superfone sends each event once and does not retry, so if your endpoint is unreachable or returns a non-2xx, the event is missed (lost) — it is never redelivered. Duplicates are unlikely and can only happen if Meta delivers the same upstream event to Superfone more than once; dedupe by messages[].id (wamid) as a defensive measure only.
  3. Iterate entry[] → changes[] → messages[] / statuses[]; handle batches.
  4. Branch on messages vs statuses — you'll receive both.
  5. Tolerate event types you don't handle (you may also receive non-message account events). Ignore what you don't need.

Further reading

The payload Superfone forwards is Meta's standard WhatsApp Cloud API webhook payload. For the full, authoritative schema of every message and status type, refer to Meta's official Webhooks Components reference.

You don't need to set up webhooks on Meta yourself — Superfone handles registration with Meta. The link above is only for understanding the payload fields you'll receive.