- Webhooks & events
- Expert
- Developers
Webhook Fan-Out
Also called: Webhook relay · Event fan-out · Webhook broadcasting · Pub/sub for webhooks
Distributing one incoming WhatsApp webhook stream to several internal consumers — inbox, CRM, chatbot, analytics — through a broker or relay you control.
Reviewed
What is webhook fan-out?
Webhook fan-out is the layer that copies one incoming WhatsApp event stream to every internal system that needs it. Meta delivers your WhatsApp events to one callback URL per app, or to an alternate URL per WhatsApp Business Account or phone number if you use overrides. Most businesses, though, have several systems that need the same events: the shared team inbox shows conversations, the chatbot answers them, the CRM integration logs them against the customer, the analytics warehouse counts them and the campaign tool watches delivery statuses. Fan-out takes the single stream from Meta and gives each of those consumers its own copy.
The usual shape is a standard event-driven architecture: a thin webhook endpoint followed by a message broker. The endpoint verifies the signature, stores the payload and returns 200 — the webhook acknowledgement Meta needs. A relay then splits each POST into individual events and publishes them to topics by type — inbound messages, statuses, template updates, account updates. Each consumer has its own subscription and message queue, so it reads at its own pace, retries its own failures and can be added or removed without touching Meta's configuration. A topic with one queue per consumer, a log with consumer groups, or a fan-out exchange all implement the same idea.
Fan-out is not a work queue. In a work queue (competing consumers), each event goes to exactly one worker; in fan-out, every subscriber receives every event it subscribed to. It also differs from webhook override, which splits traffic at Meta by phone number or WABA — and only for fields such as messages, since template and account webhooks always go to the app's default URL. Override decides where Meta sends; fan-out decides who inside your stack receives.
Key takeaways
- Webhook fan-out takes the single WhatsApp event stream Meta sends you and gives each internal system its own copy.
- Meta calls one callback URL per app; overrides per WABA or phone number apply only to fields such as messages.
- Ingest once, split each payload into events, publish them by type and give every consumer its own queue.
- Fan-out is not a work queue: every subscriber receives every event it subscribed to, not one worker per event.
- Prefer one app with internal fan-out over several apps on one WABA, because Meta sends retries to every subscribed app.
Interactive stop
One webhook in, a copy for every consumer
Only agents with the required skill are eligible; the least busy of them wins.
Incoming · 0 left
- All 11 conversations have been routed.
Conversation 11 of 11 · Skills-based
msg C3 · button reply(needs analytics)goes toAnalytics
Skills: needs analytics, and only Analytics has it.
- Full
Shared inbox
3 of 3 open
- inbox
- msg A1 · textneeds inbox
- status B7 · deliveredneeds inbox
- msg C3 · button replyneeds inbox
Chatbot
2 of 3 open
- bot
- msg A1 · textneeds bot
- msg C3 · button replyneeds bot
- Full
CRM sync
2 of 2 open
- crm
- msg A1 · textneeds crm
- status B7 · deliveredneeds crm
Analytics
3 of 4 open
- analytics
- msg A1 · textneeds analytics
- status B7 · deliveredneeds analytics
- msg C3 · button replyneeds analytics
Waiting · 1
- msg C3 · button replyneeds crm
| Strategy | Routed | Waiting | Skill mismatches |
|---|---|---|---|
| Skills-based | 10 | 1 | 0 |
| Round-robin | 11 | 0 | 5 |
Conversation 11 of 11, msg C3 · button reply: Skills: needs analytics, and only Analytics has it.
Why does webhook fan-out matter?
Without fan-out, teams either chain consumers inside the webhook handler — so the slowest system sets the response time and one outage blocks everyone — or subscribe several apps to the same WABA, where Meta notes that retries go to every subscribed app, so one app's failures create duplicates for the others. A broker in the middle isolates consumers from each other and from Meta: the CRM can be down for an hour while the inbox and chatbot carry on, and the CRM simply drains its own backlog when it returns.
How does webhook fan-out work?
Ingest once
One endpoint receives every POST, verifies
X-Hub-Signature-256, writes the raw payload durably and returns 200. Nothing downstream runs inside the request.Split into events
The relay breaks each payload into one event per message, status or change, adding an event key (such as wamid plus status), the
field, thephone_number_idand a received-at time.Publish by event type
Send each event to a topic such as
whatsapp.message.inbound,whatsapp.status,whatsapp.templateorwhatsapp.account. Use the customer's ID as the partition or ordering key so one person's events stay in sequence.Give each consumer its own subscription
The inbox subscribes to inbound messages and statuses, the chatbot to inbound messages only, analytics to everything. Each subscription is an independent queue with its own retries and dead-letter queue.
Make every consumer idempotent
Brokers deliver at least once and Meta retries, so each consumer applies webhook deduplication on the event key before its side effects run.
Watch lag per consumer
Alert on each subscription's backlog and oldest-message age. A growing lag on one consumer is a local problem, not an outage — as long as you notice it.
Example: A Chennai retailer's CRM outage
A Chennai electronics retailer fans its WhatsApp events out to a shared inbox, a chatbot, a CRM sync and a data warehouse. One afternoon the CRM vendor has a three-hour outage. The CRM subscription's queue grows to about 90,000 events, but the inbox keeps showing new chats, the chatbot keeps answering order questions and Meta keeps receiving instant 200s. When the CRM returns, its consumer drains the backlog in around 20 minutes, deduplicating anything it had half-processed before the outage (figures illustrative).
Webhook fan-out in code
import crypto from "node:crypto";const sha256 = (s) => crypto.createHash("sha256").update(s).digest("hex");const TOPIC_BY_KIND = {message: "whatsapp.message.inbound",status: "whatsapp.status",};export async function relay(payload) {for (const entry of payload.entry ?? []) {for (const change of entry.changes ?? []) {const v = change.value ?? {};const events = change.field === "messages"? [...(v.messages ?? []).map((m) => ({ kind: "message", key: "msg:" + m.id, user: m.from_user_id ?? m.from, data: m })),...(v.statuses ?? []).map((s) => ({ kind: "status", key: "st:" + s.id + ":" + s.status, user: s.recipient_user_id ?? s.recipient_id, data: s })),]: [{ kind: change.field, key: change.field + ":" + entry.id + ":" + sha256(JSON.stringify(v)), user: entry.id, data: v }];for (const e of events) {const topic = TOPIC_BY_KIND[e.kind] ?? "whatsapp." + e.kind;await broker.publish(topic, {orderingKey: e.user, // keeps one customer's events in sequenceeventKey: e.key, // consumers deduplicate on thisphoneNumberId: v.metadata?.phone_number_id,data: e.data,});}}}}
Split once, publish by type, and let every consumer subscribe to the topics it needs.
Common mistakes with webhook fan-out
Forwarding synchronously from the handler
POSTing each webhook on to four internal URLs before answering Meta makes the slowest consumer decide your response time. Publish to a broker and answer immediately.
Using a work queue and calling it fan-out
With competing consumers, each event reaches only one of them, so the CRM silently misses what the inbox processed. Give every consumer its own subscription.
Losing per-customer order
Publishing with no ordering key spreads one customer's messages across partitions, and a reply can be processed before the question. Partition by customer.
Multiplying personal data
Every copy of a message is another place customer data lives under the DPDP Act. Strip message bodies for consumers that only count, and set retention per subscription.
Webhook fan-out: key facts and limits
- Meta delivers to
- One app callback URL (overrides per WABA or number)
- Never overridable
- Template and account webhook fields
- Payload size
- Up to 3 MB, up to 1,000 updates
- Meta retry window
- Up to 7 days
- Consumer delivery
- At least once — deduplicate on event key
- Ordering key
- Customer ID (BSUID or wa_id)
Best practices for webhook fan-out
- Keep the raw payload archive separate from the fan-out, so you can replay a consumer from scratch.
- Version the event envelope you publish; consumers should not parse Meta's raw shape directly.
- Prefer one app with internal fan-out over several apps subscribed to the same WABA.
- Give the chatbot the tightest retry budget — a reply that arrives an hour late is worse than none.
Webhook Fan-Out FAQs
Can Meta send the same webhook to several URLs?+
How is fan-out different from webhook override?+
Should I subscribe several Meta apps to the same WABA instead of fanning out?+
Do I need a broker for two consumers?+
Talk to InfiQ
From definition to your first message
InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.