Abílio Azevedo.

Athletes Ocean

Cover Image for Athletes Ocean
Abílio Azevedo
Abílio Azevedo

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)
Email 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.


More posts

Cover Image for Building a Remote MCP Server for Google Workspace (Sheets, Docs and Presentation)

Building a Remote MCP Server for Google Workspace (Sheets, Docs and Presentation)

Learn how to build and deploy a remote MCP (Model Context Protocol) server for Google Workspace (Sheets, Docs and Presentation) using Next.js, Vercel, and Neon Postgres. Step-by-step guide covering two-layer OAuth authentication, tool registration, serverless deployment, and debugging with MCP Inspector — so any AI assistant can read, write, and manage spreadsheets with just a URL.

Abílio Azevedo
Abílio Azevedo
Cover Image for UX/UI for developers

UX/UI for developers

UX/UI for Developers — A practical guide on design systems, communicating with Product Designers, and knowing when to reuse components. Covers Nielsen's heuristics, Atomic Design, Tailwind CSS component libraries like shadcn/ui and Radix UI, prototyping tools like Figma and Origami Studio, accessibility best practices, and curated courses, articles, and books for developers building better user experiences.

Abílio Azevedo
Abílio Azevedo

NewsLetter

I will send the content posted here. No Spam =)