Skip to content
BotServBotServ
WhatsAppBotAI BotOllamawhatsapp-web.jsBaileysSelf-Hosting

WhatsApp Bot with Local AI

Build WhatsApp bots with Ollama: compare WhatsApp Business API vs. whatsapp-web.js, QR pairing, groups, legal notes.

S

schutzgeist

7 min read
WhatsApp Bot with Local AI

WhatsApp Bot with Local AI

What this article covers

  • Two approaches to building a WhatsApp bot: official Business API vs. unofficial libraries.
  • Practical example using whatsapp-web.js/Baileys with Ollama.
  • QR pairing, session persistence, group operation.
  • Legal and technical risks, and when to use which approach.
  • Extensions: RAG, images, voice messages, moderation.

Introduction

WhatsApp is Germany’s most popular messenger, but it’s also the least bot-friendly platform. There is no official free bot API for private accounts. Two paths exist:

  1. WhatsApp Business Platform (Cloud API): Official, stable, but complex. Requires a Meta Developer account, business verification, template messages that need approval, and costs per conversation.
  2. Unofficial libraries (whatsapp-web.js, Baileys): Simulate WhatsApp Web, work with regular accounts, but violate the terms of service and carry account suspension risk.

This article walks through both approaches and explains when each makes sense.

Common use cases

  • Internal team assistant: Employees query the bot in WhatsApp, internal documentation via RAG.
  • First-level customer support: Answer frequent questions, escalate complex issues to humans.
  • Notifications: Server alerts, appointments, order status delivered straight to your phone.
  • Family or club bot: Retrieve information, maintain lists, send reminders.
  • Public channels require caution: For public customer-facing channels, the Business API is the only safe option.

Approach 1: Unofficial Libraries (whatsapp-web.js / Baileys)

How it works

whatsapp-web.js (Node.js) and Baileys (TypeScript) speak the WhatsApp Web protocol. Your bot pairs like a second device via QR code. You need your own phone number (landlines work too: verification via call).

npm install whatsapp-web.js qrcode-terminal axios

Minimal bot with Ollama

const { Client, LocalAuth } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');
const axios = require('axios');

const client = new Client({
    authStrategy: new LocalAuth({ dataPath: './session' }),
    puppeteer: { headless: true, args: ['--no-sandbox'] }
});

client.on('qr', qr => qrcode.generate(qr, { small: true }));
client.on('ready', () => console.log('Bot bereit'));

client.on('message', async msg => {
    if (msg.fromMe || !msg.body) return;

    // Respond only to direct messages or mentions
    const chat = await msg.getChat();
    if (chat.isGroup && !msg.body.startsWith('!ki')) return;

    const prompt = msg.body.replace(/^!ki\s*/, '');

    try {
        const res = await axios.post('http://localhost:11434/api/chat', {
            model: 'llama3.1:8b',
            messages: [
                { role: 'system', content: 'Du bist ein hilfsbereiter Assistent. Kurze Antworten.' },
                { role: 'user', content: prompt }
            ],
            stream: false
        });
        await msg.reply(res.data.message.content);
    } catch (e) {
        await msg.reply('Fehler: Ollama nicht erreichbar.');
    }
});

client.initialize();

Start it, scan the QR code in the terminal with your phone (WhatsApp > Linked devices). Done.

Baileys variant (no headless browser)

Baileys implements the protocol natively, no Chromium required, lower RAM footprint:

const { makeWASocket, useMultiFileAuthState } = require('@whiskeysockets/baileys');

const { state, saveCreds } = await useMultiFileAuthState('./session');
const sock = makeWASocket({ auth: state });

sock.ev.on('creds.update', saveCreds);
sock.ev.on('messages.upsert', async ({ messages }) => {
    const m = messages[0];
    if (!m.message?.conversation || m.key.fromMe) return;
    // Call Ollama as above, then:
    await sock.sendMessage(m.key.remoteJid, { text: 'Antwort...' });
});

The risks, plain and simple

  • Account suspension: Meta regularly bans unofficial clients. Use a separate number, never your main one.
  • Pairing breaks: WhatsApp Web sessions expire. Your bot needs to handle re-pairing.
  • No guaranteed uptime: Unsuitable for business customers. Your number can disappear at any time.
  • E2EE remains intact: Messages stay end-to-end encrypted. The bot decrypts locally, which is good for privacy.

Approach 2: WhatsApp Business Platform (Cloud API)

Setup overview

  1. Meta Developer account > create app > add “WhatsApp”
  2. Verify a test number or your own business number (requires business verification)
  3. Set up a webhook endpoint on your server
  4. Generate a permanent access token

Receive messages (webhook)

from flask import Flask, request
import requests

app = Flask(__name__)
VERIFY_TOKEN = "dein-verify-token"
WA_TOKEN = "dein-permanent-token"
PHONE_ID = "deine-phone-number-id"

@app.route("/webhook", methods=["GET"])
def verify():
    if request.args.get("hub.verify_token") == VERIFY_TOKEN:
        return request.args.get("hub.challenge")
    return "Forbidden", 403

@app.route("/webhook", methods=["POST"])
def receive():
    data = request.json
    for entry in data.get("entry", []):
        for change in entry.get("changes", []):
            for msg in change.get("value", {}).get("messages", []):
                text = msg["text"]["body"]
                sender = msg["from"]
                answer = ask_ollama(text)
                send_message(sender, answer)
    return "OK", 200

