Quick Start
- Web Mode (Easiest)
- Cloud API (Production)
- Python SDK
How It Works
Cloud API vs Web Mode
| Feature | Cloud API | Web Mode |
|---|---|---|
| Setup | Meta developer account + tokens | QR code scan |
| Stability | Official, stable | Reverse-engineered, may break |
| Webhooks | Required (public HTTPS) | Not needed |
| Groups | DMs only | DMs + groups |
| Risk | None | Account may be banned |
| Best for | Production | Development, personal use |
Inbound Media → Vision
Photos and documents sent by users are downloaded via the WhatsApp Graph API, validated, and forwarded to your agent’s vision capability — no placeholder text, no extra code.Vision agent — works out of the box
Any vision-capable model receives photos automatically. No configuration needed.Send a photo to your WhatsApp business number. The agent sees the image and replies.
How the security pipeline works
| Step | What happens |
|---|---|
| Download | Bot fetches media from WhatsApp Graph API using the media id |
| Size cap | File rejected if it exceeds max_inbound_media_bytes (default 20 MiB) |
| Magic-byte check | Content type verified against actual file bytes — not just the declared MIME |
| SSRF guard | URL fetch rejects non-http(s) schemes and private/loopback/link-local hosts |
| Forward | Validated path passed to agent.chat(attachments=[path]) |
What if my agent has no vision capability? The adapter checks whether
agent.chat() accepts attachments. If it doesn’t, the attachment is silently skipped and only the caption text is forwarded. Your existing text-only agents keep working with zero changes.Configuration
| Option | Type | Default | Description |
|---|---|---|---|
max_inbound_media_bytes | int | 20971520 (20 MiB) | Maximum size for inbound media. Set 0 to disable inbound media entirely. |
Interactive Messages
WhatsApp bots renderMessagePresentation natively — approval buttons, quick replies, and select menus become tappable Cloud API interactive messages, with no code change to existing agents.
How WhatsApp adapts
| Presentation content | Native shape |
|---|---|
| ≤3 non-URL buttons | Reply buttons (interactive.type == "button") |
>3 buttons or a select block | List message (interactive.type == "list", up to 10 rows) |
| URL buttons | Inlined into the body as Label: URL text links |
| Text only | Plain text send |
Message Filtering
By default, the bot responds only to self-chat — when you message your own number. This prevents it from replying to every conversation.Four Layers of Protection
Stale Message Guard
Messages older than when the bot connected are dropped. Prevents replaying old conversations on reconnect.
Self-Chat Check
Only messages where sender = chat JID pass.
Outgoing Guard
Your messages sent to other people’s chats are always blocked — even if they’re in the allowlist.
Allowlists
Optionally allow specific phone numbers or groups.
Expand Who Can Message the Bot
- Self-Only (Default)
- Specific Numbers
- Specific Groups
- Everyone
Filtering Matrix
| Scenario | Default | --respond-to 123 | --respond-to-groups g@g.us | --respond-to-all |
|---|---|---|---|---|
| Self-chat (your own number) | ✅ | ✅ | ✅ | ✅ |
| Your msg in someone else’s chat | ❌ | ❌ | ❌ | ✅ |
| DM from 123 | ❌ | ✅ | ❌ | ✅ |
| DM from 999 | ❌ | ❌ | ❌ | ✅ |
| Group g@g.us | ❌ | ❌ | ✅ | ✅ |
| Other group | ❌ | ❌ | ❌ | ✅ |
| Old/offline messages | ❌ | ❌ | ❌ | ❌ |
Phone numbers are normalized automatically —
+1-234-567-890 and 1234567890 match the same number.Python SDK Filtering
YAML Config
Built-in Commands
| Command | Description |
|---|---|
/help | Show available commands |
/status | Bot info, model, uptime |
/new | Reset conversation session |
CLI Options
| Flag | Description | Example |
|---|---|---|
--mode | cloud (default) or web | --mode web |
--respond-to | Allowed phone numbers (comma-separated or repeated) | --respond-to 123,456 or --respond-to 123 --respond-to 456 |
--respond-to-groups | Allowed group JIDs (comma-separated or repeated) | --respond-to-groups grp@g.us |
--respond-to-all | Respond to everyone | --respond-to-all |
--creds-dir | Credentials directory | --creds-dir ~/.myapp/wa |
--agent | Agent YAML config | --agent agents.yaml |
--memory | Enable conversation memory | --memory |
--web | Enable web search tool | --web |
--thinking | Extended thinking mode | --thinking high |
--auto-approve | Auto-approve tool calls | --auto-approve |
Architecture
Cloud-mode webhook verification: inbound Meta webhooks are HMAC-verified via the shared helper. Set
WHATSAPP_APP_SECRET (see Messaging Bots). Details: Webhook Verification.Key Components
| Component | Role |
|---|---|
| WhatsAppBot | Main bot class — handles lifecycle, filtering, routing |
| WhatsAppWebAdapter | Bridges neonize (Go) events to Python asyncio |
| Session Manager | Per-user conversation sessions with expiry |
| Message Filter | Stale guard → self-chat check → allowlist check |
| AI Agent | Your configured agent processes the message and responds |
Long Agent Turns
A long agent turn on WhatsApp — deep research, big refactor, a slow model — staysBUSY for as long as it is making any progress, and is never restarted mid-stream.
The gateway watches each channel’s health and only restarts a channel when it is genuinely wedged. Progress comes from inbound transport activity (someone messaged the bot) and in-run progress (the agent emitted a streamed token, called a tool, or fired an emitter event). As long as either signal advanced within stuck_after (default 900s), the channel stays BUSY and will not be killed. Only when no signal arrives for stuck_after seconds does the channel escalate to STUCK (recoverable, will be restarted).
| Scenario on WhatsApp | Health state | Outcome |
|---|---|---|
| Active inbound traffic + active run | BUSY | Channel kept alive |
Active run, no inbound, streaming tokens or tool calls within stuck_after | BUSY | Channel kept alive |
Active run, no signal of any kind for > stuck_after | STUCK | Channel restarted (genuine hang) |
| No active runs, idle | IDLE | Channel kept alive |
The same mechanism applies to every channel adapter, not just WhatsApp. For the full evaluator behaviour, configuration knobs (
stuck_after, busy_after), and the HealthResult.last_run_progress field, see Channel Supervision.Troubleshooting
Bot responds to old messages on startup
Bot responds to old messages on startup
Fixed in latest version. The stale-message guard drops any message older than when the bot connected. If you still see this, update to the latest version:
Bot responds to all messages, not just self-chat
Bot responds to all messages, not just self-chat
Fixed in latest version. The default filter now checks that both the sender AND chat JID match (true self-chat), not just
IsFromMe. Update your installation.Decryption warnings in the logs
Decryption warnings in the logs
SessionCipher error: old counter
SessionCipher error: old counter
WebSocket close error on shutdown
WebSocket close error on shutdown
shell_tools error: /home/user not found
shell_tools error: /home/user not found
/home/user (a Linux path) on macOS. Fixed in latest version — the tool now automatically falls back to your home directory.Threading error on Ctrl+C
Threading error on Ctrl+C
QR code not showing
QR code not showing
- Use a modern terminal (iTerm2, Windows Terminal)
- Ensure
segnois installed:pip install 'praisonai[bot-whatsapp-web]' - If a saved session exists, no QR is needed. Delete to re-link:
Session expired
Session expired
WhatsApp Web sessions expire if your phone is offline for 14+ days. Delete and re-scan:
Best Practices
Use Cloud API for production, Web mode for prototyping
Use Cloud API for production, Web mode for prototyping
Web mode (
mode="web") needs only a QR-code scan and no tokens — perfect for demos. For anything user-facing, switch to the official Cloud API (mode="cloud"), which is stable, supports webhooks, and won’t break when the linked phone goes offline.Keep tokens in the environment
Keep tokens in the environment
Never hard-code the Cloud API token or phone-number ID in source. Load them from environment variables so the same bot code runs across staging and production without leaking credentials into version control.
Validate inbound media before the agent sees it
Validate inbound media before the agent sees it
WhatsApp accepts images, audio, and documents. Rely on the built-in inbound-media security pipeline (SSRF guard, type checks) and message filtering rather than passing raw attachments straight to the model — malicious payloads are stopped at the edge.
Acknowledge long turns
Acknowledge long turns
Agent replies that take time can look like the bot has stalled. Send a quick status message for long-running turns so users know work is in progress, keeping the WhatsApp thread responsive.
Related
Inbound Media
Full reference for inbound media: security pipeline, SSRF guard, and patterns
Messaging Bots
All supported platforms: Telegram, Discord, Slack, WhatsApp
WhatsApp MCP
Send WhatsApp messages from agents using MCP tools
Bot Commands
Custom command registration and handling
Approval Protocol
Human-in-the-loop tool approval for bots

