DEV Community

Apex Flow
Apex Flow

Posted on

WhatsApp lead bot with human handover

Most WhatsApp bot tutorials stop at "receive a message, call an LLM, reply". The parts that matter in production are the ones around that: when the bot must stay quiet, what happens when the model API is down, and how you avoid pestering people. This is how those parts work in the bot we run for ApexFlow Digital in Dubai.

Stack

  • Node.js with @whiskeysockets/baileys for the WhatsApp connection
  • OpenRouter for the LLM calls
  • JSON files on disk for session, lead and follow-up state
  • A small HTTP server for a health check

Caveat first. Baileys speaks the WhatsApp Web protocol and is not the official Business Cloud API. It is quick to set up and needs no template approval, but it is unofficial and WhatsApp can restrict numbers that behave like spam. If you need guaranteed uptime or high volume, use the Cloud API instead. The patterns below apply to both.

1. Keep replies short by design

The system prompt limits replies to one to three sentences and says to share a booking link only when the prospect shows intent (asks about price, scope, timing or a call). Long, eager bot messages are what make people leave.

2. Model fallback

A single model endpoint will fail at some point. The bot holds an ordered list of six models and tries them in turn with a timeout per call. If every model fails, it sends a short fixed reply instead of going silent.

for (const model of FALLBACK_MODELS) {
  try {
    const res = await callOpenRouter(model, history, { timeoutMs: 12000 });
    if (res.ok) return res.text;
  } catch (_) { /* try the next model */ }
}
return EMERGENCY_REPLY;
Enter fullscreen mode Exit fullscreen mode

3. Human handover that sticks

The bot checks each incoming message for handover phrases in English and Arabic ("speak to a person", "call me", and so on). On a match it:

  1. adds the sender to a paused set, so the bot stops replying to that chat,
  2. writes the paused flag to disk, so a restart does not wake the bot up in the middle of a human conversation,
  3. alerts the owner on their own WhatsApp number with a link to the chat.

The persistence step is the one most tutorials skip. Without it, a deploy at the wrong moment puts a bot back into a conversation that a person had taken over.

4. An owner-controlled ignore list

Some contacts are friends, suppliers or existing clients. The owner can send /ignore (or ignore <number>) to the bot's number to exclude a contact, and /unignore to reverse it. The list lives in a JSON file and is checked before anything else, including voice-note transcription.

5. One follow-up, with limits

A scheduler checks leads periodically. It sends at most one follow-up, only when:

  • the lead has been silent for 24 to 72 hours,
  • the lead is not paused,
  • it is within business hours in Dubai (10:00 to 19:00).

After the single follow-up it never contacts that lead again, and the owner is notified each time one goes out. Two things I would change if building it again: store the follow-up copy in config rather than code, and add an explicit opt-out phrase.

6. Data and privacy

Chat history and lead profiles are stored locally as JSON. If your users are in the UAE, read Federal Decree-Law No. 45 of 2021 on personal data protection and decide where the data is stored and which processors see it, including the LLM provider. This is not legal advice. Check with counsel.

What I would do differently

  • Use the Cloud API and approved templates for anything customer-critical.
  • Put state in a database instead of JSON files once there is more than one instance.
  • Log every handover and every fallback, so you can see how often the model fails.

We build and run these for UAE businesses. Details at ApexFlow Digital.

Top comments (0)