Client: Hair Time Salon Role: Solo build (architect + engineer) Period: April 2026 to Present Stack: Vapi, GPT-4o, Deepgram STT, ElevenLabs TTS, n8n cloud, Supabase Postgres, Next.js 14, Vercel, Twilio
Hair Time Salon runs a one-to-two-chair shop in Franklin Park, NJ, with a second location in North Brunswick. The owner cuts hair from 10 AM to 8 PM most days. While he is cutting, he cannot pick up the phone. After 8 PM and on Sundays, nobody can. Each missed call is a potential booking that leaks to the next salon a customer dials.
Three constraints shaped what we needed.
The owner has no time for the system. He is a hairdresser, not an ops manager. Anything that demands daily attention is dead on arrival. The system has to run itself.
The catalog is real. 85 services and 10 active specials, with day-of-week rules, gender restrictions, cash-only constraints, and discount stacking logic. The agent has to know all of it without sounding like it is reading from a list.
Customers are real humans on cell phones. Mumbling, accents, names like Priya Sharma and Satyarthi that off-the-shelf speech-to-text has never been trained on, phone numbers spoken in weird groupings. The system has to handle all of that without dropping bookings.
A 24/7 voice-booking agent that answers the salon's existing phone number when nobody picks up, books appointments straight into a Supabase database, and lets the owner manage everything from one iPad-friendly dashboard.
The voice agent. Vapi handles the realtime orchestration. Deepgram does speech-to-text, GPT-4o is the brain, ElevenLabs does the voice. The agent has four tools: lookup_customer, check_availability, book_appointment, and update_booking for mid-call changes. It does not have a find_service tool — the full 85-service catalog is rendered into the system prompt itself at publish time, eliminating a tool round-trip on every call.
The orchestration layer. n8n cloud hosts seven thin webhook workflows, each one a wrapper around a Supabase stored function. The agent never talks to the database directly. Every booking goes through n8n, which means every booking is loggable, replayable, and debuggable.
The database. Supabase Postgres with nine tables, multi-tenant from day one. Every domain row has a salon_id. Row-Level Security scopes queries automatically. The same schema serves Franklin Park today and will serve North Brunswick after a three-hour code update — no separate database, no data migration, no infrastructure duplication.
The dashboard. Next.js 14 on Vercel. The owner logs in once, sees today's bookings, can manually add walk-in visits, manage services and specials, segment customers for SMS campaigns, and drill into any booking. RLS automatically scopes everything to the salon the user belongs to. Cross-salon data leaks are impossible by construction.
The call routing. A *71 conditional carrier forward on the salon's existing line — 30 seconds for the owner to set up, $0 cost, and it catches both off-hours calls and mid-day overflow when the owner is busy. No number porting, no carrier changes, no disruption to the existing operation.
Beating speech-to-text and text-to-speech failures in production. Deepgram mishears uncommon names — "Satyarthi" becomes "Satyati" on the first try. ElevenLabs reads "May 23rd" as "May twenty rd" and "2:15 PM" as "two two PM." Both have to be fixed in the system prompt, not the audio layer. The prompt enforces explicit spelling for non-common names, spelled-out ordinals for every date 1-31, and natural-language times ("two PM", "two fifteen", "two thirty"). The cost of getting this wrong is wrong customer records and missed bookings.
Phone normalization at every layer. The agent will, given the chance, pass the word "yes" as a phone number when a caller says "yes use that one." The fix is defense-in-depth: a CRITICAL rule in the prompt, n8n webhook normalization that strips formatting and keeps the last 10 digits, placeholder rejection for 1000000000 and similar, and a Postgres CHECK constraint on the customers.phone column that refuses anything not in E.164 format. Five layers. Each one catches something the others miss.
Three caller-ID scenarios. Returning customer matched by caller ID gets a warm "welcome back, Priya" and never has to give the phone again. New customer with caller ID available gets asked for the name and confirms the last four digits of the calling number. Blocked or anonymous caller IDs get explicitly asked for both name and number. The agent picks the right branch automatically based on what Vapi's customer.number field contains.
Mid-call updates. Callers change their mind. "Actually, can we do 3 PM instead?" The agent keeps the booking_id from the just-fired book_appointment call in conversational memory and can issue update_booking(booking_id, action="change_time", new_time="15:00") without re-collecting any other information. Time, date, service, and cancellation are all supported. The customer never has to repeat themselves.
Catalog as data, not code. The 85 services and 10 specials live in Supabase. The publish script reads them, renders markdown tables, and PATCHes the live Vapi assistant in 3 seconds. Updating a price means editing one cell in the database and running one script. No prompt edits, no manual sync, no risk of the agent and the dashboard disagreeing about what something costs.
The system is live as of May 2026. A 30-day pilot is in progress.
| Metric | Status |
|---|---|
| Total monthly cost at pilot volume | ~$35-55 |
| Total monthly cost at full operation | ~$100-150 |
| Setup time for owner | 30 seconds (*71 carrier code) |
| Time to add a second location | ~3 hours of engineering + 30 seconds of owner setup |
| Calls handled (30-day pilot) | Report due June 22, 2026 |
| Bookings captured (30-day pilot) | Report due June 22, 2026 |
| After-hours bookings (30-day pilot) | Report due June 22, 2026 |
| Cost per booking (30-day pilot) | Report due June 22, 2026 |
For context: typical receptionist coverage runs $15-25 per hour. A 30-second call that ends in a booking pays for several days of the entire AI system.
The 30-day report will include real call volume, booking conversion, after-hours capture rate, total Vapi spend, and the owner's subjective take on what worked and what did not. Follow the blog for the full engineering build log and watch this page for the results update.
This is the build that anchors the Custom AI Agents + RAG and AI Workflow Automation services. If you have a problem that looks like "a small business is losing revenue to phone calls nobody picks up," this is the architecture I will design for you. The exact same pattern works for dental offices, clinics, restaurants, real estate offices, and any service business with a phone number on its website.
The non-obvious work is not the model choice or the platform. It is the engineering around the platform — speech-to-text edge cases, text-to-speech mangling, phone normalization, multi-tenant data design, and graduated call routing — that determines whether the system survives real customers.
Most "I built a voice agent" demos work in a quiet room with a developer's voice and a synthetic test number. Shipping into a noisy salon, with elderly callers, blocked caller IDs, and uncommon names, is a different category of engineering.
A scoping engagement to define your call volume, miss rate, and conversion goals. Then a build engagement to ship the first production system: voice agent, integrations (calendar, SMS, payments as needed), operator dashboard, and call routing from your existing phone line. Then optional ongoing retainer for prompt iteration, new feature builds, and second-location rollouts.
Typical engagement: 3-6 weeks from kickoff to live calls. The hard work is week 2 and week 3. The system runs itself from week 4 onward.
Book a 30-min discovery call if you have a phone line that is leaking bookings. Calendly link in the header.
Book a 30-min call. We will talk through the architecture and what it would take to ship something like this for you.