What Webhooks Do
Webhooks are HTTP requests with JSON payloads that Meta sends to your server in response to events on the WhatsApp Business Platform — incoming messages, message status updates, template approvals, account alerts, calling events, settings changes, and more.
Each webhook arrives on a specific subscription field. You subscribe your app to the fields you care about; Meta sends the relevant events to your configured callback URL.
For the inbound message envelope and inbound webhook POST validation model, see Messaging Webhook. For the management events Dualhook ingests and forwards, see Webhook Events & Notifications. For routing message-path events directly to your endpoint, see Webhook Override.
Where They Go in Dualhook
Dualhook routes webhooks two ways depending on the field:
| Field | Routing |
|---|---|
messages, smb_message_echoes, history, smb_app_state_sync | Direct to your endpoint via Webhook Override. Dualhook does not ingest these. |
Normalized management fields (account_update, account_review_update, business_capability_update, message_template_status_update, message_template_quality_update, phone_number_quality_update, phone_number_name_update, template_category_update, security, account_alerts) | App callback → Dualhook → forwarded to your endpoint with X-Dualhook-* headers |
Fields not normalized by Dualhook today (message_template_components_update, account_settings_update, calls, user_preferences, partner_solutions, payment_configuration_update, automatic_events) | Not first-class Dualhook dashboard events. Subscribe your own app or endpoint if you need them. |
Dualhook ingests normalized management events for state tracking (so the dashboard stays in sync with Meta) and forwards them to you.
For Coexistence onboarding, Dualhook triggers Meta's one-time history and smb_app_state_sync sync requests after Webhook Override is accepted. Meta delivers any resulting payloads directly to your endpoint; the trigger acceptance is not a delivery guarantee.
Subscribing to Fields
Once your webhook endpoint is configured, subscribe your app to individual fields:
- App Dashboard → WhatsApp → Configuration panel.
- Or, for apps created via the "Connect with customers through WhatsApp" use case: App Dashboard → Use cases → Customize → Configuration.
You can also override the callback URL per WABA or per business phone number — useful when partners want unique endpoints for each onboarded customer. See Webhook Override.
Permissions
| Permission | Required for |
|---|---|
whatsapp_business_messaging | The messages webhook field |
whatsapp_business_management | All other webhook fields |
If you're a partner needing these permissions to serve business customers, you must be approved for advanced access via App Review before customers can grant the permissions during onboarding. See WhatsApp Business API Permissions.
All Subscription Fields
| Field | What it notifies you of |
|---|---|
messages | Inbound messages from users + outbound message status updates (sent / delivered / read / failed). The "main" field. |
message_template_status_update | Template review outcome — APPROVED / REJECTED / FLAGGED / etc. |
message_template_quality_update | Template quality score changes |
message_template_components_update | Changes to a template's components (rare; mostly Meta-side reclassification) |
template_category_update | Template category reclassification (e.g. UTILITY → MARKETING) |
phone_number_quality_update | Phone number quality rating + tier changes |
phone_number_name_update | Display name verification outcomes |
account_update | Partner-led business verification, auth-international rate eligibility, primary business location, WABA sharing, policy violations, offboarding, reconnection, deletion. Also carries pricing tier updates and MM API onboarding events. |
account_alerts | Messaging limit changes, business profile changes, OBA status changes |
account_review_update | WABA reviewed against policy guidelines |
business_capability_update | Capability changes (messaging limits, phone number caps, etc.) |
account_settings_update | Phone number settings changed — currently only calling settings |
security | Security setting changes on a phone number |
user_preferences | Marketing-message stop / resume actions by users |
partner_solutions | Multi-Partner Solution status changes |
payment_configuration_update | Payment config changes for Payments API India and Brazil |
automatic_events | Detected purchase / lead events on Click-to-WhatsApp ad chats (opt-in feature) |
calls | All calling events — call connect, status, terminate. Required for Calling API users not on SIP. |
history | One-time chat history sync for WhatsApp Business app users onboarded via Coexistence. Dualhook requests the sync after onboarding; Meta routes the payload directly to your endpoint if the business shared history. |
smb_app_state_sync | Contact sync for WhatsApp Business app users onboarded via Coexistence. Dualhook requests the initial sync after onboarding; Meta routes this directly to your endpoint via Webhook Override. |
smb_message_echoes | Echoes of messages sent via the WhatsApp Business app or a linked device by an onboarded SMB customer. |
The messages field carries many sub-types (text / image / interactive / button reply / call_permission_reply / etc.) — your handler should switch on messages[].type and messages[].interactive.type to route them.
Override Webhooks
Use a different webhook callback URL for specific subscription fields on a WABA or phone number — see Webhook Override. Useful for testing or for partners that want per-customer endpoints.
In Dualhook setups, override is how message-path and supported Coexistence fields bypass Dualhook and go straight to your endpoint.
Payload Size
Webhook payloads can be up to 3 MB. Larger payloads are rejected at the source — Meta won't send them.
Delivery Retries
If your endpoint returns a non-200 status code, or if delivery fails for another reason, Meta retries with decreasing frequency for up to 7 days.
Retries are sent to all apps subscribed to the field on the WABA — so if you have multiple apps on the same WABA, each gets its own retry cycle, which can produce duplicate notifications for already-handled events. Idempotency at your endpoint is mandatory.
For calling-related webhooks, the retry policy is shorter (Meta hasn't published exact numbers) — check the timestamp field and skip stale events.
Mutual TLS (mTLS)
Webhooks support mTLS for added authentication. Recommended over IP allowlisting because Meta's IP ranges change periodically. See Graph API's mTLS for webhooks reference for setup.
mTLS is not supported for SIP signaling — see SIP Configuration for SIP-specific TLS requirements.
IP Allowlisting
If you must allowlist by IP, get the current Meta address space:
whois -h whois.radb.net -- '-i origin AS32934' | grep '^route' | awk '{print $2}' | sort
Or download the geofeed CSV from Meta's webhook docs. As of mid-2024 this collapses to ~23 IPv4 prefixes. Meta changes IPs periodically — set up monitoring to refresh your allowlist, or switch to mTLS to avoid the moving target.
Troubleshooting
If you're not receiving webhooks:
- Confirm your endpoint accepts requests and returns 200.
- Send a test payload from App Dashboard → WhatsApp → Configuration to verify reachability.
- Confirm the field is subscribed and your app has the right permission (
messagesneedswhatsapp_business_messaging; everything else needswhatsapp_business_management). - Check IP allowlist or mTLS configuration.
- Use Dualhook's connection Debug tab to inspect Meta's view of your subscribed_apps + webhook config.
For Webhook Override / inbound webhook POST validation specifics, see Messaging Webhook.