The problem
DoorStain had 25 full-time employees, teams in Atlanta, California and Washington DC, and $887K of booked work running through one Google Sheet and a pile of Zapier automations. Nothing in that setup tracked the money.
A phone web app wrote each booking into the Sheet, one tab per market, plus an all-day event on Google Calendar. Zapier handled contacts and text reminders, with three sets of automations per location across 6 markets. Every reschedule meant editing a calendar event by hand.
About 80% of payments came in by check or Zelle, roughly 40% each. Nothing recorded whether a check got deposited, or into which bank. Unpaid and rescheduled jobs dropped out of sight, and that gap let $25K+ a year slip through the cracks.
There were no personal logins either. I had no way to keep DC staff out of Atlanta's numbers, and no way to tie a change to the person who made it.
The same year, I rebuilt DoorStain's paid search and cut cost per lead from $634 to $59, 10x more leads for every ad dollar. Cheap leads only pay off if the office can book, schedule and collect on every one of them. My brief: account for every dollar, and replace the system the business ran on every day without breaking it.

Who it's for
All 25 employees got their own login, and each one sees exactly the markets they work and nothing else.
Role tiers couldn't describe how the company actually ran. One person needed every market but no admin rights. Someone else covered everything except Atlanta, and California had its own person. I designed access around markets first, then put money behind its own permissions.
The crews weren't technical, and they liked the old app because it was simple. The new one had to be just as quick to pick up. They already lived in Google Calendar and planned their day by place, so I kept the calendar, synced it both ways and added a job map.
I was the only admin, and I held the product to a high bar. I scored an early build 30 out of 100, wrote up 30 numbered pieces of feedback and redesigned from that list.
Key decisions
Four calls turned a fragile spreadsheet into a system built to account for every payment. Each one came from something I'd watched go wrong, in the old setup or in my own early build.
Build beside the live system, payments first
The business kept booking on the old setup the whole time I built the new one. The old app stayed untouched, and the new one went on its own subdomain and never wrote to the live Sheet. The rollout plan put payments first, then scheduling market by market, with a rollback path at each step. New client numbers start at 2000 and old ones stay in the 1000s, so the two systems can't collide.
Access by market, money behind its own switches
DC staff can't see Atlanta's money, and one app covers every working arrangement the team had. Each person gets a set of markets, checked on the server on every request. On top of that sit 14 switches for seeing what's owed, bank status and reports, and for recording payments, booking, editing and deleting. A crew member gets the same push alert as the office, minus the dollar amount.
An append-only ledger
This is the decision that stopped $25K+ a year from slipping away. From the 18 June 2026 cutover on, payments are only ever added. A mistake gets a reversing entry, and every balance is calculated from the entries. Zelle counts as paid only once it lands. The Needs attention list has no dismiss button on purpose, and an item leaves only once the payment is settled. I chose a plain username and password per person over SMS codes because crews didn't need the extra step. Per-person logins still tie every action to someone in a hash-chained audit log.
Details before action
Every job opens to its address and client phone, one tap from directions or a call. In my early build, tapping a job jumped straight into payments. Now it opens the job first. Multi-day jobs show as one connected bar, the way Airbnb draws a stay, and phone and dollar fields format as you type.

How I built it
I shipped 40+ screens and components, about 10,000 lines of interface code, in four months, with 489 test cases gating every deploy. I designed it and led the build from the first spec to launch.
Before any code, I had the live system mapped from every source I had: my voice-dictated brief, the old app's code, a Sheet export and screenshots of the Zapier flows. The old feature list was pulled twice, independently, then merged and spot-checked against the source, which kept every feature the crews relied on.
For architecture I ran a design review. There were 4 proposals, and each one was stress-tested on money safety, crew simplicity and migration risk. The ledger-first plan won. The review ruled out staying in Google, and I agreed, because a ledger the spreadsheet owner can edit can't be tamper-evident.
Then I locked decisions in rounds. Nothing was built on a guess: every integration started from the real API docs, and every piece needed an approved spec and tests that proved it worked. Reading the docs caught a dead end early. OpenPhone's API is text-only, so client photos get uploaded by hand and compressed on the phone.
CI is the release gate. Every push runs both test suites (489 test cases), the type check and the build, and deploys only if everything passes. The iPhone app is a thin Capacitor shell around the live site and went out on internal TestFlight through build 6, which means a web deploy reaches every crew phone right away.

What changed
The office got back 1,000+ hours a year on scheduling and follow-up, and the crews got back 800+ more. That's $35K+ a year in paid time. Add the $25K+ in payments that no longer slip, and the app returns $60K+ a year to the business.
The app is live and all 25 employees have their own login. Checks, Zelle, Venmo and cash each have their own steps from received to settled. Payments from the 18 June 2026 cutover on are tracked in the ledger, and earlier jobs are treated as settled. Deletes are soft and an admin can restore them, and a daily backup is kept for 30 days.
The calendar syncs with Google both ways every hour, quotes and invoices come out as PDFs, and the job map lets crews plan the day by place. Text reminders go out at each location's local time, and no-shows dropped 30%.
Jobs started on time more often, clients had fewer reasons to complain, and the old mix-ups over who collected what stopped, because every payment now lands in one ledger.
Even the push alerts are tested. I once screenshotted one that read "Reminder sent · Washington, D..." on the home screen. Now every alert follows a 3-line format, and unit tests enforce the character limits.

What's next
Next, I'd turn the ledger into the owner's morning cash view: what's owed, what's in transit and what landed in which bank, by market. Then I'd wire paid search into it, so every ad dollar traces to a booked and paid job and bids chase revenue instead of form fills. With that loop closed, the app is ready for any multi-market field-service company still running on a spreadsheet.

