smb_message_echoes is the Coexistence webhook field that tells your backend what staff sent from the WhatsApp Business app or a linked device, after that number was onboarded to Cloud API. Meta's reference documents the payload shape; what it does not spell out is that the field only exists for Coexistence numbers, that it also carries edit and revoke events for app messages, and that it is one of the fields a partner can route straight to your server instead of through their own infrastructure. In Dualhook setups it is direct-routed to your endpoint via Webhook Override and never stored by Dualhook.
What smb_message_echoes Reports
This field lets your backend see business-side app activity, including:
- messages sent from the WhatsApp Business app,
- messages sent from a companion or linked device,
- deleted messages (
revoke), - edited messages (
edit).
Use it when your system needs to keep its internal conversation state aligned with messages that staff send outside your API workflow.
smb_message_echoes vs message_echoes
Meta has two echo fields and they answer different questions:
| Field | What it echoes | Who needs it |
|---|---|---|
message_echoes | Messages the business sent through the Cloud API, from any app or tool with access to the number. | Teams that run more than one system against the same number and want one of them to see everything sent via API. |
smb_message_echoes | Messages sent from the WhatsApp Business app or a linked device on a Coexistence number, including edits and revokes. | Any backend that must stay in sync with what staff type on their phones. |
If your number is not on Coexistence, smb_message_echoes never fires. If you only subscribed to message_echoes and staff replies from the app seem to vanish, this is the field you were missing.
When It Fires
Meta sends this field when an onboarded WhatsApp Business app user sends, edits, or revokes a message from the mobile app or linked device.
Payload Shape
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "<WABA_ID>",
"changes": [
{
"field": "smb_message_echoes",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550783881",
"phone_number_id": "106540352242922"
},
"message_echoes": [
{
"from": "15550783881",
"to": "16505551234",
"id": "<WAMID>",
"timestamp": "1739321024",
"type": "text",
"text": {
"body": "Here's the info you requested."
}
}
]
}
}
]
}
]
}
type works like normal WhatsApp message payloads: it names the message type and points to a matching object such as text, image, video, document, revoke, or edit.
Revoke and Edit Events
For revoked app messages, Meta sends type: "revoke" with the original message ID.
{
"type": "revoke",
"revoke": {
"original_message_id": "<ORIGINAL_WAMID>"
}
}
For edited app messages, Meta sends type: "edit" with the original message ID and the updated message object.
{
"type": "edit",
"edit": {
"original_message_id": "<ORIGINAL_WAMID>",
"message": {
"type": "image",
"image": {
"caption": "Updated image caption"
}
}
}
}
Dualhook Handling
Dualhook does not ingest, store, forward, or replay smb_message_echoes payloads. This field is direct-routed through Webhook Override, so Meta sends it to your configured endpoint.
That routing is intentional: app-sent message echoes can contain message bodies and media metadata, so they stay out of Dualhook's storage boundary. If echoes do not arrive, check the active Webhook Override configuration and your endpoint logs; Dualhook has no copy to resend.
Implementation Notes
- Store echoes separately from API send statuses so you can distinguish "sent by staff in app" from "sent by backend".
- Make handlers idempotent by
message_echoes[].id. - For
revokeandedit, update the original message referenced byoriginal_message_id. - Validate
metadata.phone_number_idand WABA ID before accepting the payload. - Be ready for BSUID-era changes in phone-number fields on related message webhooks.