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.
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
- On Webhooks, click Add webhook.
- Enter your HTTPS endpoint URL and select the events to subscribe to.
- Save — the signing secret is shown once. Store it like a password; you need it to
verify deliveries.
- 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:
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"));
}
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.
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.