# Webhooks (/docs/webhooks)



Webhooks send changes to your application without polling.

## Set up an endpoint [#set-up-an-endpoint]

Open **Webhooks → New endpoint** in the [dashboard](/dashboard/webhooks).
Enter your HTTPS URL, choose the event types you need and save the signing secret
as `EVENTLAYER_WEBHOOK_SECRET` on your server.

## Verify an incoming event [#verify-an-incoming-event]

```ts
import { verifyWebhook } from "eventlayer";

const event = await verifyWebhook({
  body: await request.text(),
  headers: request.headers,
  secret: process.env.EVENTLAYER_WEBHOOK_SECRET!,
});

if (event.event_type === "ticket.checked_in") {
  console.log(event.data.id);
}
```

Use the raw request body. The SDK verifies the signature and timestamp before
returning a typed event. For a complete handler, follow
[React to ticket changes](/docs/guides/webhooks).

## Event types [#event-types]

| Event                 | Triggered when                       |
| --------------------- | ------------------------------------ |
| `event.created`       | A new event is created.              |
| `event.updated`       | An event is updated.                 |
| `event.deleted`       | An event is deleted.                 |
| `attendee.created`    | A new attendee is added to an event. |
| `attendee.updated`    | An attendee is updated.              |
| `attendee.deleted`    | An attendee is removed.              |
| `ticket_type.created` | A new ticket type is created.        |
| `ticket_type.updated` | A ticket type is updated.            |
| `ticket_type.deleted` | A ticket type is deleted.            |
| `ticket.created`      | A new ticket is issued.              |
| `ticket.updated`      | A ticket is updated.                 |
| `ticket.deleted`      | A ticket is deleted.                 |
| `ticket.assigned`     | A ticket is assigned to an attendee. |
| `ticket.checked_in`   | An attendee checks in.               |

## Payload [#payload]

Each event contains `id`, `event_type`, `timestamp`, `attempt`
and `data`. The data contains the affected resource; deletion events contain
only its ID.

## Handle duplicates [#handle-duplicates]

Store the verified `event.id` with your processing result. Skip events you
have already processed so retries and manual replays do not repeat side effects.
Return `2xx` after durably accepting the event. Delivery order is not guaranteed.

Automatic retries keep the same event and delivery IDs. A manual replay creates
a new delivery ID but keeps the original event ID and resource payload.
`Webhook-Delivery-Id` identifies a delivery, not a unique business change.

## Retry behavior [#retry-behavior]

Network failures, timeouts, rate limits, and server errors are retried with
backoff. Redirects and most other client errors are treated as permanent
failures. Retryable HTTP responses are `408`, `425`, `429`, and `5xx`.
Retries are scheduled after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours,
and 10 hours, for up to seven attempts in total. Worker availability may delay
an attempt beyond its scheduled time. After the retry schedule is exhausted,
the delivery is marked `failed`.

## Inspect failures and replay [#inspect-failures-and-replay]

Open **Webhooks → Delivery log** in the dashboard to see attempts, HTTP responses
and the next retry time. Select a delivery to investigate it, then use **Retry**
or **Resend** after fixing the cause. Replays use the endpoint’s current URL.

## Signature details [#signature-details]

For consumers without the SDK, verify HMAC-SHA256 over
`Webhook-Timestamp + "." + raw_request_body` using your endpoint signing secret.
Compare against `v1` values in `Webhook-Signature` in constant time and reject
timestamps more than five minutes from the current time.

Secret rotation has a 24-hour overlap. During it, the header can contain multiple
signatures; accept one that matches a trusted secret. Manage endpoints and rotate
secrets in the dashboard.
