What is webhook fan-out? Send one webhook to many endpoints

Webhook fan-out is delivering one incoming webhook to several destinations. The provider sends each event once, to one URL, and a fan-out layer copies it to every service that needs it: production and staging, a data warehouse, a Slack channel, a teammate's laptop. Each destination receives the same payload and is delivered and retried independently.

                       ┌──▶ https://api.example.com/webhooks         (production)
Stripe ──▶ one URL ────┼──▶ https://staging.example.com/webhooks     (staging)
                       ├──▶ http://localhost:3000/webhooks           (developer laptop)
                       └──▶ Slack #payments                          (people)

Why fan-out is needed

  • One URL per provider. Many providers allow a single webhook URL per account or app, or a small number. If two services need the same events, something has to copy them.
  • Separate concerns. Billing, analytics and notifications can each react to invoice.paid without one service calling the others.
  • Safer testing. Send real production events to staging or a local build alongside production, without changing what production receives.
  • Easy changes. Add or remove a consumer without editing the provider's settings or rotating its secret.

Ways to implement fan-out

ApproachHowTrade-offs
Loop in your endpointReceive, then POST to each destinationSimple, but one slow destination slows the response and a failure mid-loop is hard to retry
Queue or pub/subEndpoint publishes to SNS, Pub/Sub, Kafka or EventBridge; each consumer subscribesReliable and scalable, but you run the infrastructure and the endpoint
Webhook gatewayProvider sends to the gateway, which delivers to each configured destinationNo code; per-destination retries and logs built in

The naive loop, and why it breaks

app.post('/webhooks', async (req, res) => {
  for (const url of DESTINATIONS) {
    await fetch(url, { method: 'POST', headers: req.headers, body: req.rawBody })  // ✗
  }
  res.sendStatus(200)
})

If the third destination hangs, the provider times out and retries, and destinations one and two get the event again. If the third returns 502, there's no record of it to retry. Fan-out needs a stored copy of the event and an independent delivery per destination.

Getting fan-out right

  1. Acknowledge the provider first. Store the event, return 2xx, then fan out asynchronously.
  2. Independent delivery per destination. Each gets its own timeout, retry schedule and status. A failing staging server must not affect production.
  3. Pass the body through unchanged so each destination can still verify the provider's signature. Strip or replace headers only on purpose.
  4. Make every consumer idempotent. Each destination can see retries independently.
  5. Filter per destination. Not every consumer needs every event type. Forwarding rules or a transform can drop the rest.
  6. Watch per-destination health so a dead consumer is noticed instead of silently failing.

Fan-out with Webhook Relay

A Webhook Relay bucket has one input URL and can deliver to up to 200 destinations. A destination can be a public HTTPS endpoint, a server on localhost or a private network reached through the relay agent, or a notification channel such as Slack, Discord, Telegram, Microsoft Teams or email. Each output has its own durable retries, forwarding rules and optional transform function, and every delivery shows up in the logs. Several developers can also run relay forward against the same bucket, and every connected agent receives every webhook.

For sending your own events to many customer endpoints, the same idea in reverse, the Outbound Gateway fans each published message out to every endpoint subscribed to its event type, and fan-out doesn't count as extra messages. For a worked example on the receiving side, see Mailgun webhook fan-out.

Frequently asked questions

What is webhook fan-out?

Webhook fan-out is delivering a single incoming webhook to several destinations. The provider sends the event once to one URL, and a fan-out layer copies it to every service that needs it, such as production, staging, an analytics pipeline and a Slack channel.

Why do I need webhook fan-out?

Many providers allow only one webhook URL per account, app or event type, or limit how many you can register. Fan-out lets several services receive the same events without changing the provider's settings, and lets you add or remove consumers without touching the provider.

How do I fan out a webhook to multiple URLs?

Point the provider at one URL you control, then forward each request to every destination. You can do this with your own endpoint that writes to a queue or pub/sub topic, with a cloud service such as SNS or Pub/Sub, or with a webhook gateway that supports multiple destinations per URL.

What happens if one fan-out destination fails?

In a well-built fan-out, nothing happens to the others. Each destination should have its own delivery, timeout and retry state, so one slow or failing endpoint cannot block or duplicate deliveries to the rest.