Lesson 4 9 min read Completed
Webhooks
Receive Broadcast events in your own app, verify their signatures, handle retries and duplicates, and debug an endpoint that stops responding.
Webhooks turn Broadcast from something your app calls into something that calls your app. In this lesson you choose the events you need, receive them safely, and build a handler that copes with retries, duplicates and downtime.
Two kinds of webhook
Do not mix these up. ESP webhooks come into Broadcast from your email provider, and report deliveries, bounces and complaints. The getting started course set those up. This lesson is about outgoing webhooks: events that Broadcast sends to your app.
ESP webhooks
Provider to Broadcast
- Deliveries, bounces, complaints
- Set up in the getting started course
Outgoing webhooks
Broadcast to your app
- Events you choose
- This lesson
What you can listen for
Events come in families:
- Email events, such as
email.sent,email.delivered,email.bounced,email.complained,email.opened,email.clickedandemail.failed. - Subscriber events, such as
subscriber.created,subscriber.updated,subscriber.unsubscribedandsubscriber.preferences_updated. - Broadcast events, such as
broadcast.sent,broadcast.failedandbroadcast.paused. - Sequence events, such as
sequence.subscriber_addedandsequence.subscriber_completed. - System events:
test.webhook, andmessage.attempt.exhaustedwhen every retry failed.
Subscribe to what you use and nothing more. A CRM sync wants subscriber events. An alert for a failed campaign wants broadcast.failed. Fewer events mean less load on both sides.
Two names that look alike: email.send_delayed means Broadcast has not sent a transactional email yet, because the server is at its hourly limit. email.delivery_delayed means the provider accepted the email and the receiving server is slow.
Step 1: Create the endpoint
- Open Webhook Endpoints in the sidebar.
- Add a new endpoint with your HTTPS URL.
- Tick the event types you want.
- Save, and store the secret now. It is shown in full only once. After that it is redacted, and it cannot be changed. If you lose it, create a new endpoint.

You can also manage endpoints in code with /api/v1/webhook_endpoints, and send a test with POST /api/v1/webhook_endpoints/:id/test.
Step 2: Verify every request
Your endpoint is a public URL, so anyone can post to it. Broadcast signs each payload with HMAC-SHA256. Each request carries three headers: broadcast-webhook-id, broadcast-webhook-timestamp and broadcast-webhook-signature.
To verify:
- Read the raw request body as a string. Do not parse and re-serialize it first.
- Build the signed string: the timestamp, a dot, then the body.
- Compute HMAC-SHA256 of that string with your secret, encoded as base64.
- Take the part of the signature header after the comma (the format is
v1,<signature>). - Compare the two with a constant-time comparison.
- Reject requests whose timestamp is more than 5 minutes old, to stop replays.
The Webhooks page has a complete Node.js example.
Step 3: Respond fast, work later
Every payload has the same envelope: id, type, created_at, a delivery object and the event data.
Broadcast counts a delivery as successful when your endpoint returns a status from 200 to 299 within 30 seconds. So do as little as possible in the request: verify, store the event, return 200, and do the real work in a background job.
- Verify Check the signature
- Store Save the event, skip a known
id - Return
200Within 30 seconds - Work later In a background job
Expect duplicates. A retry after a timeout can bring the same event twice. Use the event id to skip events you already handled.
Watch delivery.delayed and delivery.held_since. They tell you an event was held and arrived late. Check them before you act on an email.opened from hours ago as if it were fresh.
Retries and a Delayed endpoint
Broadcast tries each delivery up to 6 times: the first attempt, then retries after 5 minutes, 30 minutes, 2 hours, 5 hours and 10 hours. The schedule is the same for every endpoint. When all retries fail, Broadcast sends message.attempt.exhausted.
After 5 failed deliveries in a row, Broadcast marks the endpoint Delayed. It stops sending to it and holds new events, which do not use up their retries. It sends a probe after 1 minute, 5 minutes, 15 minutes and then every hour. Any successful delivery, or a successful Send Test, marks it healthy again. Held events then go out oldest first, at up to 10 per second. Held events older than 3 days are given up.
- Healthy
- Delayed After 5 failed deliveries in a row
- Held New events wait, and probes test the endpoint
- Healthy again Held events go out, oldest first
Webhooks never hold up sending. If your endpoint is down, your email still goes out.
Debug an endpoint
The Webhooks page shows a Healthy or Delayed badge per endpoint, a banner when one is not responding, and a Queued deliveries panel. Click an endpoint to see recent deliveries. Click a delivery to see the payload, your response and the retry details.
When nothing seems to arrive, check in this order:
- The endpoint is active and subscribed to that event type.
- The URL is public HTTPS, not
localhost. - Your server returns a 2xx within 30 seconds.
- Your signature check uses the raw body and the right secret.
For local development, point a tunnel such as ngrok at your machine, or use webhook.site to inspect payloads. See webhooks not firing.
What you learned
- Outgoing webhooks send Broadcast events to your app. Subscribe only to what you use.
- Store the secret at creation, and verify every request with HMAC-SHA256 on the raw body.
- Return a 2xx within 30 seconds, process later, and skip duplicate event ids.
- After 5 failures an endpoint is Delayed. Its events are held and sent once it responds.
Go deeper in the docs
Check what you learned
PassedAnswer all 3 questions correctly to complete this lesson.