How an AI receptionist actually books an appointment over WhatsApp (calendar sync, freeBusy, and why "no" should never be the final answer)
A technical walkthrough of the booking pipeline. Plus a detour through behavioral science, because the hard part of scheduling was never the calendar.
Rory Sutherland has a line that runs my product
The Ogilvy vice chairman has spent twenty years making the same argument: most product problems aren't engineering problems. They're psycho-logic problems. People don't experience your infrastructure. They experience the interaction.
His favorite example is the train operator that put a schedule on the platform. Waits didn't get shorter. They just stopped feeling infinite. The problem was never the waiting. It was the not knowing.
Booking a haircut is exactly this. The customer's actual complaint isn't "you lack scheduling software." It's the interaction cost. Interrupt you, or wait, or call and hope. Every option is socially expensive for a haircut.
And on the other side of the counter: the owner's complaint is that 11pm customers message, hear nothing, and quietly book the salon down the street instead.
Both sides want the same thing. The transaction, minus the negotiation. That reframe is the product. Everything below is engineering in service of killing the negotiation.
What "just connect a calendar" turns out to mean
Here's the message that ends in a real booking:
"Hi! Do you have anything Saturday afternoon for a balayage?"
Five lines. To answer honestly - not "probably!", not "let me check and get back to you", but a slot that will survive contact with reality - the AI needs all of this:
- Who can perform it. A multi-staff salon isn't one calendar. It's N calendars, and not everyone does balayage.
- What's free. Existing bookings plus every external event on each staff member's personal Google Calendar. The dentist. The school run. Everything.
- What the business allows. Opening hours, blocked times, per-staff daily booking caps, and whether the slot is even long enough for the service plus buffer.
- Where the customer is. A Madrid salon with a Miami WhatsApp number needs two different "3pm"s.
- An answer in seconds. Because response time is the signal. A reply that lands while the customer is still holding the phone says "this business is alive." A perfect answer two hours later says "this will be a project."
That last point is why the AI lives in the message path, wired to the calendar, and not behind a "click here to book" link. The moment the customer has to open a second app, you've rebuilt the negotiation you were hired to remove.
This is the core promise of Conversify: calendar-wired answers in seconds, not "we'll get back to you."
The availability pipeline
Every booking-capable message runs through roughly this:
message → intent + service + preference(s)
→ candidate staff (skills, active status)
→ for each candidate:
working hours ∩ unblocked time
− calendar events (local + Google freeBusy)
− per-day booking cap for that staff member
− service duration + buffer
→ ranked slots (earliest match wins, preferences win)
→ offer 1-3 concrete options → customer picks → book
The part developers underestimate is always freeBusy. Here's the mistake I made first: caching calendars. Feels sensible. It isn't. Staff edit their calendars from their phones in the checkout line, and your cache is one edit behind, always. The fix is dumb - query freeBusy live for every candidate slot - and it wiped out the entire class of "I double-booked a haircut against my dentist appointment" complaints. Cost is an API call per candidate. Fine. Buy the API calls.
Blocked times are the quiet hero of the whole design. An owner drops "closed 2-3pm daily" as a blocked range and it applies everywhere - the AI conversation, the public booking page, the manual booking screen. One source of truth. I cannot stress this enough: if the bot, the booking page, and the staff each have their own idea of availability, you will ship a bot that cheerfully sells appointments during the owner's kid's birthday party. Ask me how I know.
Timezones you solve boringly and correctly. Store absolute times. Display the customer's local zone. "Saturday at 4pm" must read back to the customer as their 4pm. Sounds obvious. The first bug report where it isn't will burn a day of your life.
Confirmation, reminders, and the "no" that becomes a "yes"
Booking isn't the finish line. WhatsApp bookings have a specific failure mode: the customer says yes in chat, then never shows, because the appointment lives in a chat bubble that has scrolled away under a week of family group messages.
So the loop closes in three beats.
Instant confirmation. A proper WhatsApp template message - the utility-category kind, pre-approved so it can arrive outside the 24-hour service window - with date, time, staff name, and location.
A confirmation request the customer can literally answer "YES" or "NO". This isn't bureaucracy. It's a super-stimulus of reliability. The business is visibly keeping its act together.
A reminder before the appointment. And if it's cancelled, the slot returns to the pool immediately, and waitlisted requests for that exact window get first refusal.
That last part is where Sutherland earns his keep. In psycho-logic terms, a "no" is not an outcome. It's a failure of imagination.
The naive system hits Saturday-is-full and says "sorry, we're fully booked." Conversation dies. The customer's mood flips from "I want this salon" to "I want this to be over," and they call whoever answers next.
The right system treats full-ness as a reframe. Offer the nearest alternative. Offer the same stylist on a different day. Capture the request against the waitlist for cancelled slots. The customer now experiences a business fighting to fit them in, which is the entire signal an appointment was supposed to deliver in the first place. Same calendar. Same slots. Different problem. That is the trick Sutherland keeps pointing at, and most scheduling software has never heard of it.
Same discipline for escalation: if a human has stepped into the thread, the AI stands down before booking anything. It re-checks with every reply, mid-conversation. An AI that books over a human's active conversation signals chaos, and chaos is the one signal you can't afford.
The stack this runs on
Next.js and Firebase App Hosting. The WhatsApp Business API for templates and webhooks. Genkit on Google's Gemini for the understanding layer. Firestore for state. Google Calendar APIs for the truth about time.
One rule above all of it: the calendar is the source of truth, and the AI is a very polite client of it. It never invents availability. It only resolves it.
That sentence is most of the product, honestly. A receptionist that hallucinates Saturday openings is worse than no receptionist, because it converts a missed message into a broken promise with a witness.
If this is your problem too
I build Conversify - an AI receptionist that answers, books and sells over WhatsApp, Instagram, Messenger, email and web chat, for people who have a business to run more than an inbox to administer. Self-serve. Minutes to set up, not months. 14-day free trial. And it always tells the person it's AI.
Next post in this series, if it survives the fix list: handling WhatsApp Cloud API webhooks without deduplicating yourself into despair.












