WhatsApp has over 2 billion users. In India alone, it's the primary communication tool for businesses and customers. An AI chatbot on WhatsApp means 24/7 customer support, instant responses, and automated lead generation — all on the platform your customers already use.
This guide walks you through building a production-ready AI WhatsApp chatbot from scratch.
A WhatsApp chatbot that:
You'll get:
Your chatbot needs a URL where WhatsApp can send incoming messages. For local development, use ngrok:
# Install ngrok
npm install -g ngrok
# Start a tunnel to your local server
ngrok http 3000
Copy the HTTPS URL (like https://abc123.ngrok.io) — you'll need it for the webhook setup.
In the Meta Developer Portal:
https://your-url.ngrok.io/webhookmy_verify_token_123)messages events
mkdir whatsapp-ai-bot
cd whatsapp-ai-bot
npm init -y
npm install express axios openai dotenv
Create .env:
WHATSAPP_TOKEN=your_permanent_access_token
PHONE_NUMBER_ID=your_phone_number_id
VERIFY_TOKEN=your_verify_token
OPENAI_API_KEY=your_openai_key
PORT=3000
Create index.js:
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const OpenAI = require('openai');
const app = express();
app.use(express.json());
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// Store conversation history per user
const conversations = {};
// Webhook verification (GET request from Meta)
app.get('/webhook', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN) {
res.status(200).send(challenge);
} else {
res.sendStatus(403);
}
});
// Handle incoming messages (POST request from Meta)
app.post('/webhook', async (req, res) => {
const body = req.body;
if (body.object === 'whatsapp_business_account') {
const entry = body.entry?.[0];
const changes = entry?.changes?.[0];
const message = changes?.value?.messages?.[0];
if (message && message.type === 'text') {
const userMessage = message.text.body;
const userPhone = message.from;
console.log(`Message from ${userPhone}: ${userMessage}`);
const aiResponse = await getAIResponse(userPhone, userMessage);
await sendMessage(userPhone, aiResponse);
}
}
res.sendStatus(200);
});
// Send message via WhatsApp API
async function sendMessage(to, text) {
try {
await axios.post(
`https://graph.facebook.com/v18.0/${process.env.PHONE_NUMBER_ID}/messages`,
{
messaging_product: 'whatsapp',
to: to,
text: { body: text }
},
{
headers: {
'Authorization': `Bearer ${process.env.WHATSAPP_TOKEN}`,
'Content-Type': 'application/json'
}
}
);
} catch (error) {
console.error('Error sending message:', error.response?.data || error.message);
}
}
// AI response with conversation memory
async function getAIResponse(userPhone, userMessage) {
if (!conversations[userPhone]) {
conversations[userPhone] = [
{
role: 'system',
content: `You are a helpful customer support assistant for a business.
Be concise, friendly, and professional.
If you don't know something, say so honestly.
Keep responses under 3 paragraphs.
The business is based in Raipur, Chhattisgarh, India.`
}
];
}
conversations[userPhone].push({ role: 'user', content: userMessage });
// Keep only last 10 messages to manage token usage
if (conversations[userPhone].length > 11) {
conversations[userPhone] = [
conversations[userPhone][0],
...conversations[userPhone].slice(-10)
];
}
try {
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: conversations[userPhone],
max_tokens: 500
});
const aiReply = completion.choices[0].message.content;
conversations[userPhone].push({ role: 'assistant', content: aiReply });
return aiReply;
} catch (error) {
console.error('AI Error:', error.message);
return "I'm having trouble processing your request right now. Please try again in a moment.";
}
}
app.listen(process.env.PORT, () => {
console.log(`Server running on port ${process.env.PORT}`);
});
If you prefer running AI locally (no API costs, full privacy), replace the OpenAI code with Ollama:
// Replace the OpenAI import and getAIResponse function:
async function getAIResponse(userPhone, userMessage) {
if (!conversations[userPhone]) {
conversations[userPhone] = [];
}
conversations[userPhone].push(userMessage);
try {
const response = await axios.post('http://localhost:11434/api/chat', {
model: 'llama3.1:8b',
messages: [
{
role: 'system',
content: 'You are a helpful customer support assistant. Be concise and friendly.'
},
...conversations[userPhone].map(msg => ({
role: 'user',
content: msg
}))
],
stream: false
});
const aiReply = response.data.message.content;
conversations[userPhone].push(aiReply);
return aiReply;
} catch (error) {
console.error('Ollama Error:', error.message);
return "I'm having trouble right now. Please try again shortly.";
}
}
Make sure Ollama is running: ollama run llama3.1:8b
Add logic to detect when a customer needs a human:
const HUMAN_ESCALATION_KEYWORDS = [
'speak to someone', 'human', 'manager', 'complaint',
'refund', 'angry', 'frustrated', 'cancel'
];
function needsHumanEscalation(message) {
const lower = message.toLowerCase();
return HUMAN_ESCALATION_KEYWORDS.some(keyword => lower.includes(keyword));
}
// In your webhook handler, before calling getAIResponse:
if (needsHumanEscalation(userMessage)) {
await sendMessage(userPhone,
"I understand you'd like to speak with someone. Let me connect you with our team. A human agent will respond shortly."
);
// Log for human follow-up
console.log(`ESCALATION NEEDED: ${userPhone} - "${userMessage}"`);
return;
}
WhatsApp supports interactive buttons for quick replies:
async function sendButtons(to, bodyText, buttons) {
await axios.post(
`https://graph.facebook.com/v18.0/${process.env.PHONE_NUMBER_ID}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'interactive',
interactive: {
type: 'button',
body: { text: bodyText },
action: {
buttons: buttons.map((btn, i) => ({
type: 'reply',
reply: { id: `btn_${i}`, title: btn }
}))
}
}
},
{
headers: {
'Authorization': `Bearer ${process.env.WHATSAPP_TOKEN}`,
'Content-Type': 'application/json'
}
}
);
}
// Usage:
await sendButtons(userPhone, "How can I help you today?", [
"View Services", "Get Pricing", "Contact Us"
]);
function isWithinBusinessHours() {
const now = new Date();
const indiaTime = new Date(now.toLocaleString('en-US', { timeZone: 'Asia/Kolkata' }));
const hour = indiaTime.getHours();
const day = indiaTime.getDay();
// Monday-Saturday, 9 AM - 7 PM IST
return day >= 1 && day <= 6 && hour >= 9 && hour < 19;
}
// In webhook handler:
if (!isWithinBusinessHours()) {
await sendMessage(userPhone,
"Thanks for reaching out! Our business hours are Mon-Sat, 9 AM - 7 PM IST. We'll respond first thing tomorrow."
);
return;
}
npm install -g @railway/cli
railway login
railway init
railway up
Set environment variables in the Railway dashboard.
# On your server
git clone your-repo
cd whatsapp-ai-bot
npm install
npm install -g pm2
pm2 start index.js --name whatsapp-bot
pm2 save
pm2 startup
Update your webhook URL in Meta Developer Portal to your server's public URL.
The temporary token expires. To get a permanent one:
whatsapp_business_messaging permissionOnce your basic bot works, consider:
Building a WhatsApp chatbot is straightforward, but production deployment requires careful configuration, testing, and ongoing maintenance.
At The AI Server, we build custom WhatsApp AI chatbots for businesses across India. From simple FAQ bots to complex multi-language assistants — we handle the technical setup so you can focus on your customers. Get in touch for a free consultation.
Join 5,000+ founders and creators getting our weekly AI brief. Free tools, tutorials, and insider strategies — straight to your inbox.
Explore more from THE AI SERVER: