API Reference
Route handlers live under src/app/api/ (plus src/app/auth/callback/route.js) — about 230 of them, of which /api/mobile/* are the iPhone's bearer-authenticated twins of web routes. Every route has an auth signal or is explicitly allow-listed with a justification in src/__tests__/api/auth-coverage.test.js, which fails CI otherwise.
| Route | Purpose |
|---|
POST /api/auth/signup | Create a coach account + workspace |
POST /api/auth/ensure-org | Provision a workspace for a coach who has none |
POST /api/auth/delete-account | Delete the current account |
GET /api/signup-mode | Open / invite-locked / waitlist, evaluating ?invite=CODE |
POST /api/waitlist | Record a request for access |
GET /api/auth/frameio, /callback | Frame.io (Adobe IMS) OAuth |
GET /auth/callback | Supabase auth callback |
GET /api/health, GET /api/time | Health probe; public wall-clock source for clock-offset measurement |
| Route | Purpose |
|---|
POST /api/clips/analyze | Sign a clip and submit it to SurfVision |
POST /api/clips/store, POST /api/sessions/store | Cache the processed clip / session and save stats |
GET /api/clips/status, POST /api/clips/cancel, POST /api/clips/probe | Poll, cancel, probe the source rotation |
POST /api/clips/native-ingest | iPhone capture upload |
GET /api/clips/sidecar | Access-checked overlay sidecar proxy |
DELETE /api/clips/[clipId] | Remove a clip and its storage objects |
GET/POST/PATCH /api/clips/[clipId]/maneuvers | Coach-corrected maneuver labels (a training set, not a shipped claim) |
DELETE /api/sessions/[id] | Hard-delete a session and its storage objects |
POST /api/training-sessions/batch-upload, GET ?batchId= | Idempotent batch prepare; resume |
GET /api/video/sign, GET /api/video/stream, GET /api/video/check | Signed URL (1h), byte-range proxy, source reachability check |
| Route | Purpose |
|---|
POST /api/reviews/[id]/suggest | Pre-seed keypoints from measured ride windows (no model call) |
POST /api/reviews/[id]/build | Build the review; transcribe voice notes (Gemini, best-effort) |
/api/mobile/sessions/[id]/reviews, /api/mobile/reviews/[id]/receipts, /replies | iPhone review read, receipts and voice replies |
| Route | Purpose |
|---|
POST /api/invite, POST /api/invite/send | Generate / email an athlete invite (athletes.create) |
GET /api/invite/lookup/[token] | Public, rate-limited, token-scoped preview |
POST /api/invite/claim, POST /api/invite/guardian | Athlete claims the login (server-side age gate) / guardian creates it |
POST /api/athletes/import | CSV roster import |
/api/athletes/[id]/coaches, /coaches/[scId], /permissions | Multi-coach assignments; effective permissions |
POST /api/athletes/[id]/merge/[b] | Merge duplicates (athletes.delete, audited) |
POST /api/athletes/[id]/resync, /guardian-consent | Re-import a provider-linked athlete; guardian consent |
| Route | Purpose |
|---|
GET/POST /api/training-sessions, GET/PATCH/DELETE /api/training-sessions/[id] | List, create, edit, delete |
/api/training-sessions/[id]/participants | Roster: add, attend, no-show, remove, waitlist promotion |
POST /api/training-sessions/import | CSV import of past sessions |
POST /api/training-sessions/provision-video | Create the footage session for a training session when filming starts |
| Route | Purpose |
|---|
POST /api/wearable/sessions, POST /api/wearable/sessions/live, /api/wearable/upload-url | Watch session upload, live registration, sample upload |
GET/DELETE /api/wearable/sessions/[id], POST /rederive | Read / delete (coach or admin) / re-run detectors |
GET /api/wearable/my-schedule, GET /api/wearable/who-is-surfing, GET /api/wearable/roster | The wearer's day-of sessions for the watch; who is live for the capture screen; the coach's assigned roster as the capture fallback |
GET/POST/DELETE /api/wearable/labels, GET /api/wearable/accuracy | Ground-truth labels; detector precision / recall |
GET/POST/PATCH /api/wearable/sync, GET /api/wearable/highlights, POST /api/wearable/import, GET /api/wearable/samples/[id] | Clip ↔ watch alignment, per-ride highlight definitions, GPX import, raw samples |
| Route | Purpose |
|---|
/api/liveheats/athletes/search, /athletes/[id], /series, /import-rankings | LiveHeats athlete search, profile, series, rankings import |
POST /api/liveheats/circuits/[id]/import-standings | Import a circuit's events, results and rankings |
/api/liveheats/series/[id]/athletes, /divisions, /api/liveheats/events/[id], /bulk-add, /api/liveheats/organisations, /sync | Series field (Browse Roster), divisions, events, organisation cache |
/api/athlete-search/search, /[source]/[id], POST /api/athlete-search/import, GET /api/discover | Multi-source search (LiveHeats + the frozen WSL index) and import into the roster |
/api/wsl/athletes/search, /[id], /import-results | WSL index lookup and per-athlete results import (best-effort, no schedule) |
GET/POST /api/circuits/[id]/events, /[eventId] | Manual event edits on a circuit |
GET /api/strategy/[athleteId], POST/DELETE /goals | The standings view and the qualification goal |
/api/opponents, /api/opponents/suggestions | Opponent notes; same-division suggestions with head-to-head |
| Route | Purpose |
|---|
GET/POST /api/conversations, GET /api/conversations/recipients | Coach ↔ athlete conversations (anything else is refused with 400) |
GET /api/messages/attachment | Resolve a private photo attachment to a short-lived signed URL (participants only) |
POST /api/notifications/send, POST /api/notifications/deliver | Internal: create + fan out; drain the queue (service-role auth) |
/api/notifications/sms/start, /verify, POST /inbound, GET/POST /api/notifications/unsubscribe | Phone verification, Twilio inbound webhook, one-click unsubscribe |
POST/DELETE /api/push/register | Register or remove an app's push token (APNs for the iPhone; browsers save their Web Push subscription themselves). Registering a token takes it from any other account on this device |
| Route | Purpose |
|---|
/api/orgs, /api/orgs/[id] | List / create workspaces; read / update (incl. type change) |
/api/orgs/[id]/members, /[memberId], /reset-password, /invite, /invites/[inviteId], /transfer-ownership | Members, invites (seat-capped), password-reset email, ownership |
/api/orgs/[id]/roles, /[roleId], PATCH /api/orgs/[id]/modules | Roles CRUD; module toggles |
GET /api/orgs/[id]/calendar, GET /coach-directory, GET /audit | Team calendar range; name-only coach picker; audit log |
GET /api/orgs/invite/[token], POST /accept | Member invite preview and acceptance |
GET/PATCH /api/coaches/[id], /athletes, /athletes/reassign, /stats, /activity | The Coaches view |
POST /api/billing/subscribe, POST /api/billing/portal, and the /api/mobile/billing twins | Stripe Checkout (monthly only) and Customer Portal |
POST /api/stripe/webhook | Platform subscription events |
| Route | Purpose |
|---|
POST /api/tv/signin/start, /poll, /claim | Device-code sign-in: the TV starts and polls (public, rate-limited); a signed-in user claims |
GET /api/tv/devices, PATCH/DELETE /api/tv/devices/[id] | The workspace's TVs; rename or sign out |
/api/admin/metrics, /live-metrics, /activity, /audit, /orgs, /orgs/[id], /users, /users/[id], /impersonate, /impersonate/stop, /invites, /invites/[id], /waitlist, /waitlist/[id]/invite, /settings, /subscriptions, /subscriptions/grant, /message-reports, /notifications — mirrored under /api/mobile/admin/*. POST /api/activity/log records the activity events the Live page reads.
Fourteen routes under /api/cron/*, each requiring the CRON_SECRET bearer — see Cron Jobs.