Contract version and migration
Exact authentication, origin, status, and compatibility changes for first-party callers.
Independent version
The public contract is 2.0.0 for the current local deployment candidate. It is independent from package versions,
commit SHAs, Railway deployment ids, and the values returned by /version or
/api/version.
1.0.0was the breaking authentication cutover from Supabase sessions to Better Auth.1.1.0adds the concrete first-party domain operations without removing the five legacy paths.1.4.0adds published pass forms and aggregate form analytics, organiser event discovery, schedules, exhibition checkpoints and assignment replacement, and account push-subscription endpoints.2.0.0removes the merchant storefront, stall, and customer-order contract. Paid ticket checkout, provider callbacks, refunds, and historical financial records remain supported. MATRADE exhibitor reporting retains its legacy backing data without restoring merchant operations.
Caller migration
- Discard old Supabase
sb-…-auth-tokencookies and sign in again through the first-party same-host Better Auth proxy under/api/auth. Browser clients must not switch authentication calls to a separate API hostname; same-host transport keeps host-only local and isolated-preview cookies usable. - Browser calls send credentials so the current surface cookie is included.
- Server-side first-party domain callers forward the incoming
Cookie, setOriginto their exact configured frontend origin, and setx-xperience-originto the same value. - Never construct a session cookie name. HTTPS adds
__Secure-; the remaining name isvigor-<encoded project>-<encoded API host>.session_token, where each configured value uses a byte-length-prefixed lossless encoding. HTTP omits__Secure-. - Treat
401as missing/expired identity and403as an untrusted mutation origin or insufficient live organiser, platform, or scanner scope. - Do not require every successful mutation to return
200: direct NestPOSToperations return201by default. The preservedPOST /api/scancompatibility operation returns200;PATCHandDELETEreturn200.
The API rejects request bodies over 256 KiB with 413. The single poster
import operation, POST /api/discovery/studio/listings/import-poster, has a
12 MiB transport ceiling because its concrete schema carries bounded image
bytes. Narrower schema limits still apply: notably /api/scan accepts at most
500 scans per request and the first-party client batches 200.
The API-wide limit is 100 requests per minute per limiter key. Exceeding it
returns 429; callers should back off rather than retrying immediately.
Preserved paths
| Legacy operation | Canonical ownership | Migration |
|---|---|---|
GET /api/scan, POST /api/scan, DELETE /api/scan | Scanner module | Keep the path and payload. checkedIn is optional when no scans are processed. |
GET /api/me/export | Account module | Keep the path and payload; the API itself now sets the attachment and private no-store headers. |
GET /api/studio/stats/{id} | Partner module | Keep polling the same three integer fields directly from the API. |
GET /api/version | API bootstrap compatibility route | Keep treating the string as opaque; use GET /version for structured build metadata. |
GET /api/search | API proxy to the docs search handler | Keep query and `scope=guides |
The new deployment topology and intended hosts are configuration, not evidence that a production cutover has occurred.
Unlimited exhibition registration (1.3.0)
Event capacity and ticket-type quantity accept and return null for unlimited registration. Positive integers retain their existing limits. Handle null explicitly when displaying availability; do not convert it to zero or a sold-out state. Workshop creation continues to require a finite capacity. Availability requires target migration 0006_unlimited_event_capacity.sql and a verified deployment.
Pass forms, schedules, and push (1.4.0 history)
GET /api/events/pass-form returns the published version and bounded questions
for a selected public ticket. GET /api/exhibition/pass-configuration and
POST /api/exhibition/pass-configuration manage organiser drafts, publication,
and zoneIds; GET /api/exhibition/form-analytics returns aggregate answers.
PATCH /api/partner/events/details carries optional ordered schedule slots.
POST /api/exhibition/checkpoint resolves a checkpoint and
POST /api/exhibition/assignments/replace replaces a member's assigned
zoneIds.
GET, POST, and DELETE /api/account/push manage the current user's browser
subscriptions. Saving requires all three VAPID settings and device permission;
notifications are generic alerts and the inbox remains durable. iOS web push
requires Home Screen installation. No external provider delivery has been
verified for this local candidate.
These additions describe the undeployed local candidate. They do not establish production availability or a completed migration.
Merchant commerce retirement (2.0.0 candidate)
The current candidate removes merchant/stall API routes, navigation, order analytics, and customer stall-order export fields. This is a breaking API change, so callers must stop using the retired merchant and order paths before adopting 2.0.0. Existing paid ticket checkout remains registration-backed: provider callbacks still settle ticket payments, issue tickets, and preserve refund reconciliation. Historical order and payment records must be retained until their financial retention and any pending provider outcomes are closed; they are not evidence that merchant operations remain available.
MATRADE exhibitor rows continue to use the legacy event/exhibitor backing data for country, space, and report calculations. That reporting path is separate from retired vendor storefront and order operations. This is an undeployed local candidate and does not establish production availability.