Here's the scenario. Your AI booking agent takes a call, checks availability, and finds 2pm Tuesday open. It tells the caller: "I have 2pm Tuesday available — would you like to book that?" The caller says yes. Thirty seconds later, the event lands on the calendar.
Somewhere in those thirty seconds, a second call came in. A second agent instance checked the same calendar. 2pm Tuesday was still open — the first booking hadn't been written yet. The second agent offered it too. Both callers got a yes.
This is the double-booking race. It's not a bug in any individual tool. It's a structural gap in the way most AI booking workflows are built.
How the standard flow creates the window
In a typical Vapi + n8n + Google Calendar setup — and the same pattern appears in Cal.com-based workflows — the booking flow has two separate API calls:
- A
check_availabilitycall reads open slots from the calendar - A
create_bookingcall writes the event after the caller confirms
The gap between those two calls is the length of the confirmation exchange. "Does 2pm Tuesday work?" "Yes." "Great, you're all set." That's 10–30 seconds of wall-clock time where the slot looks open to anyone else who asks.
Checking availability and writing a booking aren't one step. There's a gap between them, and in that gap two callers can both get a yes.
On low-volume setups this rarely bites. But any business running multiple inbound lines, or an outbound AI campaign where dozens of calls go out simultaneously, will eventually hit it. One double-booking is enough to shake a client's confidence in the whole system.
Why "check it twice" doesn't close the window
A common workaround is adding a second availability check right before writing the booking. If the slot is still free, write it. If it's taken, tell the caller and offer another time.
This does shrink the window. But it doesn't eliminate it — two concurrent calls that reach that second check at the same moment will both see the slot as free and both proceed to write. You've made the race less likely, not impossible. And "less likely" is a bad guarantee when what's at stake is a client showing up to find someone else in their appointment slot.
The fix: hold the slot before you confirm it
The real fix is creating a tentative hold the moment you decide to offer a slot — before you say anything to the caller.
The slot isn't theirs until the booking write lands. Confirming it to the caller before that is a promise the system can't keep yet.
With a hold in place, the sequence looks like this:
- Agent checks availability — finds 2pm Tuesday open
- Agent creates a tentative hold on 2pm Tuesday with a short expiry (2–3 minutes)
- Agent tells the caller: "2pm Tuesday is available"
- Caller says yes → Agent converts the hold to a confirmed booking
- Caller says no, or call drops → Hold expires and the slot returns to available
If two agents hit step 1 at the same time, the second one to request the hold finds the slot already taken and reroutes. The conflict resolves at the hold write rather than the booking write — much faster, much harder to race.
Building this in n8n
Most calendar APIs don't have a native "hold" endpoint, so you create one. In Google Calendar, that means writing a short-duration event — something like "HOLD – 2pm Tue Aug 25" with a 3-minute end time — and storing its event ID. When the caller confirms, you update that event to the real booking. When the call ends without a confirmation, a cleanup trigger deletes it.
In n8n the implementation adds one node between your availability check and your confirmation message: a "create hold event" HTTP Request node. The node writes the hold, returns the event ID, and you store that ID in a variable so your end-of-call cleanup knows which event to target. Vapi fires an end-of-call webhook for every call outcome — connect your cleanup flow there, not just on successful bookings.
Cal.com handles this more cleanly. Their API supports a pending booking state, which works as a first-class hold — you create the booking as pending and either confirm or cancel it after the caller's response. If you have the option of using Cal.com over raw Google Calendar for a voice booking build, this is one reason to prefer it.
Who actually needs this
A solo tradesperson with one inbound number and five bookings a day won't see simultaneous calls often. The hold pattern is overkill there — add a fallback response instead ("I'll check and text you to confirm") and move on.
Businesses that need it: medical and dental offices, salons and spas running multiple chair bookings, home service contractors during storm season, gyms with popular class times, and any business running an outbound AI campaign — because outbound by design produces concurrent calls, and concurrent calls on the same calendar is exactly the scenario the pattern was built for.
If you're evaluating or building an AI voice booking system and want to know whether it handles this correctly, ask the builder: "What happens when two calls request the same slot at the same time?" The answer tells you whether the system was designed by someone who ran it in production. For AI automation builds we do here, the hold pattern goes in by default — it costs almost nothing to add and prevents the kind of failure that makes a client doubt the whole system.
See also: What an AI agent actually does — if you're still evaluating whether a voice booking agent is the right tool for your operation.
— Cole