API overview
Express exposes a JSON API under /v1. Next.js calls it with an access token. The contract is OpenAPI 3.1. The source file in this documentation project is docs/api/openapi.yaml, and the site publishes that same file for download.
Conventions
| Topic | Rule |
|---|---|
| Base path | /v1 |
| Resource ids | UUID strings. |
| Money | Integers in cents plus "currency": "USD". |
| Times | ISO-8601 UTC. |
| Auth | Authorization: Bearer access token. Refresh tokens are posted only to /auth/refresh. |
| Idempotency | Idempotency-Key on booking creation and checkout. |
| Errors | { "error": { "code": "...", "message": "..." } } |
| Admin routes | Prefix /admin. Role ADMIN and, in production, a completed MFA check. |
The Stripe success and cancel URLs return the customer to Next.js. Those pages call GET /orders/{orderId} or the reconcile action. They do not post a client-side "payment succeeded" flag that the API trusts.
The webhook route is /v1/webhooks/stripe. It reads the raw body, verifies the Stripe signature, and uses the event id for deduplication. It does not accept an access token as proof of payment.
Response codes
| Code | When |
|---|---|
| 200 | Read or a repeated idempotent request. |
| 201 | A booking, gallery, or policy was created. |
| 202 | A refund or thumbnail job was queued. |
| 204 | Logout or a notification marked read. |
| 400 | Validation failed, including a bad rating or an untrusted price. |
| 401 | Missing or expired access token. |
| 403 | Authenticated, but not allowed. Includes gallery access still pending. |
| 404 | Missing, or hidden because it belongs to someone else. |
| 409 | Duplicate referral, duplicate rating, consumed token, or idempotency key reused with a different body. |
| 422 | The booking is in the wrong status for the decision. |
| 429 | Rate limit. |
Main resources
| Area | Operations |
|---|---|
| Auth | Login, refresh, logout, current user, account setup, password reset. |
| Catalog | Public list of active services. Admin create and update. |
| Bookings | Create, list, read. Admin decision. |
| Orders | Read. Create a Stripe checkout. |
| Webhooks | Stripe. |
| Galleries | Admin upload, publish, and access decision. Customer list, signed URL, rating. |
| Rewards | Own code and balance. Admin reward policy. |
| Notifications | List and mark read. |
| Audit | Admin read. |
Field names in JSON are camelCase. Database columns are snake_case. The generated Prisma Client maps between them. Every query uses DATABASE_URL.
Versioning
This document is version 0.1.0 of the contract, matching the USD edition of the design. Additive fields are allowed. Removing a field or changing a cents calculation is a new version and a migration note.