Facebook, Instagram and Messenger webhooks: setup and testing

To test Facebook, Instagram or Messenger webhooks, use a free webhooks.sh bin as the callback URL in your Meta app: it answers Meta's hub.challenge verification and shows every event live, including Test button samples. No signup, 48 hours, 100 requests a day.

Facebook Page, Instagram and Messenger webhooks all run through Meta's Webhooks product: you set up one callback URL that passes a verification request, then set up field subscriptions per object. The quickest way to see real notifications is to put a free bin in as the callback URL, since it answers the verification by itself, and hit the Test button in the App Dashboard. WhatsApp has its own setup on our WhatsApp webhooks page.

Use a free bin as your Meta callback URL

  1. Create a bin in your browser at view.webhooks.page or by running curl -X POST https://webhooks.sh/v1/bins.
  2. In the App Dashboard, open the Webhooks settings for your product (for a Messenger webhook, go to Products > Messenger > Settings) and paste the bin's ingest URL into the Callback URL field along with any string you want as the Verify Token.
  3. Save, and the bin answers Meta's hub.challenge GET request on its own while still showing the verification call in the viewer.
  4. Subscribe to the fields you need, like messages for Messenger.
  5. Select Test next to a field, click Send to My Server, and watch the sample request show up in the viewer.

Apps in development mode only get these test notifications and events triggered by people who have a role on the app.

# 1. Create a free bin (or open view.webhooks.page)
curl -X POST https://webhooks.sh/v1/bins

# 2. The verification request Meta sends: the bin replies with the challenge
curl "https://<bin>.webhooks.page/meta?hub.mode=subscribe&hub.challenge=1158201444&hub.verify_token=meatyhamhock"

# 3. Meta's documented Messenger test body
curl -X POST "https://<bin>.webhooks.page/meta" \
  -H "Content-Type: application/json" \
  -d '{"object": "page", "entry": [{"messaging": [{"message": "TEST_MESSAGE"}]}]}'

Objects, fields and the payload shape

When you configure Meta webhooks, you pick an object type (User, Page, Instagram and others) and subscribe to specific fields on that object. In Live mode, you only get notifications for objects that have granted your app the matching permission.

Every notification arrives as a JSON POST containing object and an entry array. Each item inside entry has the object's id, time (a Unix timestamp for when Meta sent the notification, not when the change happened), and a changes array where each item holds field and value. If you turn Include Values off, the payload sends changed_fields instead of changes.

Meta can batch up to 1000 updates into one POST, so loop over the whole entry and changes arrays.

{
  "object": "user",
  "entry": [
    {
      "id": "10210299214172187",
      "uid": "10210299214172187",
      "time": 1520383571,
      "changes": [
        {
          "field": "photos",
          "value": { "verb": "update", "object_id": "10211885744794461" }
        }
      ]
    }
  ]
}

Delivery and retry rules differ per product

The Graph API, Messenger and WhatsApp handle failed deliveries differently, and Messenger is the strictest of the three.

Have your server reply with 200 OK right away and run any slow work in the background after responding. Because retries will send the same notification more than once, deduplicate incoming events on your side, and store whatever data you need when it arrives, because past webhook notifications can't be queried later. More in the webhook retries guide.

ProductExpected replyRetriesIf it keeps failing
Graph API objects (Pages, Instagram)200 OK over HTTPSImmediately, then less often over 36 hoursDropped after 36 hours
Messenger200 OK within 5 secondsA few immediate retries, alert after 15 minutesApp unsubscribed after 1 hour
WhatsApp Cloud API200 OKDecreasing frequency for up to 7 daysDropped after 7 days

Instagram webhook requirements

  • The app must be in Live mode to receive notifications for app users.
  • The comments and live_comments fields require Advanced Access.
  • The account that owns the media has to be public, because private accounts don't get comment or mention notifications.
  • Subscribed fields are chosen once for the whole app, not per account.
  • Each Instagram professional account must be subscribed to your app by sending a POST request to its /subscribed_apps edge.

