MIKHAIL NERI

Case study · Product I own

RollCall

A multi tenant BJJ gym SaaS: QR check in, belt progression, dues with dunning and family accounts, plus an Android app.

Role
Founder, sole architect and developer
Status
In development
Period
Jun 2026 to present
Stack
Laravel, PHP 8, Sanctum, MariaDB, React 19, TypeScript, React Native, Expo

Summary

Problem
Most gyms still run on a clipboard and a spreadsheet: attendance on paper, belt eligibility in the head coach's memory, dues chased by hand.
System
A Laravel API with Sanctum, a React 19 TypeScript web app and a React Native Expo Android app, all sharing one Zod typed contract; each gym is an isolated tenant.
Outcome
In development. The Android app is in release prep, with QR check in backed by an idempotent offline queue.

What needed to exist

One platform a gym owner can actually run.

Most gyms still run on a clipboard and a spreadsheet: attendance is scribbled on paper, belt eligibility lives in the head coach's memory, and dues are chased by hand. RollCall replaces all of that with one platform a gym owner can run, and that I can stand up for any number of gyms from the same codebase.

Constraints

Many gyms, one deployment, no data crossing between them.

RollCall is multi tenant from the ground up. Each gym is an isolated tenant with its own members, classes, plans and staff, so one deployment serves many gyms without their data ever touching.

Architecture

One API, two clients, one contract.

The API is the only thing that touches the database. The React 19 web app for owners and coaches and the React Native Android app for members and coaches both consume it, authenticated with Sanctum tokens.

Decisions

A shared contract, and no store payment SDK.

  1. One typed contract

    Every request and response type comes from one shared, Zod typed contract in a pnpm monorepo. Change a field once and both clients fail to compile until they agree with the server.

  2. No store payment SDK

    Dues are real world services paid at the gym, so the app carries no store payment SDK and stays out of in app purchase rules.

Hard problems

Check in that survives no signal.

The hardest part of a gym app is the least glamorous one: members scan a QR code to check in, and the mat is often exactly where the wifi is not.

Check ins are queued on the device and sync the moment connectivity returns, built around one invariant: a member is marked present for a given class on a given day at most once, however many times a flaky connection retries. The dedupe key is the member, the class and the day, enforced end to end, and the offline queue and sync classifier are covered by pure logic assertions that run against the real transpiled code.

Implementation

What it does.

  1. QR check in and attendance

    Scan to check in, with an idempotent offline queue and an attendance heatmap per member.

  2. Belt progression

    Stripes and belts with eligibility rules and a promotion history, so the coach sees who is due at a glance.

  3. Dues and billing

    Membership plans, dues tracking and reconciliation for local rails, with dunning for missed payments.

  4. Coach mode

    Class rosters and manual check in through the same idempotent path, for members without a phone on them.

  5. Member chat and board

    In app messaging and a community board per gym, plus push notifications.

  6. Family accounts

    One guardian managing several members, for the kids classes that fill a BJJ schedule.

  • Shared Zod typed contract
  • Multi tenant isolation
  • Sanctum token auth
  • API feature tests
  • Offline logic assertions
  • Strict TypeScript in both clients
  • pnpm monorepo
  • Idempotent writes

RollCall Mobile

The Android app, in release prep.

A standalone Expo React Native client for members and coaches, Android first, sharing the same typed contract as the web app: QR check in backed by an idempotent offline queue, an attendance heatmap, belt progress and push notifications, over the Laravel and Sanctum API.

Strict TypeScript throughout, secure token storage, session restore, and a clean logout on an expired token.

Result

In development.

The API, the web app and the Android app are in development, with the Android app in release prep. There is no public link until the hosting is back online.

Building something like this?

Start a project

Cafe POS Next system