What is a webhook override?
A webhook override, which Meta calls an alternate callback URL, sends the webhooks of one WhatsApp Business Account (WABA) or one business phone number to an endpoint other than the app's default callback URL. You set it with two fields, override_callback_uri and verify_token: on a WABA through the subscribed apps endpoint, POST /{WABA_ID}/subscribed_apps, or on a single number by posting a webhook_configuration object to its Phone Number ID, POST /{PHONE_NUMBER_ID}.
Meta resolves the destination of each supported event in a fixed order: the phone number's override first, then the WABA's override, then the app's callback URL from the App Dashboard. Overrides cover the message-type fields — messages, message_echoes, smb_message_echoes, calls, history, the group events and a few others. Template webhooks such as message_template_status_update and account-level webhooks such as account_update, account_review_update and account_alerts do not support overrides and always go to the app's default URL.
An override changes where events go, not which events exist. The fields still come from the app-level webhook subscription, and Meta requires the app to be subscribed to the WABA before an alternate URL can be set. That is the difference from the webhook subscription itself, which sets one default URL and one field list for every asset the app serves; an override is a per-asset exception to that default.
Key takeaways
- A webhook override sends one WABA's or one phone number's webhooks to a URL other than the app's default callback URL.
- Meta checks the phone number's override first, then the WABA's, then the app's default callback URL.
- Set it with
override_callback_uri(up to 200 characters) and averify_token, onsubscribed_appsor inwebhook_configuration. - Template webhooks and account-level webhooks such as
account_updateignore overrides and always go to the app default. - An override changes only where events go; the fields still come from the app-level webhook subscription.
A webhook override in code
# 1. Alternate URL for a whole WABA (every number in it)curl -X POST "https://graph.facebook.com/v26.0/{WABA_ID}/subscribed_apps" \-H "Authorization: Bearer {ACCESS_TOKEN}" \-H "Content-Type: application/json" \-d '{"override_callback_uri": "https://hooks.example.in/t/clinic-42","verify_token": "{VERIFY_TOKEN}"}'# -> {"success": true}# 2. Alternate URL for one business number (beats the WABA override)curl -X POST "https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}" \-H "Authorization: Bearer {ACCESS_TOKEN}" \-H "Content-Type: application/json" \-d '{"webhook_configuration": {"override_callback_uri": "https://staging.example.in/webhooks/whatsapp","verify_token": "{VERIFY_TOKEN}"}}'# 3. See the whole precedence chain for that numbercurl "https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}?fields=webhook_configuration" \-H "Authorization: Bearer {ACCESS_TOKEN}"# 4. Remove the number's override (falls back to the WABA, then the app)curl -X POST "https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}" \-H "Authorization: Bearer {ACCESS_TOKEN}" \-H "Content-Type: application/json" \-d '{"webhook_configuration": {"override_callback_uri": ""}}'
Set, inspect and clear overrides. The WABA override is removed by re-subscribing the app to the WABA with no body.
How does a webhook override work?
- Step 1
Subscribe the app first
Keep the app-level subscription and its fields in place, and attach the app to the WABA with
POST /{WABA_ID}/subscribed_apps. Meta's requirements put this before any alternate URL. - Step 2
Prepare the alternate endpoint
Give it a valid CA-signed certificate, the same
X-Hub-Signature-256check keyed with the same app secret, and a handler for the webhook verification request that accepts theverify_tokenyou will send with the override. - Step 3
Set the override
For a whole WABA, POST
override_callback_uri(up to 200 characters) andverify_tokento/{WABA_ID}/subscribed_apps. For one number, POST awebhook_configurationobject holding the same two fields to/{PHONE_NUMBER_ID}. Both return{"success": true}. - Step 4
Confirm where events will land
GET /{PHONE_NUMBER_ID}?fields=webhook_configurationreturns the number's own URL, the WABA's URL if one is set, and the app's default — the whole precedence chain in one response. - Step 5
Remove it when you are done
Clear a number's override by posting
override_callback_urias an empty string; clear a WABA's by subscribing the app to the WABA again with no body. Events fall back to the next level.
A webhook override: key facts and limits
- Field
- override_callback_uri (max 200 characters) + verify_token
- WABA level
- POST /{WABA_ID}/subscribed_apps
- Number level
- POST /{PHONE_NUMBER_ID} with webhook_configuration
- Precedence
- Phone number → WABA → app default URL
- Fields covered
- messages, message_echoes, calls, history, group and SMB sync events
- Never overridden
- Template webhooks; account_update, account_review_update, account_alerts
- Prerequisite
- App already subscribed to the WABA
Common mix-ups
Common mistakes with a webhook override
Expecting template events at the override
Template status, quality and category events, and account-level events such as
account_update, ignore overrides and go to the app's default URL. Keep that endpoint alive and route those events yourself byentry[].id.Forgetting a number-level override exists
A number override silently beats the WABA override. If one number's messages go missing after a WABA-wide change, run
GET /{PHONE_NUMBER_ID}?fields=webhook_configurationbefore debugging any code.Skipping signature checks at the alternate URL
Events at the alternate URL still belong to the same app, so they are signed with the same app secret. Share the secret securely with that service rather than leaving it unverified.
Leaving a staging override in place
A number pointed at staging for a test keeps sending real customer messages there until someone clears it. Track every override in configuration and remove it with an empty
override_callback_uri.
Example: One app, forty clinic chains
A Hyderabad software company onboards 40 clinic chains through Embedded Signup. Each chain's WABA gets an override pointing to its own tenant path, so messages arrive already separated per customer, and one chain's new appointment number gets a number-level override to a pilot bot. When a template is approved, though, the message_template_status_update event still lands on the app's default URL — so the company keeps a small router there that looks up entry[].id (the WABA ID) and forwards template events to the right tenant.
Why the difference matters
Why does a webhook override matter?
Partners and multi-brand businesses rarely want every number's traffic in one place. A tech provider can give each onboarded customer its own endpoint, a business can send its support number to the helpdesk and its sales number to the CRM, and a team can point one test number at a staging server without touching production — all without creating extra Meta apps. Knowing that template and account events ignore overrides also tells you exactly what the default URL must still handle.
Webhook Override FAQs
Can different WhatsApp numbers send webhooks to different URLs?+
Does a webhook override change which fields I receive?+
Which wins if both the WABA and the phone number have an override?+
How do I remove a webhook override?+
Is a webhook override the same as webhook fan-out?+
Rules of thumb
Best practices for a webhook override
- Put the tenant or number in the override path (for example /t/clinic-42) so a misrouted event is obvious in logs.
- Audit every number's webhook_configuration after onboarding and before big campaigns.
- Use the WABA level for per-customer routing and the number level only for exceptions.
- Keep one default endpoint that accepts every subscribed field, even when overrides carry most traffic.