A voice agent that books appointments fails in small, boring ways. It reads a date as a number. It says "certainly!" at an awkward half-second delay. It hears "no, Thursday" as a yes. The fix is rarely a smarter model. It's a script that decides, turn by turn, what the agent says, what it needs back, and what happens when it doesn't get it.
This post gives you that script for an appointment-booking call, written as a prompt you can paste, plus the fallbacks that matter and a small linter I ran to catch the mistakes that creep into spoken turns. Everything here is an untested template: I didn't have a live voice stack or API keys in this session, so none of the prompts below were run against a model or a phone line. The linter was run, and its real output is below. For the general rules of voice prompting (no markdown, spelled-out numbers, short turns) read voice AI prompting for VAPI, ElevenLabs and Twilio first. This article is the booking-specific layer on top.
What does an appointment-booking call script need?
A booking call has six moments. Script each one separately, because each fails differently:
- Open and disclose. Who is calling, that it's an automated assistant, why.
- Confirm identity or intent. Right person? Right reason?
- Collect the slot. Day first, then time window, then exact time.
- Repeat back. Date, time, service, in words.
- Close. What happens next, then goodbye.
- Escape hatches. Silence, confusion, wrong person, request for a human, out-of-scope questions.
The escape hatches are where most of the real calls go, so they get as much space below as the happy path.
The opening: disclose, name the reason, ask one question
A caller who hears something long or odd first will often hang up or talk over it. Keep the opening to three jobs: say who you are, say you're automated, say why you called. Then ask one easy question.
Inbound version:
Thanks for calling Greenleaf Dental. I'm Asha, an automated assistant. I can book, move or cancel an appointment. What would you like to do?
Outbound version (a reminder or rebooking call):
Hello, this is Asha, an automated assistant calling from Greenleaf Dental. Am I speaking with the person who booked an appointment with us?
Two notes. First, don't invent a human name that implies a person; if you name the agent, pair it with "automated assistant". Second, make the opening question answerable with one word, because callers often start with "hello?" and talk over a long first line. If your platform lets you set a fixed first message that's spoken without the LLM, use it here, because it removes a model round-trip at the moment silence is most noticeable.
The system prompt: a booking agent you can paste
This is a template. Replace the bracketed items and the clinic details, and test it with your own calls.
ROLE
You are Asha, an automated voice assistant for [BUSINESS]. You book, move and cancel appointments by phone. You are not a person and must say so if asked.
SCOPE
You can: offer open slots from the tool result, book, reschedule, cancel, and give the address and opening hours listed below.
You cannot: give medical, legal or pricing advice, discuss anyone else's appointment, or promise anything not in this prompt. For those, offer a human callback.
HOW YOU SPEAK
- Plain spoken English. No lists, no markdown, no symbols.
- One idea and one question per turn. Maximum 30 words.
- Say dates and times in words: "Tuesday the fourteenth at eleven in the morning", never "14/10 11:00".
- No filler openers like "certainly" or "absolutely". Apologise at most once per problem.
- End every turn with the one thing you need from the caller, unless you are saying goodbye.
BOOKING FLOW
1. Find out what they want: book, move or cancel.
2. For a booking, ask which day suits them. Then ask morning or afternoon. Then call check_slots and offer at most two options.
3. When they choose, repeat back the day, date, time and service in words and ask "Is that right?" Only call book_slot after a clear yes.
4. After booking, say what happens next (a confirmation message, if the tool says one was sent) and say goodbye.
NEVER GUESS
If you did not clearly hear a name, date or time, ask again once, differently. If the second attempt fails, offer a human callback. Never invent a slot, a phone number or an address. Use only values from tools or this prompt.
HANDOFF
Offer a human at once if the caller asks for one, mentions symptoms, pain, an emergency, a complaint, a refund or a payment dispute, or if you fail to understand the same answer twice. For an emergency, say you cannot help with emergencies and tell them to contact local emergency services, then end the call.
FACTS
Address: [ADDRESS]. Hours: [HOURS]. Callback number to read out: [NUMBER, written in words].
Why each block is there. SCOPE stops the agent improvising answers to questions it can't answer correctly. NEVER GUESS exists because speech recognition mishears dates and names, and an agent that fills the gap confidently books the wrong day. The emergency line is the one place a script must not be creative. If your business is medical, have a qualified person write and approve that wording; this template is not clinical advice.
How do you handle the repeat-back so it actually catches errors?
Repeat-back only helps if the caller can say "no" easily. Compare:
Weak: "So that's Tuesday at eleven. Great, you're all set!"
Better: "Tuesday the fourteenth at eleven in the morning, for a cleaning. Is that right?"
The weak version asserts and closes. The better one names the weekday and the date together (so a misheard "fourteenth" versus "fourth" shows up), gives the service, and ends on a yes/no question. If the caller says "no", don't restart the whole flow. Ask which part is wrong: "Which part should I change, the day or the time?" Then change only that.
Then add one rule to the prompt for ambiguous answers: "Yes, but Thursday" is not a yes. Tell the model that a reply containing a correction is treated as a no for the part corrected.
Fallbacks: silence, confusion, wrong person, interruptions
Put these in the prompt as explicit rules, because models left alone improvise something long.
SILENCE
If the caller says nothing after your question, wait, then say: "Are you still there?" If still nothing, say: "I can't hear you. Please call us back on [NUMBER IN WORDS]. Goodbye." and end the call.
CONFUSION
If you do not understand, say: "Sorry, I didn't catch that. Could you say the day again?" Ask once more differently. After the second failure say: "Let me get a person to call you back. Can I confirm this is the best number?" Then offer the callback.
WRONG PERSON (outbound only)
If the person is not the one you want, do not share details about the appointment. Say: "Sorry to bother you. I'll try again later. Goodbye." and end the call.
INTERRUPTION
If the caller interrupts, stop. Do not repeat the interrupted sentence. Respond to what they said.
VOICEMAIL
If you reach voicemail, say only: "This is an automated message from [BUSINESS]. Please call us back on [NUMBER IN WORDS]. Goodbye." Do not leave appointment details.
The wrong-person and voicemail rules matter for privacy. An outbound call that reads appointment details to whoever picks up discloses something about a person. Keeping voicemails generic is a design choice, not a legal rule; if you handle health or financial information, ask a professional what you may say. For the legal side of recording and disclosing, see call recording consent and AI disclosure in India.
How do you lint spoken turns before they ever reach a caller?
Voice mistakes are mechanical enough to catch with a script. This is the linter I wrote and ran on 2026-10-09 with Python 3. It flags markdown characters, digits (which a TTS engine may read unpredictably), turns over 30 words, turns with no question or goodbye, filler openers and double apologies. The 30-word limit and the rules are my choices, not a standard.
import re
MAX_WORDS = 30
RULES = [
("markdown", lambda t: bool(re.search(r"[*#`]|^\s*[-•]\s", t, re.M))),
("digits", lambda t: bool(re.search(r"\d", t))),
("too_long", lambda t: len(t.split()) > MAX_WORDS),
("no_question_or_handoff", lambda t: "?" not in t and "goodbye" not in t.lower()),
("filler_opener", lambda t: bool(re.match(r"\s*(certainly|absolutely|of course|great question)", t, re.I))),
("double_apology", lambda t: len(re.findall(r"\b(sorry|apologi[sz]e)\b", t, re.I)) > 1),
]
for i, t in enumerate(turns, 1):
hits = [name for name, fn in RULES if fn(t)]
print(f"turn {i}: words={len(t.split()):2d} flags={hits or 'none'}")
I fed it seven turns I wrote by hand, some good and some deliberately bad (so these are illustrative inputs, not model outputs). Real output:
turn 1: words=20 flags=none
turn 2: words=17 flags=['digits', 'filler_opener']
turn 3: words=12 flags=['double_apology']
turn 4: words=11 flags=['markdown', 'no_question_or_handoff']
turn 5: words=13 flags=none
turn 6: words=36 flags=['too_long', 'no_question_or_handoff']
turn 7: words= 8 flags=none
Turn 2 ("Certainly! I'd be happy to help. Your slot is 10:30 on 14 October. Shall I confirm it?") trips two rules: a digit-formatted time and a filler opener. Turn 4 was a bulleted list with bold text, the exact thing that gets read aloud as symbols. Turn 6 was a 36-word address dump with no question. The linter is crude (it would flag "Is that 5 minutes?" for the digit), but as a check on a few hundred logged agent turns or in a CI test over scripted conversations, it finds the regressions before a caller does. If you already run prompt regression tests in CI, this slots in as one more assertion on the agent's text output.
A test checklist for the booking script
Before real callers, call it yourself and try these. They're the cases a transcript review tends to miss.
| Test | What should happen |
|---|---|
| Say nothing for ten seconds | One "are you still there", then a clean goodbye |
| "Book me Thursday... no wait, Friday" | Books Friday only, after repeat-back |
| Give a date that has no free slot | Offers the next two options, doesn't invent one |
| "Is the cleaning painful?" | Declines to advise, offers a human callback |
| "Are you a real person?" | Says it is automated, immediately |
| Mumble a name twice | Offers callback after the second failure |
| Interrupt mid-offer | Stops, answers, doesn't replay the offer |
| Answer the outbound call as someone else | No details shared, polite end |
Run each at least three times, because voice pipelines are not deterministic. And log what the speech recogniser heard, not just what the model said: many "model" failures are transcription failures. For checking the underlying answers against facts the agent should never invent, the approach in building a customer support agent without hallucinations applies directly.
When a script is the wrong tool
A rigid script suits narrow tasks with a small set of outcomes, like confirm, move, cancel. If callers mostly ask open-ended questions, the booking flow above will feel like a wall, and you're better off with a human or a retrieval-backed assistant that hands off at booking time. Also skip outbound automation entirely until you've checked the calling rules that apply to you, since unsolicited commercial calls are regulated in many countries. In India, TRAI's 2026 amendment asks senders to declare automated (A2P) calling to their telecom operator in advance; the consent and disclosure post has what I read and what to ask your provider. If a call must be scored afterwards, the call QA scoring prompts cover that.
Checklist to ship
- Opening discloses automation in the first turn.
- One idea, one question, under about 30 words per turn.
- Dates and times in words; read back day, date, time and service.
- Written rules for silence, confusion (two strikes), wrong person, voicemail and interruptions.
- Handoff triggers listed, with a fallback when no human is available.
- Linter run over scripted turns; real calls placed and reviewed with recogniser output.
- Facts like addresses and phone numbers hard-coded in the prompt or fetched by tool, never generated.



