Lead-to-cash system for a field business · Live in production
Bay Area Wi-Fi Networks
The quoting and CRM platform my network-installation company runs on — a customer can go from a form to an approved, paid quote without anyone picking up the phone.
Role: Product design, flow design, money model, and direction of the agents that wrote it.
Timeline: Live in production — running the business since early 2026
- Sold since launch
- $216,594
- Kinds of information tracked
- 28
- Things it can do
- 227
- Commits
- 1,993
- Automated test files
- 51
Work sold through the Bay Area Wi-Fi Networks system since it went live: $200,443 already received and $16,151 approved and awaiting payment. Read from the system's own financial reporting on 21 September 2026. That company is mine, so this is a figure I can stand behind directly rather than a client's claim. It counts money the system has recorded, so it is what the business has booked rather than a bank-reconciled total.
Distinct record types in the data model, from counting the table definitions in the schema file. Counted on 21 September 2026.
Separate operations the system can perform, from counting the HTTP endpoints registered in the server code. Counted on 21 September 2026.
Individual changes recorded in the project history, from git rev-list --count HEAD. Counted on 21 September 2026.
Files of automated tests that run on every change. Counted on 21 September 2026.
What was actually broken
In field services, the business goes to whoever quotes first. A lead comes in from Yelp, Google or Thumbtack, and that customer is getting three other quotes the same afternoon. If yours takes two days because someone has to spec the equipment, price the labour and write it up by hand, the job is gone before you send it.
That was the real constraint — not marketing, not capacity. The gap between "someone is interested" and "here is a price you can accept" was measured in days, and every hour in that gap lost jobs.
Everything in this system exists to close that gap.
The flow I designed
The website answers a question before it asks for anything. The front door is a Network Performance Score — a short assessment of how someone's current setup is actually performing — and a cost estimator that gives a real range. You find out something useful about your own house before anyone asks who you are. Same principle as the scorecard on the CLEAR site, and it works for the same reason.
Quotes are assembled, not written. The quote builder holds the live UniFi catalogue, a compatibility rules engine that knows which equipment actually works together, and the labour maths. What used to be an evening of specification becomes minutes, which is the entire competitive advantage.
The customer approves and pays in one place, with no account. A private link opens a page where they review the work, pick optional extras, choose payment terms and pay. No password, no portal, no phone tag.
Technicians see the job, not the money. The technician portal is reached by token and deliberately shows scope without pricing. A tech standing in someone's hallway needs to know what to install; showing them the margin helps nobody and creates an awkward conversation waiting to happen.
A lead stays "new" until someone actually does something about it. Not until someone opens it — until someone acts. Systems that mark work as seen when it is merely glanced at are lying to you about your own follow-up, and follow-up is the whole business.
Interface notes
The promise is time
Two minutes, not “contact us for a quote”. The job goes to whoever answers first.
Two doors, deliberately
One door for people happy to help themselves, one for people who want a human. Picking only one loses half of them.
It names the fear, right under the button
The reason people hesitate is what happens to their number afterwards. Answered right at the button, not in a privacy policy.
You can see the whole shape of it
Seven steps, said up front. Hiding how long a form is loses people halfway through.
Big targets, plain words
Big tiles, not a dropdown. This gets filled in one-handed by someone annoyed their Wi-Fi is down.
Still nobody has asked who you are
The price comes first. Contact details come after there is a number worth having.
The money is on the row
Every job shows what it is worth in the list. Hide that behind a click and people open everything.
A failed payment is a status, not an email you missed
A failed payment shows on the job itself. Money problems that live only in a card processor get found weeks late.
Change the stage from where you already are
Change the stage right here. Status that is tedious to update stops being true.
One job, one place
Quote, scope, technician, approvals and payments on one job — not six systems each knowing a different part.
Collected and contracted are different numbers
Side by side, never merged. Counting signed work as money in the bank is how businesses talk themselves into trouble.
A funnel you can watch leak
Stage by stage. You cannot fix a drop-off you cannot see — and it is almost always between quote sent and quote accepted.
The close rate is computed, not remembered
Worked out from the same records everything else uses. A figure typed into a spreadsheet quarterly is one nobody acts on.
The hard part
Making sure the price someone agreed to is the price that stands.
Quoting looks simple until real life turns up. One job usually needs a few versions — good, better, best — that live together without anyone losing track of which is which. The moment the customer accepts one, the others have to stop being live offers.
And the price they accepted has to freeze. If somebody tidies the quote up next week, that must never quietly change what the customer already agreed to buy.
So an accepted quote keeps an exact copy of what was accepted, and later edits cannot reach back through it. Whether the other versions are closed is not a note somebody has to remember to update — it is worked out from whether one was accepted, so it cannot drift away from the truth.
Payment plans made it harder again. Charging a card over several months means more than one thing can trigger the same payment, so the protection against charging twice has to sit in several places at once.
The feature is easy. The guarantee is hard. And where money is involved, the guarantee is the entire product.
Stack
| Frontend | React 18 + Vite, Wouter, TanStack Query |
|---|---|
| UI | shadcn/ui on Radix, Tailwind, framer-motion, Recharts, TipTap |
| Backend | Express + TypeScript |
| Database | PostgreSQL + Drizzle ORM |
| Payments | Stripe Checkout, webhooks, and financing instalments — Idempotency guards at several layers, because a double-charge is unrecoverable trust. |
| Telephony | Twilio voice and SMS, with a shared team inbox |
| IMAP/SMTP with a Gmail API fallback, threaded per lead | |
| AI | OpenAI for estimator analysis and voicemail extraction |
| Catalogue | Puppeteer + Cheerio, scraping the live UniFi store — Equipment pricing that goes stale produces quotes that lose money. |
| Lead sources | Yelp, Google and Thumbtack, ingested automatically |
| Testing | Node's built-in test runner, booting the real Express app |
Outcome
Every dollar the business has sold has gone through it — the figure beside this is read from the system's own books each week, not from memory.
It runs the business end to end: lead capture from three marketplaces, the assessment funnel, quoting, approvals, payments, financing, the shared inbox for email and SMS, technician dispatch, and the financial reporting.
An unexpected second effect: because the site is structured, plainly written and genuinely answers the questions people ask, AI assistants started recommending the company to people looking for network installers. That was not the goal — it turns out that building a site to be understood is the same work as building one to be cited.