Hi everyone! I could use some advice from people who’ve delivered WhatsApp AI agents to clients.
There are plenty of tutorials on building the agent, but I’m curious about the part the client actually uses: reading conversations, stepping in to reply, and handing things back to the agent.
In your last project, what did you use for that? A shared inbox, the WhatsApp Business app, something custom?
I’d love to hear what worked well—and what turned out to be more hassle than expected. Even a short example would really help.
The short version: it comes down to who answers the conversation at 2am, and that decides the tool.
The WhatsApp Business app is only viable if one person handles everything - it can’t share a number with the Cloud API, so it’s out the moment the bot and a human need the same number. That catches people after they’ve already onboarded the client on it.
For client work I’d repeat: Cloud API on the number, plus a shared inbox that has a real API. Self-hosted Chatwoot gives agent assignment, canned replies and full history, and its webhooks let the workflow drive the handoff.
The handoff is the bit worth engineering: keep a “human owns this conversation” flag keyed on the customer’s wa_id, set it whenever an agent sends an outbound message, and have the workflow check it before the bot replies so it doesn’t talk over the human. Clear it on a timer or a “resume bot” action in the inbox - a bot that resumes on its own mid-conversation is the complaint that gets you fired.
What turned out to be more hassle than expected: the 24-hour customer service window (outside it you can only send pre-approved templates), per-seat pricing clients grumble about, and template approval delays for anything unprompted. Prefer an inbox with documented webhooks over a reseller dashboard with a CSV export - the handoff is exactly where you’ll need to hook in.
I haven’t handed this over to a paying client yet (I’ve built it as a demo), so take this as the design I’d ship, not a war story.
The part that matters most is a conversation state, not a tool. I store one row per chat with a mode: bot, human or paused, plus a last_human_message_at timestamp. The n8n workflow checks that row before the agent replies. If a person has replied, the mode flips to human and the bot stays silent. It resumes after a timeout or when the owner sends a short command like /bot. Without this, the AI and the owner talk over each other.
For the client’s side, I’d pick by who they are:
Solo owner or a very small team: keep the number on the WhatsApp Business app and check if the number can use Cloud API alongside it. They keep the interface they already know.
A team with several people replying: a shared inbox such as Chatwoot connected to the Cloud API. It gives assignment, labels and history, and the bot posts into the same thread.
Always add a “needs a human” alert (Slack, email or a WhatsApp message to the owner) with a one-line summary of the conversation so far, so they don’t have to read the whole thread.
One surprise to plan for: WhatsApp’s 24-hour window. If a human takes over and the customer goes quiet, you can only restart with an approved template, so decide up front what the handback message looks like.
Curious what others settled on, especially for non-technical owners.
One from production: a small law firm, about 300 conversations a month, official WhatsApp API through YCloud.
The partners don’t get an inbox. They reply from WhatsApp on their own phones, on the same number the bot uses: Meta’s coexistence mode lets the Cloud API and the Business app share one number, so the “can’t share the number” limit mentioned above is worth rechecking with your provider. Every reply they send from the phone is logged as a team message and pauses the bot for that conversation.
The part we had to rebuild was the alerts. 78% of leads ended up needing a person, and we pinged a partner on WhatsApp for every one of them, plus an “N people waiting” summary every 3 hours. It buried her.
What fixed it:
Three digests a day instead of instant pings: 09:00 with everything pending, urgent first; 13:00 and 17:00 with only what’s new; nothing from 19:00 until the next morning.
A reaction from a partner on the alert counts as “handled” and pauses the bot for 30 minutes. No app to open.
She can text “clear the alert for X” or “remind me about X” instead of using a dashboard.
The bot rates each lead cold, warm, hot or urgent, and only hot and urgent reach the team.
One bug worth checking in yours: after a human answered, the customer’s “ok, thanks” was read as “nobody replied yet” and reopened the case. Filter polite closers before you decide a conversation is still pending.
For a non-technical owner, I’d design the alerts before picking the inbox.
Two things I’d add to the handoff flag others described, because they’re where it breaks in practice:
Race condition. The flag check before the LLM call isn’t enough: the agent can take 5-10 s to generate, and a human may reply in that window. Check the flag again right before the send node and drop the draft if it flipped. Also debounce rapid-fire customer messages (wait ~3-5 s, merge them into one turn), otherwise the bot answers three half-messages separately.
Webhook retries. Meta re-delivers webhooks if you respond slowly, so dedupe on the WhatsApp message id (a unique key in Postgres or a data table) before anything else, and return 200 immediately, then process async. Without it you get double replies and, worse, double bookings.
On the number question: the Business app and Cloud API can’t share a number in the classic setup, but it’s worth checking whether the client’s number is eligible for Meta’s coexistence mode, since availability depends on country. If so, a solo owner keeps the app they know, and you handle app-sent messages arriving on your webhook as the “human took over” signal.
For non-technical owners I’d skip dashboards entirely: send a Telegram/WhatsApp alert with a one-line summary and “take over / resume bot” buttons that flip the state row. They never open an inbox. And add an error workflow that alerts you (not the client) when a send fails or a template is rejected, so failures don’t surface as “the bot ignored my customer”.
Happy to compare notes on the state-row schema if useful.