DocumentationFundamentals

n8n WhatsApp Cloud API Webhook Setup

Receive WhatsApp Cloud API webhooks in self-hosted n8n: a stable public URL for Meta, the hub.challenge verify-token handshake answered automatically, and messages streaming into your workflow — no public IP or tunnel.

Meta's WhatsApp Cloud API only delivers webhooks to a public HTTPS URL, and before it sends anything it runs a verification handshake — a GET request your endpoint must answer by echoing the hub.challenge query value. That is two problems for self-hosted n8n: your instance usually is not public, and tunnels with rotating URLs force you to re-verify in the Meta dashboard every time the URL changes.

This tutorial solves both with a stable Webhook Relay endpoint: Meta verifies one permanent URL (the handshake is answered by a tiny Function, automatically), and your n8n instance receives every message over an outbound WebSocket — no public IP, no open ports, nothing to re-verify.

Meta ──GET hub.challenge──▶ Webhook Relay (Function answers handshake)
Meta ──POST messages──────▶ Webhook Relay (stable public URL)
        n8n ──outbound WebSocket─┘   ← n8n connects out; nothing inbound

1. Install the Webhook Relay node in n8n

In n8n go to Settings → Community Nodes → Install and enter n8n-nodes-webhookrelay. Then create an API key at my.webhookrelay.com/tokens and add it as a Webhook Relay API credential. (The general setup, with screenshots, is in the n8n Webhook Trigger tutorial.)

2. Create a bucket for WhatsApp

In my.webhookrelay.com create a bucket named whatsapp. Its default input URL — https://xxx.hooks.webhookrelay.com/... — is permanent. This is the URL Meta will verify once and deliver to forever.

3. Answer the verify-token handshake with a Function

Meta's verification GET must be answered with the raw hub.challenge value. Attach this Function to the bucket's input so the handshake is handled at the edge (n8n never needs to see it):

// Answer Meta's one-time subscription verification; let real events through.
var VERIFY_TOKEN = "my-verify-token"; // must match what you enter in Meta

if (r.RequestMethod() === "GET") {
  if (r.RequestQuery("hub.verify_token") === VERIFY_TOKEN) {
    r.SetResponseStatusCode(200);
    r.SetResponseBody(r.RequestQuery("hub.challenge"));
  } else {
    r.SetResponseStatusCode(403);
    r.SetResponseBody("verification failed");
  }
}

POST deliveries (the actual messages) pass through untouched and get the bucket's normal 200 response, which is all Meta needs.

4. Point Meta at the bucket

In the Meta App Dashboard: WhatsApp → Configuration → Webhook → Edit. Enter the bucket's input URL as the Callback URL and the same verify token as in the Function, then save — verification succeeds instantly. Click Manage and subscribe to the messages webhook field.

5. Receive messages in n8n

Add the Webhook Relay Trigger node to a workflow, select the whatsapp bucket, and activate. The node opens an outbound WebSocket and every WhatsApp delivery arrives as a workflow execution.

Messages sit a few levels deep in Meta's envelope. A Set or Code node right after the trigger tidies that up:

// Code node: flatten the Cloud API envelope into one item per message
const out = [];
for (const entry of $json.body.entry || []) {
  for (const change of entry.changes || []) {
    for (const msg of change.value?.messages || []) {
      out.push({ json: {
        from: msg.from,
        type: msg.type,
        text: msg.text?.body,
        timestamp: msg.timestamp,
        contact: change.value.contacts?.[0]?.profile?.name,
      }});
    }
  }
}
return out;

Status updates (sent/delivered/read) arrive on the same bucket with a statuses array instead of messages — filter on what is present.

Notes

  • Restarts are free. Meta verified the bucket URL, not your n8n instance — redeploy or move n8n anytime, nothing to re-verify.
  • Verify signatures if you need authenticity: Meta signs POSTs with X-Hub-Signature-256 (HMAC-SHA256 of the raw body with your app secret). The header reaches your workflow intact, so you can check it in a Code node — see Verify a webhook signature.
  • Payload reference: the full setup-and-payload story (including self-hosted gateways like WAHA) is in WhatsApp Cloud API webhooks: setup, verify token and payloads.
Did this page help you?