def ask_ollama(prompt):
    r = requests.post("http://localhost:11434/api/chat", json={
        "model": "llama3.1:8b",
        "messages": [{"role": "user", "content": prompt}],
        "stream": False
    })
    return r.json()["message"]["content"]

def send_message(to, text):
    requests.post(
        f"https://graph.facebook.com/v19.0/{PHONE_ID}/messages",
        headers={"Authorization": f"Bearer {WA_TOKEN}"},
        json={
            "messaging_product": "whatsapp",
            "to": to,
            "type": "text",
            "text": {"body": text[:4000]}
        }
    )

Key limitations of the Business API

  • 24-hour window: Free messages only if the user wrote in the last 24 hours. Otherwise only template messages (approved by Meta).
  • Costs: Around 0.07-0.11 € per conversation in Germany (service conversations partly free since 2024, marketing higher).
  • Verification required: Without a verified business account, you’re limited to 250 conversations per 24 hours.

Extensions

RAG: Document knowledge

Same pattern as the Telegram bot. Incoming message > embedding > Qdrant search > context to Ollama:

def ask_ollama_with_rag(prompt):
    context = search_qdrant(prompt)   # Top-3-Chunks
    r = requests.post("http://localhost:11434/api/chat", json={
        "model": "llama3.1:8b",
        "messages": [
            {"role": "system", "content": f"Nutze diesen Kontext:\n{context}"},
            {"role": "user", "content": prompt}
        ],
        "stream": False
    })
    return r.json()["message"]["content"]

Transcribe voice messages

WhatsApp voice messages (Opus OGG) > local Whisper > text > Ollama. With whatsapp-web.js: call msg.downloadMedia(). With Business API: use Media ID to download from the endpoint.

Group Operation

  • In groups, respond only to a prefix (!ki) or direct mentions, otherwise the bot reads every group chat message.
  • Admin list for moderation commands (!kick, !warn).
  • Group ID whitelist: the bot should only be active in approved groups.

Human Handoff (Business API)

Use the “bot first, human fallback” pattern. When the AI is uncertain, escalate the conversation to a team member via internal notification or a ticket system like Chatwoot.

Deployment

whatsapp-web.js requires more resources due to Puppeteer and Chromium:

FROM node:20-slim
RUN apt-get update && apt-get install -y chromium fonts-liberation \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
CMD ["node", "bot.js"]
services:
  whatsapp-bot:
    build: .
    restart: always
    volumes:
      - wa_session:/app/session   # Persist session!
    environment:
      - OLLAMA_URL=http://ollama:11434

volumes:
  wa_session:

Critical: Use a session volume. Without persistence, you’ll need to scan the QR code again on every restart.

  • GDPR: WhatsApp bots process phone numbers and chat content, which are personal data. For business use, document a privacy policy, a data processing agreement with Meta, and user consent.
  • Terms of Service: Unofficial libraries violate WhatsApp’s Terms of Service, risking account suspension. Tolerated for hobby projects, not for business use.
  • No Spam: Unsolicited messages violate competition law in Germany. Only respond to user requests.

Common Pitfalls

  • QR code expires: Session volume not persisted, or the phone went offline. Use LocalAuth with a volume.
  • Account suspended: Too many messages sent too quickly, or users report the bot. Implement rate limiting and 2-5 second response delays.
  • Chromium consumes RAM: whatsapp-web.js needs roughly 500MB or more. Alternative: Baileys (pure WebSocket protocol, no browser).
  • 24-hour window forgotten: The Business API refuses free messages after 24 hours. Use templates or require the user to message first.
  • Group IDs change: JIDs change when re-registering. Never hardcode group IDs.

Further Reading

Key Takeaways:

  • WhatsApp has no free bot API: unofficial libraries (suspension risk) or Business API (complex, paid).
  • Use whatsapp-web.js for hobby projects with a spare number; the Business API for customer contact.
  • Persist your session, otherwise you’ll scan the QR code on every restart.
  • Respect the 24-hour window with the Business API; templates require Meta approval.
  • For most self-hosters, Telegram is the more pragmatic choice.

FAQ

The official Business API is legal. Unofficial libraries violate WhatsApp’s Terms of Service. While not illegal in criminal terms, your account can be suspended. For hobbies using a spare number it’s acceptable, but business use requires the official API.

whatsapp-web.js or Baileys?

Baileys is lighter (no Chromium, roughly 100MB RAM) while whatsapp-web.js is simpler with better documentation. For long-term projects, Baileys is preferable.

How do I avoid suspension?

Use a spare number, never your primary one. Implement rate limits (maximum 20-30 messages per minute), add 2-5 second response delays, avoid mass messaging to strangers, and never send unsolicited messages.

What does the Business API cost?

Service conversations (user initiates): typically free up to roughly €0.03. Marketing and utility templates: roughly €0.07-0.11 per conversation. Additional fees may apply depending on BSPs like Twilio or 360dialog.

Is encryption preserved?

With unofficial libraries, yes. They decrypt locally like WhatsApp Web. With the Cloud API, end-to-end encryption terminates at Meta: Meta can theoretically read messages.

Are there simpler alternatives?

Yes: Telegram (free bot API, no approval process) or Signal (signal-cli, end-to-end encryption remains). WhatsApp only makes sense if your target audience is actually there.

Does the bot work without a phone?

Yes, since Multi-Device the bot runs even when your phone is offline (the session lasts roughly 14 days without phone contact, then re-pairing is needed). However, the phone must be online during initial setup.

Sources and Further Reading

Back to Blog
Share:

Related Posts