Athletes Ocean

Building Athletes Ocean — From $5K to $70K+ MRR
The Numbers Tell the Story
| Metric | Start | Current | Growth |
|---|---|---|---|
| MRR (approx) | ~$5K | ~$71K | 14x |
| Monthly Orders | 663 | 1,172 | 1.8x |
| Users | ~7,500 | 14,899 | 2x |
| Teamspaces | ~45 | 533 | 12x |
| Teamspace Athletes | - | 3,590 | - |
| Videos | ~2,700 | 6,001 | 2.2x |
| Instructors | - | 141 | - |
| Products | - | 261 | - |
| Active Subscriptions | - | 1,182 | - |
| Commits | - | 4,032 | - |
The revenue trajectory was explosive: starting at $72K in gross order value, growing to $125K/month at peak, and stabilizing around $71K+. Teamspace creation went from ~10/month to 121 in a single month as the team collaboration model found product-market fit.
The Tech Stack
A true full-stack monorepo powering iOS, Android, Web, Admin, CRM, and Workers:
| Layer | Technology |
|---|---|
| Mobile + Web App | Expo SDK 52, React Native 0.76, Expo Router |
| Styling | NativeWind (Tailwind CSS) |
| State | Zustand + TanStack React Query |
| Backend API | Node.js, Express, TSOA (auto-generated routes + OpenAPI) |
| Database | PostgreSQL, Prisma ORM (59 models, 1,369-line schema) |
| Admin Dashboard | AdminJS with 70+ resource configs, custom React dashboards |
| Auth | Firebase Auth (client) + Firebase Admin (server) |
| Payments | Stripe (web), RevenueCat (iOS/Android in-app purchases) |
| Video | Mux (streaming + secure playback + transcription) |
| Storage | Cloudflare R2 |
| Chat | Stream Chat (real-time messaging) |
| Resend + React Email templates | |
| Error Tracking | Sentry (app + server) |
| Analytics | PostHog (web + mobile + server), GA4 |
| SEO | Server-side rendering for crawlers, structured data, sitemaps |
| Workers | Cloudflare Workers (CRM automation, image resizing) |
| CI/CD | GitHub Actions, EAS Build/Update, Lefthook |
Codebase size: ~787 app files, ~255 server files, ~142 admin files, 779 test files.
Feature Timeline — What We Built
Phase 1 — Foundation & Admin Panel
The starting point: $5K MRR, basic product. We kicked off with:
- AdminJS dashboard — full admin panel with user impersonation, product relations, reset password
- Prisma v6 migration — major ORM upgrade
- RevenueCat integration — user attributes for mobile subscription tracking
- Teamspace invitation emails — automated onboarding flow
- Free content previews on unpurchased products
Phase 2 — Instructor Payouts & Video Infrastructure
- Stripe Connect for instructors — automatic payout splits to content creators
- Bunny.net video sync — bulk video infrastructure management
- Purchase confirmation emails — transactional email system
- Cron: process-order-items — automated order processing pipeline
- Welcome emails for Apple subscribers
- Scripts to fix DB and Stripe data consistency
Phase 3 — Admin Metrics & User Experience
- Revenue Dashboard (AdminJS) — first custom dashboard with metrics
- Video progress tracking — resume-where-you-left-off across devices
- User feature flags — gradual rollout system
- Change email flow — multi-step email migration
- Seat management for teamspaces
- Instructor dashboard behind feature flag
Phase 4 — Subscription & Payout Overhaul
- Teamspace subscription migration — migrating legacy billing to new model
- Payout system — cost payouts, credit payouts, approval workflows
- Purchase without authentication — guest checkout increasing conversion
- Bundle upsell — cross-sell bundles at checkout
- Markdown support for product descriptions
- Teamspace users purchase email notifications
Phase 5 — AI & Push Notifications
- AI Chat (Conversation AI) — OpenAI-powered assistant for athletes
- Push notifications — Firebase Cloud Messaging for iOS/Android
- GTM checkout tracking
- Stripe session validation improvements
Phase 6 — Resend & Ads Platform
- Resend audience sync — split production/staging email lists
- Ad Tags system — instructor advertising and insights platform
- User deletion — GDPR-compliant account removal
- Default lesson/video item selection
Phase 7 — Store & Auth Hardening
- Store page — dedicated product browsing experience
- Firebase token refresh — hardened auth flow preventing expired sessions
- Intercom integration — customer support chat
- CMS Promos — promotional banners with scheduling
- Debounce search on homepage
Phase 8 — Calendar System
- Recurring calendar events — full RRULE support (daily, weekly, custom)
- Calendar templates — reusable practice plans
- Optimistic UI — mark assignments as completed with instant feedback
- Assigned users can mark items as done
- Repeat event validation and form helpers
Phase 9 — Teamspace Resources & Mobile Cart
- New calendar event types — expanded beyond practices (meetings, film sessions, etc.)
- Teamspace resources — file/document sharing
- Cart on mobile — in-app purchase cart experience
- Push notification deep links — tap notification to navigate to comment/product
- Teamspace invitation reminders
- Calendar form skeleton UI
Phase 10 — Search & Journal
- Global search — search across videos, instructors, teamspaces
- Journal timezone support — timezone-aware journal reminders
- Change password modal — in-app password management
- Instructor search and discovery
- Search on all content pages
Phase 11 — Notifications Engine (The Growth Inflection)
This was the phase where teamspaces exploded from 15/month to 121. Key features:
- Push notification batching — send to all teamspace members efficiently
- Daily summary emails — automated digest for teamspace owners
- Assignment due reminders — never miss a practice deadline
- Calendar reminders by timezone — respect athlete timezones globally
- Practice preview notifications — preview upcoming practices
- Sort calendar and purchased products
Phase 12 — Calendar Templates & Access Control
- Calendar template system — coaches create reusable practice plans
- Teamspace groups — organize athletes into squads/groups
- Chat access guard — only active teamspace users can access messaging
- Calendar link detail pages — deep-linkable calendar entries
- Rate-limited email sending
Phase 13 — Dashboards & Mux Migration
- Teamspace Dashboard (AdminJS) — comprehensive team analytics
- Mux video migration — moved from Bunny.net to Mux for professional video streaming
- Notion teamspace sync — sync teamspace data to Notion for reporting
- Last login tracking
- Emoji picker for web chat
Phase 14 — Product Bundles & Email Attachments
- Product bundles — package multiple products into purchasable bundles
- Purchase email attachments — send files (PDFs, guides) on purchase via R2
- Access gate — content gating for non-subscribers
- Draggable video lists — sortable video ordering
- Cloudflare R2 storage — file management for attachments
- Dropdown actions on web (replacing mobile-only swipe actions)
- Stripe webhook integration tests
Phase 15 — Video Upload & Invite Overhaul
- Multi-video upload — bulk upload APIs for coaches
- Video publish/unpublish — draft mode for videos
- Teamspace invite redesign — unified invite link + QR code modal
- Strikethrough pricing — show savings on pre-paid seats
- Video replay — recently watched videos in practice builder
- Product Manager (AdminJS) — complete product management interface
- Setup account page for new invitees
- Video integration tests
Phase 16 — CRM, SEO, Analytics & AI Video
- CRM system — full CRM with contacts, opportunities, event log, source attribution (6 new DB tables)
- Cloudflare Workers — CRM automation workers (lead intake, pre-call briefing, call intelligence, booking ingest, daily ops, follow-up, no-show recovery)
- SEO overhaul — structured data (JSON-LD), sitemaps with image extensions, crawler-specific HTML, meta tags, FAQ sections
- Analytics layer — centralized analytics with GA4 + PostHog adapters, browse funnel, checkout tracking
- AI video metadata — auto-generate titles and captions from transcriptions using OpenAI
- Event logging system — track all user actions (auth, purchases, webhooks) with IP/device metadata
- User Activity page (AdminJS) — timeline view with DAU/MAU metrics
- Instructor Dashboard rewrite — accurate metrics with extracted helpers
- PostHog integration — server + web + mobile product analytics
- Google Calendar bridge — sync bookings into CRM
- Stream Chat channel management (add/remove members)
- E2E tests with Playwright
The AdminJS Story
AdminJS deserves its own section. We built a full internal operations platform with:
- 70+ resource configurations — every Prisma model has a tailored admin interface with custom properties, actions, and navigation groups
- 4 custom dashboards:
- Revenue Dashboard — MRR, revenue by store, monthly trends
- Teamspace Dashboard — team health metrics, user activity, subscription status
- Instructor Dashboard — content metrics, payout tracking
- Sales Commission Dashboard — rep assignment, payout calculator, transaction history with CSV export
- Custom components: Video Manager, Product Manager, Bulk Tag Manager, Email Attachment Manager, Payout Approval, Order Item Export, User Impersonation, Firebase Token Manager, Teamspace Chat Sync, User Activity Timeline
- Component loader with 90+ registered components
- Runs as a separate app (
admin-adminjs/) connecting to the same PostgreSQL via Prisma
Challenge: AdminJS's built-in CRUD is great for simple cases, but every real-world resource needed custom actions (assign rep, approve payout, export CSV, bulk operations). The custom React components inside AdminJS communicate with the server via TSOA-generated admin endpoints — a pattern we evolved over time that keeps business logic on the server while giving the admin UI full flexibility.
Key Technical Challenges
1. Dual Payment System (Stripe + RevenueCat)
Web payments go through Stripe (checkout sessions, subscriptions, invoices). Mobile goes through RevenueCat (App Store, Google Play). Both need to create the same Order/OrderItem/Subscription records. We learned the hard way that isMobile is true for both iOS AND Android (fixed the RevenueCat API key bug), and that store fees need per-store calculation via getStoreFee() instead of hardcoded Apple rates.
2. Bunny.net to Mux Video Migration
6,000+ videos migrated from Bunny.net CDN to Mux for proper video streaming with secure playback tokens, automatic transcription, and thumbnail generation. This involved migration scripts, dual-CDN support during transition, and updating every video reference in the system.
3. Teamspace Pricing & Seat Management
Teamspaces have complex pricing: per-seat costs, bulk discounts, coupon support, seat add-ons mid-subscription, and pro-rated renewals. Getting calculateTeamspacePricing right — especially for edge cases like partial refunds and mid-cycle seat additions — required multiple iterations and scripts to fix historical data.
4. Calendar Recurring Events
Implementing RRULE support (RFC 5545) for recurring calendar events that respect timezones, handle exceptions (deleted/modified occurrences), and work across iOS/Android/Web was one of the most complex features. Each occurrence needs its own composite ID while sharing the parent's template.
5. Real-time Chat with Access Control
Stream Chat integration needed custom access guards — only active teamspace members can access channels, and when a subscription expires, chat access must be revoked. Channel management (add/remove members) with optimistic UI and proper state refresh added another layer.
6. Monorepo Without Workspaces
We intentionally avoided Yarn workspaces because Expo/Metro bundler doesn't play well with hoisted dependencies. This means app/ and server-api/ have independent node_modules, and shared code lives in server-api/shared/ — a pragmatic trade-off that prevented bundler nightmares at the cost of some duplication.
7. Webhook Reliability
Stripe and RevenueCat webhooks must be idempotent, handle retries, and never block the main payment flow. We added createSafeFromOrderItem wrappers that catch errors for non-critical side effects (like sales transaction creation), while ensuring core order creation remains atomic with prisma.$transaction.
What I Learned
Building a platform from $5K to $70K+ MRR taught me that the features that drive growth aren't always the flashy ones. The teamspace explosion (from 15/month to 121/month) was driven by notification infrastructure — daily summaries, assignment reminders, timezone-aware calendar notifications. Making coaches' lives easier with automated communication was the real growth lever.
The AdminJS investment paid for itself many times over. Having a powerful internal tool meant the operations team could self-serve instead of filing engineering tickets. Every custom dashboard we built reduced the support load.
And using Claude Code as a daily co-pilot across this entire stack — from Prisma migrations to React Native components to Cloudflare Workers — let a small team ship at a pace that would otherwise require 3-4x the headcount.