Use the Test button in the App Dashboard first to check your parser against a sample payload, then trigger a real comment or message.

Messenger and the 5-second rule

Messenger expects a 200 OK over HTTPS within 5 seconds. If deliveries are still failing after 15 minutes, Meta sends an alert to your developer account, and if they keep failing for 1 hour, you get a Webhooks Disabled alert and the app is unsubscribed from webhooks for that Page or Instagram professional account, which means you have to go back and subscribe it again by hand. Answer the request first and process the payload later.

Since a webhooks.sh bin replies 200 to every request, pointing your callback URL at a bin while your handler is down keeps events flowing and visible in the browser, as long as you stay under 100 requests a day and the bin hasn't hit its 48-hour expiry.

Check X-Hub-Signature-256

Every POST from Meta includes an X-Hub-Signature-256 header formatted as sha256= followed by a SHA256 signature of the payload, keyed with your app secret. You need to hash the raw request body bytes before parsing the JSON and compare your result to the string after sha256= with a constant-time comparison function.

Free bins don't verify Meta signatures for you, but X-Hub-Signature-256 is not redacted in the bin viewer, so you can paste a real body and signature into the HMAC signature verifier to check your code.

import hashlib
import hmac


def is_from_meta(raw_body: bytes, signature_header: str, app_secret: str) -> bool:
    digest = hmac.new(app_secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest("sha256=" + digest, signature_header or "")

Keep the URL or move to localhost

A free bin lasts 48 hours and takes up to 100 requests a day (with bodies up to 100KB). To keep the verified callback URL working past 48 hours, claim the bin with a free Google sign-in before it expires; the timer stops and you keep 30 days of rolling history at the same URL.

For more volume, Solo ($12 a month, see pricing) gives you a named endpoint on webhooks.host with 250,000 requests per billing period. With Solo, the wsh CLI can relay live deliveries straight to your local server (wsh listen <endpoint> --to localhost:3000), and you can set the endpoint to forward every request to up to 3 verified HTTPS URLs. If you're working with WhatsApp Cloud API payloads and its 7-day retry window, see the WhatsApp page linked at the top.

FAQ

How do I verify a Facebook webhook callback URL?

Meta sends a GET request with hub.mode=subscribe, hub.challenge and hub.verify_token. Check that the token matches your Verify Token and respond with the hub.challenge value. A free bin answers this check automatically.

How do I test an Instagram webhook?

In the App Dashboard Webhooks settings, select Test next to a field and click Send to My Server to send a sample payload, then trigger a real event on Instagram. Development-mode apps only get test notifications and events from people with a role on the app.

Why did Meta disable my Messenger webhook?

Your endpoint failed to return a 200 OK within 5 seconds for 1 hour straight, so Meta sent a Webhooks Disabled alert and unsubscribed the app from that Page or Instagram professional account. Once you fix the endpoint, you have to subscribe the Page or account again.

Do Meta webhooks need App Review?

The Webhooks product itself doesn't require App Review, but your app only receives notifications for objects that have granted it the required permissions. For Instagram, getting comments and live_comments notifications requires Advanced Access.

Why am I not getting Instagram comment webhooks?

Check four things: the account that owns the media must be public, your app has to be in Live mode, the comments field needs Advanced Access, and the Instagram professional account must be subscribed to your app via a POST to its /subscribed_apps edge.

How long does Meta retry failed webhooks?

For Graph API objects like Pages and Instagram, Meta retries immediately and then a few more times with decreasing frequency over 36 hours before dropping the notification. WhatsApp Cloud API webhooks retry for up to 7 days, while Messenger unsubscribes your app after 1 hour of failed deliveries.

Last updated

Try it in 10 seconds.

Create a free bin at view.webhooks.page — no signup, no install. Point your sender at the URL and watch requests land live. Bins expire after 48 hours.