Stripe, LiveHeats, Frame.io, Resend, Twilio, push, Sentry, PostHog, Mapbox
Integrations
All configured through environment variables.
Stripe — platform subscriptions
One Stripe flow: the workspace pays Surflink. Plans and per-seat prices live in src/lib/stripe.js with price ids from env; only the month interval is accepted. POST /api/billing/subscribe builds line items (base + seats) and opens Checkout tagged with metadata.kind: 'platform' and the org_id; POST /api/billing/portal opens the Customer Portal from the org's stored customer id. The webhook keeps org_subscriptions in sync; seats are the quantity of the seat price item only. There is no free trial; the daily billing cron expires the trials that already existed and warns owners three days out; super admins grant access without charge through an audited console action. A workspace never gets a second subscription: subscribing again switches the existing one's plan or opens the portal. Configure the webhook endpoint (/api/stripe/webhook) in the Stripe dashboard.
LiveHeats — competition data
The federation results system (used by the BSA) is the source of truth for results, events and standings. Coaches import on demand; three crons refresh the caches (daily athlete/circuit sync, weekly series roster re-cache, weekly BSA rebuild). Every view stamps the newest imported ranking time. The WSL athlete index is kept as a frozen cache with best-effort lookups and no scheduled refresh.
Frame.io — video import
Adobe Frame.io V4 via Adobe IMS OAuth (src/lib/frameio.js). Coaches connect from the Upload page, browse workspaces / projects / folders and import up to 10 video assets per batch. Tokens are stored in HTTP-only cookies; the OAuth state is verified.
Resend — email
Transactional email for invites, reminders, digests and the email channel of notifications. Every send is guarded by RESEND_API_KEY; without it, invite links are surfaced in the UI to copy.
Twilio — SMS
The SMS channel for opt-in, urgent notification types (session reminder, starting soon, payment failed), with phone verification (/api/notifications/sms/start + /verify) and a signed inbound webhook.
Push — APNs and Web Push
The delivery worker (src/lib/notificationDelivery.js, drained by /api/notifications/deliver and the every-minute cron) sends APNs pushes to iPhone tokens (JWT signed via jose, src/lib/push/apns.js) and VAPID Web Push to browser subscriptions (src/lib/push/webpush.js). App tokens are registered through /api/push/register; a browser saves its Web Push subscription itself (src/lib/webPushClient.js). APNs needs the APNS_* variables.
Sentry and PostHog
Sentry receives server and browser errors (and CSP reports); every cron carries a Sentry monitor. PostHog records product analytics (no-op when unset) and carries the Send-to-TV feature flag.
Mapbox
GPS track maps on wearable session pages (NEXT_PUBLIC_MAPBOX_TOKEN).
Gemini
Guided-review voice-note transcription only — see SurfVision Pipeline.