/api/public
No session — 9 routes reachable by anyone holding the right link.
The prefix is the whole access rule: everything here is either public by nature or addressed by an unguessable id. A customer scanning a poster has no session and no way to get one.
Enrollment
Each call answers at two addresses — the UUID that every printed QR already carries, and the slug pair a poster can print in words.
| Method | Path | |
|---|---|---|
GET | /api/public/programs/{programId}/enroll | The form: fields, colours, consent text, the offer |
GET | /api/public/tenants/{tenantSlug}/programs/{programSlug}/enroll | The same by slug |
POST | /api/public/programs/{programId}/enroll | Take a card. 200, not 201 |
POST | /api/public/tenants/{tenantSlug}/programs/{programSlug}/enroll | The same by slug |
Enrollment is idempotent on (tenant, phone, program): scanning the poster twice hands back the
same card with the same stamps on it, and alreadyEnrolled says which happened.
A TICKET program's form only works when the merchant turned on self-issuance. When they have not,
it answers the same 404 as a program with no form at all — a distinct error would tell anyone
holding the URL that they nearly minted themselves a free ticket.
A ticket sold through a booking system does not use this surface at all. See the partner guide.
Install links
| Method | Path | |
|---|---|---|
GET | /api/public/passes/{serial}.pkpass?t=<token> | The signed Apple pass file |
GET | /api/public/passes/{serial}/google?t=<token> | 302 to Google Wallet's save flow |
A wrong or missing t is a 404, not a 401 — a photographed QR must not confirm that a pass
exists. Both URLs are returned by enrollment and by partner issuance; they carry the pass's auth
token, so they belong to one customer only.
Public assets
| Method | Path | |
|---|---|---|
GET | /api/public/tenants/{slug}/page | The merchant's public business page |
GET | /api/public/tenants/{slug}/images/{imageId}/v{version}.png | Logo and cover |
GET | /api/public/programs/{programId}/images/{imageId}/v{version}.png | Card artwork |
The v{version} segment is there to change: images are served immutable with a one-year cache, so
an edit becomes a new URL rather than a stale phone.
| Method | Path | |
|---|---|---|
POST | /api/public/google/callback | Google tells us a pass was saved or removed |
The Google callback is the only signal Google gives that an object exists — a pass can live its whole life issued and never installed, and that gap is a merchant metric rather than an error.