# Managing webhooks

> Subscribe an HTTPS endpoint to delivery and engagement events. SendPing POSTs a signed JSON payload to your URL and retries with backoff on failure.

Webhooks push email events to your own server in near-real time, so you do not have to poll. When an email is delivered, opened, clicked, bounces, or is complained about, SendPing sends an HTTP `POST` with a JSON body to the endpoint(s) you have configured.

Each delivery is signed so you can verify it really came from SendPing — see [Verify webhook requests](https://www.sendping.co/docs/webhooks/verify).

## Configuring an endpoint

1. **Add an endpoint** — In the dashboard, add a webhook with the HTTPS URL that should receive events. The URL must be publicly reachable over HTTPS and resolve to a public IP (private/loopback addresses are rejected).
2. **Choose events** — Select which events the endpoint should receive. Events you do not subscribe to are never delivered. See [Event types](https://www.sendping.co/docs/webhooks/events) for the full list.
3. **Save the signing secret** — A `whsec_` signing secret is generated for the endpoint and shown once. Store it securely — you will use it to verify every incoming request.
4. **Verify and go live** — Use the dashboard Test button to send a sample delivery, confirm your endpoint returns a 2xx, then start handling live events.

> **Warning:** Your endpoint must respond with a 2xx status. Any non-2xx response (or a timeout) counts as a failed delivery and is retried.

Webhooks can also be managed programmatically via the API — see [Create webhook](https://www.sendping.co/docs/api/webhooks-create), [List webhooks](https://www.sendping.co/docs/api/webhooks-list), and the retrieve/update/delete endpoints. The create call returns the `whsec_` signing secret in the response body; retrieve and list never do. If you lose it, `POST /webhooks/:id/rotate` issues a new one and invalidates the old.

## The payload

Each delivery is a JSON body in the envelope `{ "type", "created_at", "data" }`, where `data` holds the resource-specific fields for that event. See [Event types](https://www.sendping.co/docs/webhooks/events) for the envelope and a sample, and the per-event pages for each `data` shape.

## The signing secret

Each endpoint has its own signing secret. SendPing uses it to compute an HMAC-SHA256 signature over `"<X-SendPing-Timestamp>.<raw request body>"` and sends that in the `X-SendPing-Signature` header. Keep the secret server-side — anyone with it can forge events. The secret is stored encrypted at rest.

## Delivery, timeouts, and retries

Each delivery is a single `POST` with a 10-second timeout. A delivery is considered successful only on a 2xx response.

- On failure, SendPing retries — up to **5 attempts total** (the initial attempt plus 4 retries).
- Retries use an increasing backoff of roughly **1, 5, 30, then 120 minutes**.
- The attempt number is included in each request as the `X-SendPing-Attempt` header (starting at 1), so you can detect retries.
- An endpoint that fails **50 consecutive deliveries** is automatically disabled. Re-enable it from the dashboard once your endpoint is healthy.

> **Note:** Make your handler idempotent. Because failed deliveries retry and event transports can redeliver, your endpoint may occasionally receive the same event more than once.

## Duplicates, ordering, and exhausted retries

- **Duplicates are possible** — a timeout can occur after your server has processed a request. Deduplicate on the `svix-id` header, which stays stable across retries of a delivery. Persist accepted events before acknowledging them.
- **Retries are bounded** — a delivery can remain unsuccessful after the attempt limit, or while an endpoint is disabled. Monitor failures and reconcile important email states through the API; successful receipt of every event is not guaranteed.
- **No ordering guarantee** — network conditions and retries can change arrival order. Keep event history and apply event-aware state updates so a late event does not incorrectly reverse a later state. The payload timestamp helps investigation, but sorting received timestamps alone cannot recover a missing event or resolve every state transition.
