← All guides
Messenger & InstagramJuly 12, 20266 min read

Test Messenger & Instagram Webhooks on Localhost

Receive, route, and transform Meta Messenger and Instagram webhooks on your messenger webhook localhost with Relayers — verify handshake and bot messages included.

Building a Messenger or Instagram bot means fighting two problems before you ever read a single message. First, Meta insists on a verification handshake: a GET request carrying hub.mode, hub.verify_token, and hub.challenge that your callback URL must echo back correctly, or the subscription silently refuses to save. Second, once you clear that gate, every inbound message arrives as a POST from Meta's servers — which cannot reach the API running on your laptop.

The usual workaround is to deploy to staging, tail remote logs, and message your own bot from a phone while squinting at a dashboard. That loop is slow and it hides the actual payload from your debugger. Relayers gives you a public HTTPS endpoint that forwards Meta's requests — untouched — straight to localhost, so both the handshake and real bot messages hit your breakpoints.

What you'll build

Prerequisites

1Create an endpoint in Relayers

Open the desktop app, go to Endpoints, and create a new one. Give it a name like meta-messaging and copy the generated ingest URL:

https://api.relayers.app/v1/webhooks/wep_8fk29dl3

That wep_<id> URL is public, HTTPS, and stable — exactly what Meta's dashboard expects. Use Send test to fire a sample payload and confirm the endpoint is live before wiring anything else up.

2Configure the webhook in the Meta App Dashboard

In the Meta App Dashboard, open your app and go to Messenger → Webhooks (or Instagram → Configuration for IG messaging). Under Callback URL paste your Relayers ingest URL, and under Verify Token enter any secret string you choose — it just has to match what your handler compares against.

When you click Verify and Save, Meta sends a GET request to your callback URL:

GET /v1/webhooks/wep_8fk29dl3?hub.mode=subscribe
    &hub.verify_token=your-secret-token
    &hub.challenge=1158201444

Your local handler must confirm the token and echo the raw hub.challenge value back with a 200:

app.get("/webhook", (req, res) => {
  const mode = req.query["hub.mode"];
  const token = req.query["hub.verify_token"];
  const challenge = req.query["hub.challenge"];

  if (mode === "subscribe" && token === process.env.VERIFY_TOKEN) {
    return res.status(200).send(challenge);
  }
  res.sendStatus(403);
});

Because Relayers forwards the query string and method verbatim, this handshake succeeds against localhost just as it would in production. Once verified, subscribe to the fields you need — messages and messaging_postbacks are the essentials — and, for Messenger, subscribe the specific Page to your app. For Instagram, make sure the IG account is subscribed under the Instagram product.

3Forward events to localhost

Point the tunnel at your bot. From a terminal:

relayers login
relayers listen --forward localhost:3000

Alternatively, do it in the desktop app: open Rules on your endpoint and set the destination to a tunnel → localhost:3000. Either way, every request Meta sends to wep_8fk29dl3 now replays against your local server. Re-run the verification in the dashboard and watch the GET land in your logs.

4Route and filter with JQ

Meta sends more than messages — it also delivers read receipts, delivery confirmations, and echoes of your own outbound messages. You usually want only genuine inbound messages hitting your handler.

Open Rules, add a JQ filter, and keep the request only when it carries an inbound message body:

.entry[0].messaging[0].message != null

Rules match top to bottom and first match wins, so you can stack more specific rules above general ones — for example, route postbacks to one localhost port and messages to another — and drag to reorder them. Everything that fails the filter simply isn't forwarded.

5Transform the payload

Meta's envelope is deeply nested. If your handler only cares about who sent what, use a Transformation to flatten it before it reaches your code. In the Transformations view, pick jq and use the test playground with a captured event:

{
  sender: .entry[0].messaging[0].sender.id,
  text: .entry[0].messaging[0].message.text
}

The playground shows the output side by side with the input, so you can iterate without redeploying. Relayers also supports JSONata, JavaScript, and Go templates if you prefer a different transform language.

A note on signatures

Meta signs every POST with an X-Hub-Signature-256 header — an HMAC-SHA256 of the raw body keyed on your app secret. Relayers forwards the original body and headers untouched, so that signature stays valid end to end. Your local verification code works exactly as it will in production:

const expected = "sha256=" + crypto
  .createHmac("sha256", process.env.APP_SECRET)
  .update(rawBody)
  .digest("hex");
// timing-safe compare against req.headers["x-hub-signature-256"]

If you apply a transformation, verify the signature before the transform runs, since HMAC is computed over the original bytes.

Going to production

When your bot is ready to ship, keep the same Relayers endpoint and just repoint the rule's destination from the tunnel to your public production URL. Nothing in the Meta App Dashboard changes — the callback URL and verify token stay identical, so there's no re-verification dance on deploy. You can also Export your endpoint, rules, and transformations as a JSON bundle and Import them into another workspace to reproduce the exact setup.

Troubleshooting

Every request Meta sends is captured in Analytics and Events, so if something goes wrong you can inspect the exact payload and retry it against localhost without messaging your bot again.

Wrap-up

The Messenger and Instagram webhook lifecycle — verification handshake, field subscriptions, and signed message delivery — is entirely testable on your machine when the public URL forwards everything untouched. Create an endpoint, run relayers listen --forward localhost:3000, filter to inbound messages with JQ, and debug real bot conversations locally with signatures intact.

Get started at relayers.app and download the app to build your first Meta messaging endpoint in minutes.