Skip to main content
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

EventFired when
pass.issuedA pass finished issuing and is live
pass.updatedPass data or template version changed
pass.voidedA pass was voided
pass.failedIssuance failed
pass.scannedA scan was recorded — every decision fires (accepted, duplicate, unknown, ambiguous, denied), so you see the same signal the audit log records
pass.scan.reversedAn 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:
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.