Standby webhooks
Updated: Aug 4, 2026
Copy for LLM
Standby webhooks notify your app about conversation activity on a WhatsApp Business phone number when another sender — such as Meta Business Agent (AI) — is currently handling the conversation.
Overview
When Meta Business Agent is enabled on your WhatsApp Business phone number, it coexists with your app on the same number. At any point in time, one sender is the active handler for a given consumer conversation, and the other is a passive listener.
As the passive listener, your app receives standby webhooks — real-time notifications about what is happening in the conversation without being expected to respond. This allows your app to:
- Maintain conversation context — Stay informed about the full conversation history so your app can respond intelligently if control is later transferred to it.
- Monitor messaging activity — Track what Meta Business Agent is saying to WhatsApp users on your behalf.
- Measure delivery — Observe delivery and read receipts for messages sent by Meta Business Agent.
Standby webhooks are intended for passive listening. However, if your app sends a service message while observing a conversation, it will take over as the active handler. This is a valid way to reclaim ownership of a conversation when needed (for example, to escalate to a human agent).
What is a message echo?
When Meta Business Agent (or another sender on the number) sends an outbound message to a consumer, your app receives an “echo” of that message — a copy of what was sent, so your app stays informed even though it did not initiate the message.
Prerequisites
- Your app is subscribed to the
standbywebhook field in the App Dashboard. - The business has enabled Meta Business Agent on the phone number and granted your app standby permissions.
- You have a valid webhook URL configured to receive notifications.
Subscribing to standby webhooks
To receive standby webhooks, subscribe to the
standby field in your app’s Webhooks configuration:- Go to your app in the Meta Developer Portal.
- Navigate to WhatsApp > Configuration > Webhook fields.
- Subscribe to the standby field.
When you receive each webhook type
| What happened | What your app receives | Sub-field |
|---|---|---|
WhatsApp user sends a message (your app is not the active handler) | Standby inbound message | standby.messages |
Meta Business Agent sends a message to the WhatsApp user | Standby message echo | standby.message_echoes |
A message sent by Meta Business Agent gets delivered or read | Standby status receipt | standby.statuses |
What you will NOT receive
You will not receive standby webhooks when:
- You are the active handler — You receive regular
messageswebhooks instead (under themessagesfield). - Your own app sends a message — You do not echo yourself.
- The business hasn’t granted standby permissions — Your app must be configured as a standby listener.
- You haven’t subscribed to the
standbyfield — Subscription is required in your app’s webhook configuration.
Payload structure
Common envelope
Every standby webhook uses this envelope:
{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": {} }, "field": "standby" } ] } ] }
The
standby object contains one of: messages, message_echoes, or statuses.Parameters
| Name | Type | Description |
|------|------|-------------|
|
object | string | Always whatsapp_business_account. |
| entry | array | Array of entry objects. |
| entry[].id | string | Your WhatsApp Business Account ID. |
| entry[].changes | array | Array of change objects. |
| entry[].changes[].value.messaging_product | string | Always whatsapp. |
| entry[].changes[].value.metadata.display_phone_number | string | The business phone number in international format. |
| entry[].changes[].value.metadata.phone_number_id | string | ID of the business phone number. |
| entry[].changes[].value.standby | object | The standby payload. Contains one of: messages, message_echoes, or statuses. |
| entry[].changes[].field | string | Always standby for standby webhooks. |Standby inbound message
Dispatched when a WhatsApp user sends a message and your app is not the active handler (for example, Meta Business Agent is handling the conversation).
Example payload
{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": { "contacts": [ { "profile": { "name": "Test User" }, "wa_id": "<WA_ID>" } ], "messages": [ { "from": "<WA_ID>", "id": "<MESSAGE_ID>", "timestamp": "<TIMESTAMP>", "text": { "body": "Test standby message" }, "type": "text" } ] } }, "field": "standby" } ] } ] }
Parameters
| Name | Type | Description |
|---|---|---|
standby.contacts | array | Array of contact objects describing the message sender. |
standby.contacts[].profile.name | string | The WhatsApp user’s profile name. |
standby.contacts[].wa_id | string | The WhatsApp user’s WhatsApp ID (phone number). |
standby.messages | array | Array of message objects. |
standby.messages[].from | string | Sender’s WhatsApp ID. |
standby.messages[].id | string | Unique message identifier ( wamid.*). |
standby.messages[].timestamp | string | Unix epoch timestamp in seconds. |
standby.messages[].type | string | Message type — for example, text, image, document, audio, video, location, contacts, interactive, button, reaction. |
standby.messages[].text.body | string | Text content (present when type is text). |
All message types supported by WhatsApp (such as image, document, audio, video, location, contacts, interactive, and reaction) are delivered in standby using the same schema as standard incoming message webhooks. The example above shows a
text message; other types include their respective content objects.Standby message echo
Dispatched when Meta Business Agent (or another sender on the number) sends a message to the consumer. The echo contains the send-time parameters exactly as passed to the Send Message API — not the rendered content.
Text message echo
A plain text message sent by Meta Business Agent to the WhatsApp user:
{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": { "message_echoes": [ { "id": "<MESSAGE_ID>", "timestamp": "<TIMESTAMP>", "message": { "messaging_product": "whatsapp", "to": "<WHATSAPP_USER_PHONE_NUMBER>", "recipient_type": "individual", "type": "text", "text": { "body": "Hello! Your order #12345 has shipped.", "preview_url": true }, "context": { "message_id": "<PREVIOUS_MESSAGE_ID>" } } } ] } }, "field": "standby" } ] } ] }
Template message echo
For template messages, the echo contains two template-related objects:
message.template— The send-time parameters (template name, language, component variable values) as passed to the Send Message API. This tells you what values were substituted.template(sibling ofmessage, inside each echo object) — The full unhydrated template definition, matching the format described in Templates overview. This tells you the template structure with placeholder markers (for example,1), not the rendered text.
Use the sibling
template object to understand the template layout, and message.template to see which parameter values were filled in at send time.{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": { "message_echoes": [ { "id": "<MESSAGE_ID>", "timestamp": "<TIMESTAMP>", "message": { "messaging_product": "whatsapp", "to": "<WHATSAPP_USER_PHONE_NUMBER>", "recipient_type": "individual", "type": "template", "template": { "name": "summer_sale_2026", "language": { "code": "en_US" }, "components": [ { "type": "header", "parameters": [ { "type": "image", "image": { "id": "<MEDIA_ID>" } } ] }, { "type": "body", "parameters": [ { "type": "text", "text": "Maria" }, { "type": "text", "text": "25%" } ] }, { "type": "button", "sub_type": "url", "index": 0, "parameters": [ { "type": "text", "text": "summer2026" } ] } ] } }, "template": { "name": "summer_sale_2026", "language": "en_US", "category": "MARKETING", "components": [ { "type": "HEADER", "format": "IMAGE" }, { "type": "BODY", "text": "Hi 1, enjoy 2 off!" }, { "type": "FOOTER", "text": "Limited time" }, { "type": "BUTTONS", "buttons": [ { "type": "URL", "text": "Shop Now", "url": "https://example.com/1" } ] } ], "status": "APPROVED" } } ] } }, "field": "standby" } ] } ] }
Interactive flow message echo
A flow-type interactive message sent by Meta Business Agent:
{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": { "message_echoes": [ { "id": "<MESSAGE_ID>", "timestamp": "<TIMESTAMP>", "message": { "messaging_product": "whatsapp", "to": "<WHATSAPP_USER_PHONE_NUMBER>", "recipient_type": "individual", "type": "interactive", "interactive": { "type": "flow", "header": { "type": "text", "text": "Book Appointment" }, "body": { "text": "Schedule your visit with us." }, "footer": { "text": "Tap below to continue" }, "action": { "name": "flow", "parameters": { "flow_id": "<FLOW_ID>", "flow_cta": "Book Now", "flow_action": "navigate", "flow_action_payload": { "screen": "APPOINTMENT_SCREEN" } } } } }, "flow": { "id": "<FLOW_ID>", "name": "Appointment Booking", "status": "PUBLISHED", "categories": ["APPOINTMENT_BOOKING"] } } ] } }, "field": "standby" } ] } ] }
Parameters
| Name | Type | Description |
|---|---|---|
standby.message_echoes | array | Array of echo objects. |
standby.message_echoes[].id | string | Unique message ID ( wamid.*) assigned at send time. |
standby.message_echoes[].timestamp | string | Unix epoch in seconds when the message was sent. |
standby.message_echoes[].message | object | The exact request body as passed to POST /{phone-number-id}/messages. Contains all send-time parameters. |
standby.message_echoes[].template | object | (Optional) Present only for template messages. The full unhydrated template definition. See Templates overview. |
standby.message_echoes[].flow | object | (Optional) Present only for flow messages. The full flow definition as returned by GET /{flow-id}. |
For template messages,
message.template carries only the send-time parameters (variable values) — see Cloud API — Send message templates. The sibling template object contains the full template definition format as described in Templates overview.Standby status receipt
Dispatched when there are delivery status updates (
sent, delivered, read) for messages sent by Meta Business Agent.Example payload
{ "object": "whatsapp_business_account", "entry": [ { "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "<BUSINESS_PHONE_NUMBER>", "phone_number_id": "<PHONE_NUMBER_ID>" }, "standby": { "statuses": [ { "id": "<MESSAGE_ID>", "status": "delivered", "timestamp": "<TIMESTAMP>", "recipient_id": "<RECIPIENT_WA_ID>", "conversation": { "id": "<CONVERSATION_ID>", "origin": { "type": "utility" } }, "pricing": { "billable": true, "pricing_model": "PMP", "category": "utility", "type": "regular" } } ] } }, "field": "standby" } ] } ] }
Parameters
| Name | Type | Description |
|---|---|---|
standby.statuses | array | Array of status objects. |
standby.statuses[].id | string | The message ID this status refers to. |
standby.statuses[].status | string | Delivery status: sent, delivered, or read. |
standby.statuses[].timestamp | string | Unix epoch in seconds of the status event. |
standby.statuses[].recipient_id | string | WhatsApp ID of the message recipient. |
standby.statuses[].conversation.id | string | Conversation ID. |
standby.statuses[].conversation.origin.type | string | Conversation origin: utility, marketing, authentication, service, or referral_conversion. |
standby.statuses[].pricing.billable | boolean | Whether this message is billable. |
standby.statuses[].pricing.pricing_model | string | Pricing model (for example, PMP). |
standby.statuses[].pricing.category | string | Pricing category. |
standby.statuses[].pricing.type | string | Pricing type (for example, regular). |
Best practices
- Use standby for context, not for responding — Standby webhooks are designed for passive listening. If your app needs to take over the conversation, it can do so by sending a service message, which will make it the active handler.
- Store context from standby events so your app can provide continuity when it becomes the active handler.
- Handle duplicates — Use the message
idfield to deduplicate events in case of redelivery. - Monitor delivery — Use status receipts to track delivery and read rates for messages sent by Meta Business Agent.
See also
- Webhooks overview — General webhook setup and verification
- Webhooks reference — Messages — Standard incoming message webhook reference
- Send Message API — Message API reference (echoed
messagefield mirrors this) - Send message templates — Template send parameters reference
- Templates overview — Template definition format