Overview
The HTTP API of pass-service — five surfaces, four credentials, one context path.
pass-service puts loyalty cards and event tickets into Apple Wallet and Google Wallet. It signs
the pass, syncs it to the phone, notifies against it and expires it. It does not sell anything: no
inventory, no payments, no seat map.
This reference covers the wire — paths, credentials, status codes. For why the API is shaped
the way it is, the backend repo's CONTEXT.md and docs/adr/ are the source, and they are better
reading than anything here.
api-tracker is the contract of record
The authoritative description of what the backend serves is pass-service/api-tracker/ — one
dated markdown file per backend change set, newest last. Those files are spec, and spec does not
change once landed.
These pages are a navigable index over that surface, generated against the controllers and kept
deliberately thin on request and response schemas. Where this site and api-tracker/ disagree,
api-tracker/ is right. There is no springdoc, no openapi.json, and no generated client.
Conventions
Everything below applies to every surface.
| Context path | /api — every route on this site already includes it |
| Casing | camelCase on the wire, in both directions |
| Timestamps | ISO-8601 instants in UTC (2026-09-04T11:00:00Z). Wall-clock rendering is Asia/Ulaanbaatar and happens on the card, not on the wire |
| Ids | UUID strings |
| Money | Mongolian tugrik, integer minor-unit-free |
| Errors | Not RFC 7807 — see below |
A request that misses the /api prefix never reaches Spring at all: Tomcat answers with HTML, so
a dropped prefix surfaces as a JSON parse error rather than a 404. Check the status before parsing
a body.
Error bodies
{
"timestamp": "2026-08-26T04:29:35.323+00:00",
"status": 400,
"error": "Bad Request",
"path": "/api/partner/tickets",
"message": "a booked ticket needs the screening it admits to",
"errors": [{ "field": "startsAt", "defaultMessage": "must not be null" }],
"correlationId": "a6fea1fa-a384-40f2-8930-0017246402cf"
}- Branch on
status, displaymessage.messageis English prose aimed at a developer and may be reworded without notice.erroris only the HTTP reason phrase — there is no machine-readable error code anywhere in this API. errors[]carriesfieldanddefaultMessage, and appears for both bean-validation failures and domain-rule refusals. Spring writes nested paths asmilestones[0].value.correlationIdis echoed on every response as theX-Correlation-Idheader too. Quote it in a bug report and the backend can pull every log line the request produced.- On a 5xx the
messageis replaced withUnexpected server erroranderrorsis dropped — server internals never cross the wire. ThecorrelationIdstill comes back.
The five surfaces
Each prefix is its own access rule, enforced by path rather than by per-endpoint annotations.
| Prefix | Who calls it | Credential |
|---|---|---|
/api/partner | A merchant's own booking system | Tenant API key, Authorization: Bearer |
/api/dashboard | The merchant web dashboard | Session cookie, OWNER or MANAGER |
/api/scan | The staff scanner app | Session cookie from a paired device, any role |
/api/public | Anyone — enrollment pages, install links, images | None, or an unguessable id plus a token |
/api/apple, /api/admin | Apple's PassKit web service, Google's callback, platform staff | Pass auth token / shared secret / platform admin session |
Building a merchant integration? Only /api/partner concerns you. Start at the
partner guide.