Skip to content

Webhook Subscription

Also called: App webhook configuration · Callback URL and verify token · Webhook fields subscription · Application Subscriptions API

The app-level setup that tells Meta where to send WhatsApp webhooks: a callback URL, a verify token and the WABA fields you want to receive.

Reviewed

What is a webhook subscription?

A webhook subscription is the configuration on your Meta app that switches webhooks on. It has three parts: a callback URL (your public HTTPS endpoint), a verify token you choose, and the list of WhatsApp Business Account fields to receive — messages for inbound messages and statuses, plus fields such as message_template_status_update, account_update or phone_number_quality_update.

You set it in the App Dashboard under WhatsApp > Configuration (or Use cases > Customize > Configuration for apps built on the WhatsApp use case), or with the Application Subscriptions API: POST /{APP_ID}/subscriptions with object=whatsapp_business_account, callback_url, verify_token and fields, where {APP_ID} is your Meta App ID and the call uses an app access token. Setting or editing the URL or token triggers the webhook verification request, and the change is saved only if your endpoint echoes hub.challenge with a 200.

The subscription sets one default URL and one field list for every WhatsApp asset the app serves. It does not connect any account by itself: each WABA must also be attached to the app through the subscribed apps endpoint, POST /{WABA_ID}/subscribed_apps — a separate step and the most common reason a correctly configured endpoint receives nothing. A per-WABA or per-number exception to the default URL is a webhook override.

Key takeaways

  • A webhook subscription is the app-level setting that tells Meta where to send WhatsApp webhooks and which fields to send.
  • It has three parts: an HTTPS callback URL, a verify token you choose, and the WhatsApp Business Account fields to receive.
  • Saving the URL or token triggers a verification GET; the change saves only if your endpoint echoes hub.challenge with a 200.
  • Each WABA must also be attached with POST /{WABA_ID}/subscribed_apps, or no events arrive for it.
  • Meta warns that some webhooks are not sent while an app is in Development mode, so switch it to Live.

See InfiQ pricing

Interactive stop

Tick off a subscription that actually delivers

9/9

ticked

Required
7 / 7
Optional
2 / 2

Events will flow

Every item ticked.

Everything a working subscription needs, in setup order. Tick items yourself: the required ones are what stand between a saved callback URL and events actually arriving.

Events will flow. 9 of 9 ticked.

How does a webhook subscription work?

  1. Stand up the endpoint

    Publish a webhook endpoint: a public HTTPS URL with a valid CA-signed certificate — Meta does not support self-signed ones — that answers GET verification requests and POST events.

  2. Choose a verify token

    Pick a long random string, store it as a secret on the server, and use the same value in the subscription.

  3. Save the callback URL

    Enter the URL and token in WhatsApp > Configuration, or call POST /{APP_ID}/subscriptions. Meta immediately sends the hub.challenge GET; the save succeeds only if your endpoint echoes it.

  4. Subscribe to fields

    Tick messages plus the account fields your team acts on, or pass them as a comma-separated fields list. Read the result back with GET /{APP_ID}/subscriptions.

  5. Attach each WABA

    Call POST /{WABA_ID}/subscribed_apps with a token that can manage that account. Until you do, the subscription has no account to send events for.

  6. Go Live and test

    Switch the app to Live mode, send a test payload from the Configuration panel, then message the business number and watch a real messages POST arrive.

Common mistakes with a webhook subscription

  • Configuring the app but not the WABA

    The callback verifies and the fields are ticked, yet nothing arrives, because the WABA was never attached with subscribed_apps. Check with GET /{WABA_ID}/subscribed_apps.

  • Subscribing only to messages

    You get conversations but miss template pauses, quality changes and account restrictions. Add the account fields your operations team acts on.

  • Leaving the app in Development mode

    Meta warns that some webhooks are not sent while an app is in Dev mode. Switch it to Live before relying on production traffic.

  • Dashboard-only configuration

    Hand-edited settings drift between staging and production and vanish when someone edits the wrong app. Manage the subscription through the API in a deploy step and verify it with a GET.

  • Changing the URL before the new endpoint is ready

    Each edit of the URL or token runs a fresh handshake against the new values. Deploy the endpoint and its token first, then save, or the change is rejected.

