I wasn't originally planning to put this in the portfolio, but I've mentioned it in my CV and cover letters, so I thought it was worth a short write-up here too.

The Problem

I coach in a specific way, and the reason I built this is that I wanted onboarding and check-ins systemised around my own coaching style rather than using a generic template. Up until now, I've also not been able to delivr any sort of report to m clients, and having something tangible for them to read, really helps convey a sense of professionalisma nd value. My goal for this system was to collect the client's information — goals, injuries, medical history, eating patterns, sleep, stress, and a self-assessment of current habits — in one structured form, turn it into a report that both the coach and the client can work from, and then keep the same structure going with a weekly check-in.

The second requirement was operational: I made this for me, to make my own work more efficient (and even more professional). The actual app including it's infrastrcutre had to be simple.

What It Does

The coach logs in, adds a client, and starts the intake — either with a "fill during session" button that opens the form directly, or by giving the client a single-use link to complete it in their own time. The client fills in a two-sheet intake: one sheet of specifics (goals, injuries, medical history, what they actually eat, training and life logistics) and one sheet of 1-to-10 self-ratings across nutrition, sleep, stress, and movement, each with an optional one-line note in their own words. On submit, the engine scores the ratings deterministically, two AI agents interpret the free text, and a structured report is stored and rendered for the coach. After onboarding, the same structure continues week to week: I built a weekly check-in form that each client accesses through one reusable link, and a third agent writes a coach-only trend read on every submission.

Outcome
Client intake collected through one structured form, in session or via a link
Coach-ready report generated within a minute of the client submitting
Each report generated in two versions: one for the coach (clinical, third person) and one for the client (second person)
Weekly check-ins on one reusable link per client, with an AI trend narrative
Every AI call logged with prompt version, tokens, cost estimate and request id
Hosting and database run in managed free tiers; AI costs are about 17 cents per intake form (full length intake form)

The Onboarding Flow

The main architectural decision was how to divide the work between deterministic code and AI. The diagram below shows the onboarding flow and where each sits.

Coach dashboard                    Client (no account)
(Clerk-protected)                  (single-use link or in-session)
      │                                   │
  add client ──── intake link ───────────►│
      │                                   │
      │                        two-sheet intake form
      │                        (specifics + self-ratings)
      │                                   │
      │                                   ▼
      │                          POST /api/intake/submit
      │                                   │
      │              ┌────────────────────┼────────────────────┐
      │              ▼                    ▼                    ▼
      │      deterministic         nutrition agent      report agent
      │      ratings engine        (what the client     (narratives, plan,
      │      (scores, focus,        says they eat)       readiness read)
      │       gap detection)              │                    │
      │              └────────────────────┴────────────────────┘
      │                                   │
      ◄──────────── stored report ────────┘
      │
  report viewer (coach / client voice) ── weekly check-in loop

Key Design Decisions

Decision · Hosting

Managed platform over self-built infrastructure. My other projects run on hand-built AWS: VPCs, ECS, Terraform. This one deliberately doesn't. Vercel hosts the Next.js app, Neon provides serverless Postgres in the Sydney region, and Clerk handles coach auth. Deploys are a git push, preview environments are free, and there is no instance anywhere that can fill a disk or miss a patch. For a tool supporting a one-person business, low operational overhead was the most important requirement. The tradeoff is platform lock-in, which I judged acceptable because every piece (Postgres, React, coach auth) is portable in substance even if the wiring is not.

Decision · Client access

Clients don't have accounts. Asking a new coaching client to create a login before they've had a single session adds friction at the point where they are most likely to drop off, so clients only ever use tokenised public links. Intake links are single-use and expire after 14 days. Check-in links are deliberately the opposite: one reusable link per client that lives in their bookmarks, with revocation instead of expiry and a 20-hour double-submit guard. Two different token models for two different behaviours, each implemented as its own small validator rather than one generic system.

Decision · Multi-coach data model

Although built for my own practice, the system supports multiple coaches from the start. Any coach can sign up for their own account and gets their own client list, intake forms, and stored reports, scoped entirely to them. I did this in case I ever want to offer the system to other coaches — retrofitting per-coach data scoping later would have meant reworking every table and query, so it was cheaper to build it in from day one.

Decision · Delivering the intake form

The intake form is opened in the session rather than emailed. I considered emailing the form to clients through Resend, and spent some time on whether I wanted email delivery at all. In the end I added a "fill during session" button that opens the form directly from the client's record, alongside the option of handing over a link. Dropping email meant one less external service to integrate and test, and it fits how onboarding actually happens in my practice.

Decision · Knowledge base

A coaching knowledge base is shared between the coach and the AI agents. The way I coach is written down in a set of reference documents. I can consult them myself, and the agents draw on the same material when writing reports and check-in reads, so the AI output stays consistent with how I actually work with clients instead of defaulting to generic fitness advice.

Decision · AI boundaries

AI is limited to interpreting free text, and each agent can fail without affecting the rest of the system. Each AI agent has one job and a typed tool schema: one estimates nutrition from the client's own food descriptions, one writes the report narratives, one reads weekly check-ins in context. Each call is wrapped so it can never throw away an intake: if an agent fails, the report still renders from the deterministic output with a visible "rules-based only" notice, and the failure is logged. The client's data is written before any AI runs.

Decision · Observability of the AI

Every AI call attempt writes a log row. Status, the exact prompt sent, raw tool output, token counts, duration, prompt version, and the provider request id. A coach-facing log viewer sits behind every report with a per-call cost estimate. When a report reads oddly, I can see precisely what the model was told and what it returned, and prompt versions make regressions diffable. This makes prompt changes much easier to evaluate.

Decision · Report versions

Each report is generated in two versions: one for the coach and one for the client. The coach view is clinical third person and includes coach-only material (readiness assessment, self-awareness gaps where a client's self-rating contradicts their own numbers). The client view is second person and motivational, with those sections hidden. Both versions are produced in the same generation pass and stored on the same report, so they can't drift out of sync.

How It Was Built

I used Claude Code heavily throughout. This is a working tool for my own business, so I favoured whatever got features built and tested in the shortest amount of time: describe the behaviour, have the assistant write the code, then test it against real intake data and correct from there. The design decisions above — where the AI boundaries sit, how the tokens work, what stays deterministic — were mine; Claude Code did most of the typing.

The system is a work in progress. I'm testing it with my own clients, and I'll keep making changes as I see fit.


Live site ↗ View on GitHub ↗ ← All projects