Skip to main content

Webhook Fan-Out

Accept events on one webhook and deliver every event to several downstream endpoints. Each destination retries on its own, so a receiver that fails for a moment still gets every event, and the others are not held back or dropped.

Components: http_server input → mapping (adds received_at) → broker output with pattern: fan_out over three http_client outputs, each with retries.

Needs: HTTP endpoints you control that accept a JSON POST and answer 2xx. Each receives the full event.

Proved on a local-mode node

This job ran end to end on an expanso-edge v2.1.21 node in local mode, with three local test receivers. It was not deployed through Expanso Cloud, and no third-party service received the events.

Complete job

webhook-fan-out.yaml
name: webhook-fan-out
type: pipeline
selector:
match_labels:
pipeline_role: webhooks
config:
input:
http_server:
address: "127.0.0.1:8089"
path: /webhook
allowed_verbs: [POST]
pipeline:
processors:
- mapping: |
root = this
root.received_at = now()
output:
broker:
pattern: fan_out
outputs:
- http_client:
url: "https://RECEIVER_A_HOST/notify"
verb: POST
headers: {Content-Type: application/json}
retries: 5
retry_period: 200ms
- http_client:
url: "https://RECEIVER_B_HOST/notify"
verb: POST
headers: {Content-Type: application/json}
retries: 5
retry_period: 200ms
- http_client:
url: "https://RECEIVER_C_HOST/notify"
verb: POST
headers: {Content-Type: application/json}
retries: 5
retry_period: 200ms

The selector sends the job to a node labelled pipeline_role: webhooks; set that label on your node, or change the selector to a label it already carries. With no matching node, the job is stored and never runs.

Replace the three receiver URLs with yours, and add or remove http_client entries to match the number of destinations. The webhook listens on 127.0.0.1:8089 of the node that runs the job; change address to accept events from other hosts.

Send it test events

Deploy the job, then post events to the webhook from the node:

expanso-cli job deploy webhook-fan-out.yaml
for n in 1 2 3; do
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
-H 'Content-Type: application/json' \
--data "{\"id\":\"order-00$n\",\"amount_cents\":${n}000}" \
http://127.0.0.1:8089/webhook
done

Each post should print 200, and each receiver should get all three events, each with a received_at field added. The job keeps listening until you stop it.

What the run proved

  • The webhook accepted 10 of 10 events.
  • Each of the three receivers got each of the 10 events exactly once, with the payload unchanged.
  • One receiver answered 503 to two attempts. Those were retried, and it still received all 10 events.

Limits

  • Proved on a local-mode node only, not through Expanso Cloud.
  • The receivers were local test servers. Delivery into any particular SaaS product was not tested.
  • The job adds received_at and otherwise forwards the event as received. It does not verify webhook signatures; see HTTP Webhook Receiver for that pattern.