Skip to main content

Building a Chatbot Flow

A good chatbot resolves common requests instantly and hands off gracefully when it can't. This guide covers flow design, state, and the human fallback that keeps customers from getting stuck.

🚀 Quick Start​

curl -X POST https://api.afriroute.ai/api/v1/whatsapp/send \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+254712345678",
"type": "interactive",
"interactive": {
"type": "button",
"body": "Hi! What do you need?",
"buttons": [
{"id": "balance", "title": "Balance"},
{"id": "pay", "title": "Pay bill"},
{"id": "agent", "title": "Agent"}
]
}
}'

🧭 Flow Design​

Model the bot as a small state machine. Each state knows what it expects and where each input leads.

StateExpectsNext
welcomebutton idbalance / pay / handoff
pay_amountnumberpay_confirm
pay_confirmyes/nodone / welcome
handoff—human queue

💾 Managing State​

Store per-user state keyed by phone number with a TTL so abandoned sessions expire.

import time

def get_state(phone): return store.get(f"chat:{phone}")

def set_state(phone, state, data=None):
store.set(f"chat:{phone}", {"state": state, "data": data or {}, "ts": time.time()}, ttl=1800)
async function handleMessage(from, text) {
const session = (await getState(from)) || { state: 'welcome' };
switch (session.state) {
case 'welcome': return handleWelcome(from, text);
case 'pay_amount': return handleAmount(from, text, session);
default: return sendWelcome(from);
}
}

🔀 Fallback to Human​

Never trap the user. Trigger a hand-off on any of these signals:

  • User taps an "Agent" button.
  • The bot fails to understand the same input twice.
  • The user types "help", "agent", or "human".
  • A timeout with no resolution.
let misunderstood = session.data.misses || 0;
if (!matchedIntent) {
misunderstood += 1;
if (misunderstood >= 2) return routeToHuman(from);
await setState(from, session.state, { ...session.data, misses: misunderstood });
}

When handing off, pass conversation context (last intent, order ID) to the agent so the customer doesn't repeat themselves.

💡 Best Practices​

  • Keep menus shallow — 3 buttons or a single list, not nested trees.
  • Always offer "Main menu" and "Agent" at every step.
  • Expire idle sessions (~30 min) and greet returning users fresh.
  • Confirm before actions that move money or change data.
  • Log every transition for debugging and analytics.
  • Degrade to SMS when WhatsApp delivery fails.

⚠️ Common Pitfalls​

  • Storing state in memory — it's lost on restart and across instances. Use a shared store.
  • Free-text parsing when buttons would do — buttons remove ambiguity.
  • Silent dead ends where unknown input gets no response.

Last Updated: May 2026