PHOENIX WELLNESS
A clinic CRM built around four jobs: book the visit, know who needs a refill, send the invoice, and actually reach the patient. The spreadsheet is gone.
Appointments, refills, Square billing, and reminder email — live.



Appointments, refills, charging, and communication. Everything else is a later add-on — the v1 was scoped to replace a spreadsheet, not to become an EHR.
Protocols carry a duration. The refill board is cumulative windows — today, 3, 7, 30 days — so the front desk prepares ahead instead of scanning a sheet.
The clinic was tracking renewals in a spreadsheet and paying for a tool that did a lot of things they didn't use. The brief was four words: appointments, expiring, charging, communication. Everything else — portals, e-sign, a reports suite — was explicitly later.
It's also a product, not a one-off. Nothing in the code names the clinic. Branding is runtime config, so the next one stands up without a fork.
Appointments
The next 30 days, grouped by day, with check-in state. An appointment belongs to a person — reassigning is cancel and rebook, not a silent move between records.
Refills
Everyone whose protocol is due or lapsed, in cumulative buckets that share their predicates with the list they open — a chip and the rows behind it cannot disagree.
Billing
Square invoices sent from the patient record. The local row is written first so a retry can't double-bill; a publish is what actually delivers the pay link.
Communication
Every email the system has sent, with the reminder that triggered it. A daily scan queues the day's windows, reclaiming failed sends instead of burning the slot.
A product, not a one-off
No clinic name in the code. Branding is APP_NAME / APP_MARK / APP_LOGO at runtime, so standing the same app up for the next clinic is config, not a fork.
One vendor, one BAA
Next.js on Cloud Run, Cloud SQL Postgres, files in GCS streamed through the app — not signed URLs, which would land in the request log. The whole stack sits inside Google Cloud.
Revenue snapshotted at send
Two programs, one Square account, split after the fact. The program is stored on the invoice when it's sent. Joining back to the patient would re-attribute history every time someone corrected a record.
Date-only, clinic-local
Expiry is a calendar date, parsed as a date, never as UTC midnight. The container runs with the clinic's timezone so a scan at 9pm doesn't shift every bucket by a day.