WhatsApp Agent — Architecture

Architecture

Sign in to view this page.

Key Components
FileResponsibility
backend/server.pyFlask app, Evolution webhook route, group message two-step logic, Evolution management endpoints, dashboard API, auth middleware
backend/whatsapp.pyThin wrapper delegating to Evolution API provider
backend/providers/evolution.pyEvolution API: webhook parsing (group vs DM via @g.us/@s.whatsapp.net JIDs), message sending, instance management (create, status, QR code)
backend/llm.pyBrightWrapper integration: generate_reply() for responses, evaluate_relevance() for group scoring
backend/models.pySQLite database: conversations (with is_group/group_id), messages, settings, llm_decisions tables
frontend/dashboard.htmlAdmin SPA: conversations, Evolution connection/QR code, decision chain viewer, settings (provider, bot name, threshold, system prompt)
Evolution API Instance

An Evolution API "instance" is a single WhatsApp Web session — one phone number linked via QR code scan. Evolution API (built on the Baileys library) connects to WhatsApp's servers using the same WebSocket protocol as the WhatsApp Web browser app. Our instance is named whatsapp-agent.

The instance maintains a persistent connection to WhatsApp and stores session authentication keys in PostgreSQL. When a message arrives, Evolution API forwards it as a webhook POST to our Flask app. When we want to send a reply, we call Evolution API's REST endpoint, and it sends the message through the existing WebSocket connection.

This is an unofficial integration using the WhatsApp Web protocol via Baileys, which supports both group chats and 1:1 messaging.

External Services
  • Evolution API (localhost:9209) — WhatsApp Web bridge using Baileys library; manages the WebSocket connection to WhatsApp servers and exposes a REST API for sending messages and managing instances
  • BrightWrapper (brightwrapper.com/api/chat) — stateless LLM chat; called twice for group messages (relevance + response), once for DMs
  • AuthReturn (authreturn.com) — Cognito JWT authentication for the admin dashboard
Design Decisions
  • Evolution API (Baileys) — Uses the WhatsApp Web protocol for full group chat and 1:1 support. The trade-off is that this is unofficial (WhatsApp ToS risk), but it enables group chat functionality that the official Business API doesn't support.
  • Two-step LLM for groups — in a busy group, the bot shouldn't respond to every message. Step 1 asks "should I respond?" (cheap, fast). Only if confidence exceeds the threshold does Step 2 generate an actual response. Both decisions are logged for threshold tuning.
  • Provider abstractionwhatsapp.py wraps the Evolution API provider with a stable interface: parse_incoming_message(), send_message(), is_configured().
  • ThreadPoolExecutor over Celery/Redis — at personal-bot scale, 5 workers handle concurrent conversations without infrastructure overhead
  • Immediate 200 response — webhooks return 200 before processing to prevent retries from slow LLM calls
  • 20-message context window — balances conversation quality against token cost; older messages are not sent to the LLM
  • Message splitting at paragraph boundaries — WhatsApp's 4096-char limit would silently truncate; we split proactively at natural breakpoints
  • SQLite with WAL — single-file database sufficient for this scale, WAL allows concurrent dashboard reads during message processing
Infrastructure
  • WhatsApp Agent: systemd whatsapp-agent.service, runs as app_whatsapp_agent on port 9208
  • Evolution API: systemd evolution-api.service, runs as app_evolution_api on port 9209, internal only (no nginx exposure)
  • PostgreSQL: systemd postgresql.service, database evolution_db for Evolution API session/message storage
  • Web server: nginx serves static files directly, proxies /api /dashboard /architecture to Flask
  • SSL: Certbot, auto-renewal
  • Secrets: /opt/secrets/evolution-api.json (Evolution API key + DB creds), /opt/secrets/brightwrapper.json (BrightWrapper API key)
  • Data: /home/app_whatsapp_agent/data/whatsapp_agent.db (app data), PostgreSQL evolution_db (Evolution sessions)