> ## Documentation Index
> Fetch the complete documentation index at: https://docs.passlet.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive signed pass lifecycle events, verify signatures, and track every delivery.

Webhooks push **pass lifecycle events** to your systems the moment they happen — no polling. Manage them on the **Webhooks** page (endpoint management is owner-only; members can inspect deliveries) or via the [webhooks API](/api-reference/overview).

## Events

| Event                | Fired when                                                                                                                                         |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pass.issued`        | A pass finished issuing and is live                                                                                                                |
| `pass.updated`       | Pass data or template version changed                                                                                                              |
| `pass.voided`        | A pass was voided                                                                                                                                  |
| `pass.failed`        | Issuance failed                                                                                                                                    |
| `pass.scanned`       | A scan was recorded — **every** decision fires (accepted, duplicate, unknown, ambiguous, denied), so you see the same signal the audit log records |
| `pass.scan.reversed` | An accepted scan was reversed                                                                                                                      |

## Setting up an endpoint

1. On **Webhooks**, click **Add webhook**.
2. Enter your HTTPS endpoint URL and select the events to subscribe to.
3. Save — the **signing secret** is shown **once**. Store it like a password; you need it to
   verify deliveries.
4. Use **Send test** to queue a test event and confirm your endpoint responds.

Endpoints can be edited, disabled/enabled, and deleted at any time.

## Verifying signatures

Every delivery is signed. The `X-Passlet-Signature` header has the form:

```
X-Passlet-Signature: t=1720000000,v1=5f8a…e3
```

* `t` — unix timestamp (seconds) when the delivery was signed.
* `v1` — hex HMAC-SHA256 of `"{t}.{rawBody}"` using your endpoint's signing secret.

To verify:

```ts theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const timestamp = Number(parts.t);
  // Reject replays older than 5 minutes
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(parts.v1, "hex"), Buffer.from(expected, "hex"));
}
```

<Warning>
  Compute the HMAC over the **raw request body** exactly as received — parse the JSON only
  after verification. Reject deliveries with timestamps outside a ±5 minute window.
</Warning>

## Delivery tracking and retries

The **Deliveries** section records every attempt with its event type, status (`PENDING` / `SUCCESS` / `FAILED`), attempt count, and last response code or error. Failed deliveries are retried automatically with backoff; you can also **redeliver** a specific delivery manually (console or `POST /v1/webhooks/deliveries/{id}/redeliver`).

Respond with a `2xx` quickly — do the heavy processing async on your side. Non-2xx responses and timeouts count as failures.

## Idempotency

Deliveries can arrive more than once (retries, redelivery). Use the delivery ID or the event's entity state to make your handler idempotent.
