All ten
Active Join logo

Active Join

Operating system for jiu-jitsu academies

Technical lead — architecture, API, admin apps, billing and delivery

Live, running a real academy

Gyms lose members quietly — people drift off the mat weeks before anyone cancels. Active Join runs the whole academy (members, classes, kiosk check-in, belt progression, billing) and watches attendance for exactly that drift, so a coach can reach out while the member is still reachable. It runs the founder's academy in Nürnberg, where churn fell from 75% to 23%.

Gym cockpitPlatform adminKiosk check-inMobile (Expo)
18API modules
6audiences in one schema
3billing relationships
75% → 23%churn at the founding gym

Screens

Key engineering

field-level GraphQL authorisationStripe Connect three-way billingidempotent webhook reconciliationrrule schedule expansionoutbox → inbox notificationsstreak recomputation jobsGDPR export & erasureaudit log + domain eventsdataloader batching

Under the hood

Architecture

A single GraphQL API over modular services, with authorisation enforced at field level so six audiences share one schema instead of one API per role.

Data & state

Relational source of truth with an append-only audit trail and domain events. Recurring schedules are expanded from rules rather than stored as rows, and notifications go through an outbox so a delivery failure cannot lose the event that caused it.

Runs on

Managed Postgres behind a serverless-friendly API runtime. Web and mobile clients ship independently of the schema, with a generated SDK keeping them in step.

Operational risk

Money is the sharp edge: webhooks arrive twice, late, or out of order, so reconciliation is idempotent and all three billing relationships settle against the provider rather than against an assumption. Belt progression and billing history are auditable because both get disputed years later.

Stack

API
BunHonoPothos GraphQLrelay paginationdataloadersscope-authquery complexityScalarSentry
Clients
TanStack StartTanStack RouterApollo Clientgraphql-wsLingui i18nRadixTailwindExpo push
Data
PostgreSQL (Neon)Drizzlerruleaudit logdomain eventsS3 presigned uploads
Money
Stripe Connectplatform subscriptionsstudent billingfamily billing groupsinvoice cacheidempotent webhooks
Platform
Better Authpasskeysnotification outbox → inboxfeature flagsGraphQL codegen SDKReact EmailResend

What it took

  • Six audiences, one schema. Owners, coaches, students, parents acting for dependents, association admins and platform staff — access enforced at GraphQL field level through scope-auth, never hidden in the UI and hoped for.
  • The retention engine is the product. Attendance streaks with monthly grants and recalculation runs, leaderboard snapshots and milestones feed an at-risk signal, so a coach hears about a fading member while there is still time to act.
  • Billing is three systems that have to agree: what the gym owes the platform, what a student owes the gym, and what a family pays as one group — reconciled against Stripe webhooks that arrive twice, late, or out of order.
  • Belt progression is auditable — rank history, progression rules and graduation events — because a promotion is a claim someone will dispute years later.
  • GDPR as engineering, not a checkbox: data export, real account deletion, privacy controls around sensitive records, and an audit trail over all of it.
  • A way in for gyms leaving other software: import mapping templates and row-level import runs, so a messy spreadsheet lands as clean membership data.