# Webhooks

> Receive Broadcast events in your own app, verify their signatures, handle retries and duplicates, and debug an endpoint that stops responding.

Source: https://sendbroadcast.net/university/developers/webhooks

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

Same word, opposite directions.

## What you can listen for

Events come in families:

- **Email events**, such as `email.sent`, `email.delivered`, `email.bounced`, `email.complained`, `email.opened`, `email.clicked` and `email.failed`.
- **Subscriber events**, such as `subscriber.created`, `subscriber.updated`, `subscriber.unsubscribed` and `subscriber.preferences_updated`.
- **Broadcast events**, such as `broadcast.sent`, `broadcast.failed` and `broadcast.paused`.
- **Sequence events**, such as `sequence.subscriber_added` and `sequence.subscriber_completed`.
- **System events**: `test.webhook`, and `message.attempt.exhausted` when 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

1. Open **Webhook Endpoints** in the sidebar.
2. Add a new endpoint with your HTTPS URL.
3. Tick the event types you want.
4. 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.

![The Create New Webhook Endpoint form with Active, Webhook URL, Description and Event Types fields](https://sendbroadcast.net/assets/docs/webhooks/webhooks-new-form-e1aa6737.png)

The form for a new endpoint. Tick only the events you use.

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:

1. Read the raw request body as a string. Do not parse and re-serialize it first.
2. Build the signed string: the timestamp, a dot, then the body.
3. Compute HMAC-SHA256 of that string with your secret, encoded as base64.
4. Take the part of the signature header after the comma (the format is `v1,`). 5. Compare the two with a constant-time comparison. 6. Reject requests whose timestamp is more than 5 minutes old, to stop replays. The [Webhooks](/docs/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.

1. **Verify** Check the signature
2. **Store** Save the event, skip a known `id`
3. **Return `200`** Within 30 seconds
4. **Work later** In a background job

Everything before the 200 happens inside the request, so keep it short.

 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.

1. **Healthy**
2. **Delayed** After 5 failed deliveries in a row
3. **Held** New events wait, and probes test the endpoint
4. **Healthy again** Held events go out, oldest first

How an endpoint goes down and comes back. Your email keeps sending the whole time.

 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: 1. The endpoint is active and subscribed to that event type. 2. The URL is public HTTPS, not `localhost`. 3. Your server returns a 2xx within 30 seconds. 4. 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](/how-to/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.

## Quiz
1. What must your endpoint return for Broadcast to count a delivery as successful?
   - Any response within 5 minutes
   - An HTTP status from 200 to 299 within 30 seconds (correct)
   - The event id in the response body
   Success is a 2xx status, and each request times out after 30 seconds.
2. What happens after 5 failed deliveries in a row to one endpoint?
   - The endpoint is deleted
   - Events are dropped until you resend them
   - Broadcast marks it Delayed, holds new events, and probes it until it responds (correct)
   - Broadcast stops sending email
   A Delayed endpoint has its events held, not lost, and gets probes until a delivery succeeds.
3. Where do you get the secret that verifies webhook signatures?
   - It is shown in full once, when you create the endpoint (correct)
   - It is your API access token
   - It is in /opt/broadcast/app/.env
   The secret is shown in full only at creation. After that it is redacted, so store it when you get it.