A webhook subscription in code

# 1. Create or update the app-level subscription (app access token = {APP_ID}|{APP_SECRET})
curl -X POST "https://graph.facebook.com/v26.0/{APP_ID}/subscriptions" \
-d "object=whatsapp_business_account" \
-d "callback_url=https://api.example.in/webhooks/whatsapp" \
-d "verify_token={VERIFY_TOKEN}" \
-d "fields=messages,message_template_status_update,phone_number_quality_update,account_update" \
-d "access_token={APP_ID}|{APP_SECRET}"
# -> {"success": true}, once your endpoint has echoed hub.challenge
# 2. Read it back
curl "https://graph.facebook.com/v26.0/{APP_ID}/subscriptions?access_token={APP_ID}|{APP_SECRET}"
# 3. Attach a WhatsApp Business Account so its events flow to the subscription
curl -X POST "https://graph.facebook.com/v26.0/{WABA_ID}/subscribed_apps" \
-H "Authorization: Bearer {ACCESS_TOKEN}"

Steps 1 and 2 configure the app; step 3 connects an account. You need both.

A webhook subscription: key facts and limits

Parts
Callback URL, verify token, subscribed fields
Object
whatsapp_business_account
API
POST /{APP_ID}/subscriptions with an app access token
Certificate
Valid CA-signed TLS; self-signed not supported
Saved when
Your endpoint echoes hub.challenge with a 200
Also required
POST /{WABA_ID}/subscribed_apps for each WABA
App mode
Live — some webhooks are not sent in Dev mode

Why does a webhook subscription matter?

Every inbound message, delivery status and account alert depends on this one configuration. Subscribe to too little and you miss template pauses or quality drops; configure the app without attaching the WABA and nothing arrives at all; leave the app in Development mode and Meta warns that some webhooks will not be sent. Getting the subscription right — and keeping it in code rather than in someone's memory of the dashboard — is what makes webhooks dependable across environments.

Example: Subscriptions as code in Nagpur

A Nagpur logistics start-up keeps its webhook subscription in a deploy script. On each release it calls POST /{APP_ID}/subscriptions for its staging and production apps with messages, message_template_status_update and phone_number_quality_update, reads the list back with a GET and fails the deploy if a field is missing; its onboarding job calls subscribed_apps for every new franchise WABA. The check paid off when a teammate debugging in the dashboard unticked message_template_status_update on production — the next deploy failed loudly instead of hiding a template pause during the festive rush.

— Subscriptions as code in Nagpur

Webhook Subscription FAQs

Can staging and production share one webhook subscription?+
No, because a subscription belongs to one Meta app and has one default callback URL. Use a separate app for staging, with its own verify token, app secret and test number, or point a single test number at staging with a phone-number webhook override.
What is the difference between the webhook subscription and subscribed_apps?+
The subscription is app-level: one callback URL, verify token and field list. `subscribed_apps` attaches a specific WABA to the app so its events flow to that subscription, and you need both.
Can I set up the webhook subscription through the API instead of the dashboard?+
Yes, with `POST /{APP_ID}/subscriptions` and an app access token. Pass `object=whatsapp_business_account`, `callback_url`, `verify_token` and `fields`, then read the result back with a GET.
Do I need one subscription per phone number?+
No, one app subscription serves every WABA attached to the app and every number in them. Each payload says which WABA (`entry[].id`) and which number (`phone_number_id`) it concerns.
Can different numbers send webhooks to different URLs?+
Yes, with webhook overrides on a WABA or a phone number. The app subscription stays the default URL and still defines which fields are sent.

Best practices for a webhook subscription

  • Use a different verify token per environment so a staging endpoint can never verify a production subscription.
  • Keep the verify token separate from the app secret: the token only answers the handshake, while the app secret signs every POST.
  • Read the subscription back after every change and alert if a field disappears.
  • Return 200 for every field from day one, even before its handler exists.
  • Write down which team owns each subscribed field and acts on its events.

Talk to InfiQ

From definition to your first message

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.