Events
Delivery payload
Every delivery is a JSONPOST with the same envelope:
id: unique delivery ID. Use it to deduplicate (see Idempotency).type: the event type from the table above.timestamp: when the delivery was created (ISO 8601).correlationId: ties the delivery to the API request or job that caused it,nullwhen there was no correlated caller. Quote it in support requests.data: the event’s subject.
What data contains
One rule across every event: data is the event’s subject, in the same shape the REST API uses
for that resource, carrying only state the event establishes.
data.idis always the thing the event is about: the pass for a lifecycle event, the scan for a scan event.- State the event merely observed is deliberately absent. Deliveries are retried, and an offline
scan report can sync hours after its operational scan time, so a pass label or a scan counter would arrive stale
while looking authoritative. Fetch what you need, or cache it from the event that set it, keyed by
externalPassId.
pass.issued
pass.updated
changedFields semantics
changedFields lists the top-level pass fields the update touched. It is not a value-level diff:
- A variable change is reported as
variables. Individual variable names are never listed; to find out which variable changed, fetch the pass (GET /v1/passes/{id}) and compare with your source system. redemptionandexpiresAtare always included whenever the update contained variables, because variables can drive the resolved redemption policy and expiry. Their presence does not guarantee those values actually changed.- A template upgrade reports
templateVersionId(together withvariables,redemption, andexpiresAt). Data updates do not, so this entry is the way to tell upgrades and data updates apart. - An update that writes identical values still fires the event, with the same
changedFields. - Treat unknown entries as possible and ignore ones you do not recognise. For example,
manual_requeueappears when an operator requeues an update job from the console.
pass.voided
pass.failed
pass.scanned
The scan event carries the Scan object as data, byte for byte what
GET /v1/scans/{id} returns. One parser handles the API and the webhook:
Per the rule above, the payload carries no pass label, status, or redemption
counters: those are pass state the scan only observed.
externalPassId is here
so you never need them to route the event.
metadata
Whatever the scanning client attached (note, or any other key), plus two keys
Passlet stamps itself:
conflict: present only on an offline-reported scan whose device verdict disagreed with what the server would have decided at report time. The value is the reason the server would have returned. The device’s verdict is what was acted on, soresultandreasonare never overwritten.validateAsOf: the operator’s validity-date override (YYYY-MM-DD), when one was in force. It explains why the operationalscannedAtdate can differ from the realrecordedAtdate.
webhook.test
Sent only when you use Send test on an endpoint, so you can confirm your receiver verifies
signatures and returns a 2xx before real traffic arrives. There is nothing to subscribe to, and it
never reports pass or scan state: data names the endpoint you tested and nothing else.
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, because you need it to verify deliveries.
- Use Send test to queue a test event and confirm your endpoint responds.
Verifying signatures
Every delivery is signed. TheX-Passlet-Signature header has the form:
t: unix timestamp (seconds) when the delivery was signed.v1: hex HMAC-SHA256 of"{t}.{rawBody}"using your endpoint’s signing secret.
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 and do the heavy processing asynchronously on your side. Non-2xx responses and timeouts count as failures.