Most "AI WhatsApp bot" tutorials pass the customer's message to an LLM and send back whatever it writes. Fine for a demo. For a real small business it's a liability, because the model will quote a price, a warranty or an opening hour that doesn't exist.
I built a WhatsApp FAQ bot for small service businesses (the sample data is an aircon-servicing company) with one rule: the LLM never writes the reply. It only gets to pick which stored answer to send.
The LLM picks an ID
The FAQ lives in a table, a Google Sheet or a CSV, with id, question, keywords, answer.
Every message first goes through plain keyword scoring. Most messages ("how much for a general service?") match without any AI, which is fast and costs nothing. Only when nothing matches does the bot send the question plus the list of FAQ ids and questions to the LLM, asking for one id or null and nothing else.
The response is then checked. If it isn't exactly a known id, it counts as unknown. A valid id means the stored answer goes out word for word. Unknown, null, free text or an API error all trigger a human handoff: the customer gets one "a staff member will reply" message, the owner gets a WhatsApp alert, and the bot stays quiet in that chat until someone resumes it.
So the worst case is a human answering a question the bot could maybe have handled. I'll take that over a made-up price.
The rest of the flow
Customer message -> Webhook (GET verify / POST messages)
-> X-Hub-Signature-256 HMAC check (bad signature -> 401, nothing processed)
-> Dedupe on message id (Meta retries webhooks)
-> STOP / START opt-out handling
-> Chat in human handoff? -> stay silent
-> Asked for "agent / human" or sent a photo/voice note? -> handoff
-> FAQ match (keywords) -> fallback: LLM picks an FAQ id
-> Reply via Graph API POST /{phone_number_id}/messages
Some things that bit me:
Check the signature before anything else. Meta signs every webhook with your app secret, and in n8n you need the raw body for the HMAC, so turn on the raw body option in the Webhook node.
Dedupe on the message id. Meta retries when you're slow, and without dedupe a customer gets the same answer two or three times.
Handle STOP and START with a confirmation message. Opted-out chats get nothing after that, reminders included.
I kept the Code node logic in separate .js files and generated the workflow JSON from them, so the logic could be unit tested outside n8n. The workflow then passed a 16/16 live run on a real n8n instance against a mocked Graph API.
Booking reminders
The same workflow can send an approved WhatsApp template to customers whose booking starts within 24 hours, once per booking, skipping anyone who opted out. Outside the 24-hour customer-service window WhatsApp only allows template messages, so it has to be a template.
Code
A free, smaller version (keyword matching plus the OpenAI id-picking fallback) is in the official n8n template library: Answer WhatsApp FAQs with keyword matching and an OpenAI fallback.
The full kit adds signature checks, dedupe, opt-out, handoff and reminders, plus an equivalent Python FastAPI service (SQLite state, 31 pytest tests), a Docker + Caddy HTTPS setup and a step-by-step Meta Cloud API guide. It's $19 on Gumroad: WhatsApp AI Customer Service Bot kit.
For B2B outreach there's also an n8n + LangGraph lead-generation template where nothing is sent until you click Approve: B2B AI Lead Generation kit, $29.
The id-picking trick isn't specific to WhatsApp. I'd use it for ticket routing or intent classification too: anywhere you can limit the model's output to a set you can check.
Top comments (0)