Build a WhatsApp AI receptionist that can qualify and book
I build Settl, an expense-splitting app, solo with Claude, so I spend a lot of time deciding where an AI should act and where it should stop. A WhatsApp receptionist is a perfect example. The reply is easy. The rules around the reply are the real build.
Why a chat-only agent fails
A model that can only write messages is not a receptionist. It is autocomplete inside a green bubble. A useful receptionist needs four abilities:
- Answer from approved facts such as opening hours, services, locations, and starting prices.
- Collect one missing detail instead of dumping a questionnaire into the chat.
- Take a real action such as checking availability or creating a lead.
- Hand the conversation to a person when the request is risky, emotional, or unclear.
Meta now offers Business AI inside WhatsApp for eligible businesses, including answers, lead capture, appointments, and owner takeover. Test that first if it covers your workflow. Build a custom agent when you need your own calendar, CRM, pricing rules, or approval logic. See Meta's current Business AI overview and check the current WhatsApp Business rules before launch.
The smallest useful architecture
Keep the model in the middle, not in charge. Your code receives the message, loads the business facts and conversation state, asks the model for a decision, validates that decision, then runs an allowed action.
WhatsApp message
-> verify and normalize the incoming event
-> load business facts and conversation state
-> ask the model for one structured decision
-> validate the decision against allowed actions
-> reply, check availability, book, or hand off
-> save the result and message id
Save a tiny conversation state instead of trusting a long transcript:
{
"lead_name": "Maya",
"service": "30-minute consultation",
"preferred_day": "Tuesday afternoon",
"status": "needs_availability",
"human_required": false
}
Store that state against the WhatsApp conversation, not inside the prompt alone. A customer may reply five hours later, your process may restart, or the same event may arrive twice. The next turn should begin from the saved state and the verified message id. Keep the original message beside any extracted value so a person can see why the agent chose a service or preferred day.
There should also be one clear owner for the business facts. A receptionist that reads three old price lists will eventually quote the wrong one. Give every fact a last-reviewed date, remove old versions from retrieval, and make an unknown fact trigger a question or handoff. Silence is safer than a confident guess.
The receptionist prompt
The prompt should define the job, the source of facts, and the stop conditions. It should not ask the model to "be helpful" and hope for the best.
You are the WhatsApp receptionist for this business.
Your jobs are:
1. Answer only from BUSINESS_FACTS.
2. Ask one short question when a required booking detail is missing.
3. Use check_availability before offering any time.
4. Use create_booking only after the customer confirms an offered time.
5. Hand off complaints, refunds, custom pricing, threats, emergencies, and uncertainty.
Never invent a price, policy, service, discount, or appointment time.
Keep each reply under 45 words. Ask one question at a time.
Return one JSON decision using the allowed actions below.
Give it a narrow action contract
Do not let the model call arbitrary functions. Give it a short list and validate every field in code before anything runs.
{
"action": "reply | ask | check_availability | create_booking | handoff",
"message": "text shown to the customer",
"reason": "short internal reason",
"booking": {
"service": null,
"start": null,
"timezone": null
}
}
Your code should reject an unknown action, an unverified time, a price outside your price table, or a booking without explicit confirmation. The model proposes. Your application decides.
The handoff rules
Human handoff means the agent stops replying and routes the conversation to a person. Make the rule visible and boring. That is what makes it safe.
- Immediate handoff: complaints, refunds, safety issues, legal threats, abusive messages.
- Handoff after one failed clarification: unclear request, unsupported language, missing fact.
- Never hand off: a simple answer that exists word for word in the approved facts.
- Customer control: if the customer asks for a person, stop the agent immediately.
The handoff message should set an expectation without pretending someone is already typing.
I am passing this to Rahul because it needs a human decision. Your details are saved, and he will
reply during business hours. I will stop sending automated replies in this conversation.
A handoff is incomplete until someone can actually find it. Save the reason, the last customer message, the details already collected, and the promised response window. Put it in the same queue your team already checks. When the person replies, mark the conversation as human-owned so the agent does not jump back in halfway through.
Test the awkward conversations
Test behaviour, not eloquence. A beautiful answer that books a fake time is a failed test.
Test 1: "How much is the premium package?"
Expected: quote only an approved price or hand off.
Test 2: "Book me tomorrow at 3."
Expected: check the real calendar, offer the time only if free, then wait for confirmation.
Test 3: "I want a refund and I am furious."
Expected: acknowledge briefly, hand off, and stop automated replies.
Test 4: send the same incoming message twice.
Expected: one reply and one action, not duplicate bookings.
Use fictional phone numbers and a test calendar until every branch passes. Also test malformed events, delayed events, and repeated events before connecting a real business number.
Review failed conversations weekly. Count fake-answer attempts, repeated questions, abandoned bookings, and handoffs that waited too long. Do not optimize for the number of automated replies. Optimize for correct answers, completed actions, and clean stops. A shorter conversation that reaches the right human is a win.
Run this on your codebase
Inspect this codebase and design the smallest safe WhatsApp receptionist agent.
1. Find the current HTTP routes, database layer, calendar integration, and messaging code.
2. Do not edit anything yet. List the exact files you would reuse or add.
3. Define a conversation-state object with only the fields needed to qualify and book.
4. Define these allowed decisions: reply, ask, check_availability, create_booking, handoff.
5. Write a system prompt that answers only from approved business facts.
6. Add validation so the model cannot invent prices, times, actions, or required fields.
7. Add a human-handoff state that blocks further automated replies.
8. Make repeated incoming message ids produce no duplicate reply or booking.
9. Write tests for a normal booking, unavailable time, refund request, unknown fact,
malformed event, and duplicate delivery.
10. Return the proposed file tree, data shapes, prompt, and test plan first.
End with the one command or prompt I should use to begin the smallest implementation slice.
Download the runnable pack
This is a complete starter for the workflow in this guide, not a screenshot. The safe default validates fictional input and returns a deterministic, reviewable draft. It does not publish content, message a customer, change pricing, edit production data, or execute another external action.
- Download the complete pack
- Import the n8n workflow
- Open the sample input
- See the expected dry-run result
- Run with Docker Compose
- GitHub validation workflow
- GitHub manual run workflow
Run the downloaded folder locally:
npm test
npm run validate
npm run sample
cp .env.example .env
docker compose up --build
The ZIP includes an importable n8n webhook, a dependency-free Node 22 service, Docker Compose, GitHub Actions validation and manual-run workflows, fictional sample input, expected dry-run output, and tests. If you later enable Claude, keep the API key in the service environment. The n8n export contains no credentials. Live mode still returns a draft for approval.