The real order of operations for connecting a business phone number to the WhatsApp Cloud API - app subscription vs WABA subscription, the number registration PIN, health_status, and the traps that silently break inbound messages.
Key takeaways
- There are TWO webhook subscriptions, not one. Subscribing the app is not enough - you must also POST to /{waba_id}/subscribed_apps, and it never happens automatically. Skipping it means outbound works and inbound silently does not.
- Meta fires the webhook verification GET during the subscription call itself. Your public endpoint must already be deployed and answering with the saved verify_token before you make that call, or it fails.
- A number must be registered once via POST /{phone_number_id}/register with a 6-digit PIN before any send will work. The rate limit is 10 attempts per number per 72 hours, so guessing the PIN can lock you out for three days.
- Use GET /{phone_number_id}?fields=health_status before blaming your code. Meta returns AVAILABLE, LIMITED or BLOCKED - it answers "can this number send right now" directly.
Connecting a business phone number to the WhatsApp Cloud API looks straightforward in the documentation and turns out to be an order-dependent sequence where failing one step produces no clear error. This is the actual order of operations, including three points Meta mentions only in passing that are, in practice, the most common reason for "everything is configured but inbound messages never arrive".
What must exist first
- Business Manager with a verified business. Verification is not required to send test messages, but it is required to leave the lowest messaging tiers.
- A Meta app with the WhatsApp product added, and its
app_idandapp_secretin hand. - A WhatsApp Business Account (WABA) with its
waba_id, and a phone number with itsphone_number_id. - A System User token carrying
whatsapp_business_messagingand explicitly assigned to the WABA and phone number assets. A short-lived token from the Graph API Explorer works today and fails tomorrow with code 190. - A public HTTPS endpoint for the webhook, deployed and live. Not a temporary tunnel - the address that will stay.
Step 1: find the phone number ID
The phone number ID is not the phone number. Retrieve it from the WABA's number list with GET /{waba_id}/phone_numbers; nearly every later call uses it.
Step 2: configure the app-level webhook
The call is POST /{app_id}/subscriptions, and it is equivalent to doing it by hand in the App Dashboard under WhatsApp then Configuration. Request fields:
object- the valuewhatsapp_business_accountcallback_url- your public webhook URLverify_token- the string you have stored on your sidefields- at minimummessages; in practice alsomessage_template_status_updateandmessage_template_quality_updateso you learn when a template is approved, rejected or pausedaccess_token- not the user token. This call needs an App Access Token, which is literallyAPP_ID|APP_SECRETjoined by a pipe
The trap: Meta fires the GET verification request at your callback_url during this call, synchronously. If your server is not deployed yet, or the verify_token you send does not match the one you stored, the call fails. There is no later retry.
Step 3: the subscription everyone forgets
This is the step behind most "inbound messages never arrive" incidents. The app-level subscription tells Meta where to send events, but it does not connect your WhatsApp Business Account to the app. That is a separate call:
POST /{waba_id}/subscribed_apps - with an empty body.
It is idempotent and safe to repeat. It never happens automatically. Without it, outbound sending works perfectly, which is exactly what misleads you into thinking the connection is fine. Verify with GET /{waba_id}/subscribed_apps.
Step 4: register the number
Before any first send, the number must be registered for Cloud API messaging:
POST /{phone_number_id}/register with messaging_product: "whatsapp" and a six-digit pin.
The PIN is the number's two-step verification code. If two-step verification is off, this call sets it. If it is already on, the PIN must match the existing one. The rate limit is 10 attempts per number per 72 hours, so repeated guessing locks the number out for three days. If you do not have the PIN, reset it in WhatsApp Manager before trying.
An unregistered number returns 133010 or 131045 on every send.
Step 5: confirm the number can actually send
Rather than guessing, Meta exposes a purpose-built check: GET /{phone_number_id}?fields=health_status. The answer is one of AVAILABLE, LIMITED or BLOCKED. Run this first whenever sending stops working, before looking for a bug in your code.
Handling the inbound webhook
Your endpoint must serve two request types:
- GET - verification. Meta sends
hub.mode,hub.verify_tokenandhub.challenge. If the verify token matches, return thehub.challengevalue as raw text. - POST - events. You must validate the
X-Hub-Signature-256header, an HMAC-SHA256 of the raw request body keyed with the app secret.
Second common trap: signature validation must run against the raw bytes. If your framework parses the JSON and you re-serialise it to a string, the signature will not match even when the content is identical, because key order and whitespace change. Behind a proxy, make sure the webhook path is mounted before the body parser.
One last rule: always return 200 to POST events, even when your internal processing fails. Otherwise Meta redelivers the same event repeatedly and eventually disables the subscription. Log the error on your side and move on.
What stays manual
Business verification, uploading a business profile photo and requesting a messaging-tier increase all go through Meta's own interfaces and take days rather than minutes. Start them early, in parallel with development, rather than discovering on launch day that the account is capped at 250 recipients per day.
Frequently asked questions
Why does WhatsApp Cloud API outbound work but inbound messages never arrive?
Almost always because only the app-level webhook subscription was done. There is a second, separate call - POST /{waba_id}/subscribed_apps - that connects your WhatsApp Business Account to the app. It never happens automatically, and without it outbound sending works normally while no inbound event is ever delivered.
What access token does POST /{app_id}/subscriptions need?
An App Access Token, not the System User token used for messaging. The App Access Token is formed by joining the app ID and app secret with a pipe character: APP_ID|APP_SECRET. Sending the messaging token here fails with a permissions error.
Do I need to register the phone number before sending?
Yes, once per number, via POST /{phone_number_id}/register with messaging_product set to whatsapp and a six-digit PIN. Until that succeeds, every send fails with 133010 or 131045. Note the limit of 10 registration attempts per number per 72 hours - guessing the PIN can lock the number for three days.
Why does my X-Hub-Signature-256 validation always fail?
Because the HMAC is computed over the raw request body. If a JSON body parser runs first and you re-serialise the parsed object, key ordering and whitespace change and the signature no longer matches, even though the data is the same. Capture the raw bytes before any parsing, and behind a proxy mount the webhook route before the body parser.
How do I check whether a WhatsApp number is currently able to send?
Call GET /{phone_number_id}?fields=health_status. Meta answers with AVAILABLE, LIMITED or BLOCKED. This is a purpose-built check and it is the fastest way to separate an account-level problem from a bug in your own integration.
Should my webhook return an error status when processing fails?
No. Always return 200 for POST events. A non-200 causes Meta to redeliver the same event repeatedly and, if it persists, to disable the subscription entirely. Log the failure on your side, return 200, and reconcile separately.
Keep reading
Related service
WhatsApp Cloud API
Templates, a two-way inbox and reminders on the official Meta API.
About the author
Yehonatan Saadia
Freelance automation, web & MVP engineer
I'm Yehonatan Saadia, a senior engineer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build and I'll tell you the fastest reliable way to ship it.
