No credit card required • Cancel anytime

Webhooks: why messages arrive, and why they sometimes don't

The failure where every light is green and nothing is delivered — once for WhatsApp, once for a Page — and how to prove which side it is on.

Sending and receiving are two different systems, and only one of them is proved by a token that verifies.

  • Sending is us calling Meta. A valid token with the messaging permission is enough.
  • Receiving is Meta calling us. It needs a webhook URL Meta has verified, the messages field subscribed, and the account the messages belong to subscribed to your app.

That last condition is the one this article exists for. It has two forms — one for WhatsApp, one for Instagram and Messenger — and Meta surfaces neither of them.

The WhatsApp form: an account subscribed to somebody else's app

Every signal can be green while nothing is delivered.

Your credentials verify. The Meta console shows the callback URL verified with a green tick. The messages field shows as subscribed. Your test message sends and arrives on a real phone. And every message a customer sends you vanishes — no error, no bounce, no entry in any log on either side.

The cause is that a WhatsApp Business Account is subscribed to an app, and it is not necessarily yours. An account created by Meta's "Try it out" flow is subscribed to Meta's own internal app. The callback URL and field subscription you configured belong to your app, which the account is not talking to — so they are correctly configured and never consulted.

Nothing in Meta's console shows this. It cost us a live debugging session to find, which is why our connect flow now makes the subscription call for you every time you save.

What we do about it

When you save your credentials we subscribe your app to your WhatsApp Business Account automatically. The call is additive — it does not displace another app's subscription, and repeating it on an account already subscribed succeeds rather than failing, so reconnecting is always safe.

If it does not work, the save still succeeds and the result tells you so. Two messages are worth recognising:

  • "We could not tell which WhatsApp Business Account this number belongs to." We had no account ID and could not read one back from Meta, so the subscription was never attempted. Copy the WhatsApp Business Account ID from WhatsApp → API Setup and save again.
  • A subscription error quoting Meta. The call was made and refused — almost always a token missing whatsapp_business_management. Regenerate it with all three permissions (Generate a permanent token).

In both cases your number can still send. It is receiving that is not wired up, which is exactly the state that looks fine until a customer messages you.

The Page form: a Page that was never subscribed to your app

Instagram and Messenger have an exact twin of the problem above, and it is easier to hit.

A Facebook Page is subscribed to an app in its own right, separately from anything you set in the Messenger or Instagram webhook panels. You can have a verified Page access token, a callback URL the console shows as verified, and the messages field ticked — and receive nothing, because the Page itself was never subscribed to your app.

As with WhatsApp, nothing in Meta's console shows this, and as with WhatsApp we make the call for you: connecting Instagram or Messenger subscribes your Page to your app. If that call fails the connection still saves — it can send — and the result says that inbound messages may not arrive.

To check it yourself, ask Meta what the Page is subscribed to:

GET https://graph.facebook.com/v19.0/{PAGE_ID}/subscribed_apps
Authorization: Bearer {YOUR_PAGE_ACCESS_TOKEN}

An empty list, or a list without your app in it, is the whole problem. Reconnect from Configuration → Channels, which re-runs the subscription.

One Page, two channels, two subscriptions to think about. Instagram DMs and Messenger messages both arrive against the same Page, but Meta addresses them differently — an Instagram webhook names the Instagram professional account, a Messenger webhook names the Page — so having one of them working tells you very little about the other. If Messenger arrives and Instagram does not, check that the Instagram professional account is still linked to the Page in Meta Business Suite → Linked accounts; that link is where we read the account id from, and it is what an Instagram webhook is addressed to.

Why there is no test-send button for Instagram or Messenger

Because there is nothing to send one to.

Instagram and Messenger only allow a message to someone who has written to you first. A test send would have no recipient and would be refused every time — a button whose only possible outcome is teaching you that a working connection is broken.

The proof those connections receive is the message you send your own Page or profile from a personal account. The proof they send is the bot's reply to it. Do both before you tell a customer about the channel — the same advice as for WhatsApp, and for the same reason.

Proving which side is broken

Do these in order; each one rules out a layer.

  1. Send a test message from Configuration → Channels. If this fails, the problem is your token or permissions, not webhooks at all.

  2. Message your business number from a personal phone and watch the Inbox. If it appears, everything works.

  3. If it does not appear, ask Meta which apps your account is subscribed to:

    GET https://graph.facebook.com/v19.0/{WHATSAPP_BUSINESS_ACCOUNT_ID}/subscribed_apps
    Authorization: Bearer {YOUR_PERMANENT_TOKEN}
    

    The reply lists the subscribed apps by id and name. If your app is not in that list, that is the whole problem — reconnect from Configuration → Channels, which re-runs the subscription, and check again.

The rest of the webhook setup

You do not paste the callback URL or verify token from this article — Configuration → Channels shows the exact values for your workspace, and they are the ones to copy. The same two values work for all three channels; what differs is where in Meta's console you paste them (WhatsApp → Configuration, Messenger → Webhooks, Instagram → Webhooks) and that each one needs the messages field subscribed separately. Two things about them are worth understanding:

  • Meta verifies the URL by calling it once with a challenge value (opens in a new tab) it expects echoed back. That handshake either passes immediately or does not; a URL that will not verify is unreachable from the public internet, not misconfigured.
  • Only the messages field matters for lead intake. Subscribing to more fields does no harm and no good.

Every inbound message is signature-checked

Meta signs each webhook request with your app secret. We recompute the signature over the exact bytes that arrived and reject anything that does not match — that is the entire reason the app secret is one of the four values you paste, and why a wrong one produces a number that sends perfectly and receives nothing.

Then we route the message to your workspace by the id inside it — the phone number ID for WhatsApp, the Page id for Messenger, the Instagram professional account id for Instagram — which is why those values have to be yours and correct. It is also why the Instagram card has no account field: we read the linked account off your Page during verification rather than ask you for the one value that is easiest to get wrong and impossible to notice being wrong.

Was this helpful?