Pass API

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
CasingcamelCase on the wire, in both directions
TimestampsISO-8601 instants in UTC (2026-09-04T11:00:00Z). Wall-clock rendering is Asia/Ulaanbaatar and happens on the card, not on the wire
IdsUUID strings
MoneyMongolian tugrik, integer minor-unit-free
ErrorsNot 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, display message. message is English prose aimed at a developer and may be reworded without notice. error is only the HTTP reason phrase — there is no machine-readable error code anywhere in this API.
  • errors[] carries field and defaultMessage, and appears for both bean-validation failures and domain-rule refusals. Spring writes nested paths as milestones[0].value.
  • correlationId is echoed on every response as the X-Correlation-Id header too. Quote it in a bug report and the backend can pull every log line the request produced.
  • On a 5xx the message is replaced with Unexpected server error and errors is dropped — server internals never cross the wire. The correlationId still comes back.

The five surfaces

Each prefix is its own access rule, enforced by path rather than by per-endpoint annotations.

PrefixWho calls itCredential
/api/partnerA merchant's own booking systemTenant API key, Authorization: Bearer
/api/dashboardThe merchant web dashboardSession cookie, OWNER or MANAGER
/api/scanThe staff scanner appSession cookie from a paired device, any role
/api/publicAnyone — enrollment pages, install links, imagesNone, or an unguessable id plus a token
/api/apple, /api/adminApple's PassKit web service, Google's callback, platform staffPass auth token / shared secret / platform admin session

Building a merchant integration? Only /api/partner concerns you. Start at the partner guide.

On this page