A fully independent WhatsApp channel plugin for OpenClaw that passively logs all WhatsApp messages to a queryable SQLite database while running the standard agent pipeline alongside it.
- Independent channel — registers as
whatsapp-pro, replaces the built-in WhatsApp plugin with identical agent functionality plus observer capabilities - Full message capture — every message (inbound, outbound, agent replies, reactions, edits, deletes) is logged at the Baileys wire level for all accounts
- Observer-only accounts — dedicated passive listeners that never send, never trigger agent sessions, and never create conversations
wa-proCLI — standalone oclif CLI for querying messages, managing accounts, and configuring observer settings- Agent skill — automatically teaches the OpenClaw agent about the CLI so it can query message history on demand via
exec - Per-account configuration — recording mode, allowlist, blocklist, and retention are configurable per account with global defaults
- Three recording modes — control what gets stored and what the agent/CLI can retrieve
- History sync — automatically captures historical messages on connect via Baileys' full history sync
- On-demand backfill — request older messages for specific conversations via
wa-pro backfill - Contact directory — automatically collects contact names and group metadata from WhatsApp events
- FTS5 search — full-text search index for fast queries on large message databases
- OpenClaw installed and running (
>=2026.3.22) - Node.js 22+
- A WhatsApp account to link (personal or business)
# 1. Clone
git clone https://github.com/FroeMic/wa-pro.git
cd openclaw-whatsapp-observer-plugin
# 2. Install (backs up config, disables built-in whatsapp, installs wa-pro CLI)
bash scripts/install.sh
# 3. Setup an account
bash scripts/setup.sh personal # interactive: choose normal or observer mode
# Scan the QR code with WhatsApp > Linked Devices > Link a Device
# 4. Start
openclaw gateway restart
# 5. Verify
wa-pro stats
wa-pro conversations --format tablebash scripts/install.sh [--migrate | --no-migrate]| Flag | Description |
|---|---|
--migrate |
Migrate existing channels.whatsapp accounts and policies to whatsapp-pro |
--no-migrate |
Start fresh — disable built-in whatsapp without copying config |
| (none) | Prompted interactively if existing whatsapp config is found |
The install script:
- Backs up
openclaw.json(.pre-whatsapp-pro.bak) - Records whether the built-in whatsapp plugin was previously enabled
- Disables the built-in whatsapp plugin and cleans up stale config entries
- Installs the plugin via
openclaw plugins install - Installs root + CLI npm dependencies
- Links the
wa-probinary to/usr/local/bin - Installs
tsxglobally if not present
Existing config (accounts, policies, observer settings) is preserved across reinstalls.
bash scripts/setup.sh <accountId>You'll be prompted to choose:
- Normal — full agent pipeline (receives messages, agent replies, DM policy enforced)
- Observer — passive logging only (no sessions, no replies, no sends)
Both account types log all messages to the observer database. The difference is whether the account participates in the agent pipeline.
You can set up multiple accounts (e.g., a personal phone as observer + a business phone as normal).
bash scripts/uninstall.sh [OPTIONS]| Flag | Description |
|---|---|
--migrate |
Migrate accounts back to channels.whatsapp (observer-only accounts excluded) |
--no-migrate |
Remove config without migrating back |
--restore-whatsapp |
Force re-enable built-in whatsapp plugin |
--no-restore-whatsapp |
Don't re-enable built-in whatsapp |
--purge-credentials |
Delete WhatsApp Web credentials (~/.openclaw/credentials/whatsapp/) |
--keep-files |
Keep plugin files on disk |
Without --restore-whatsapp / --no-restore-whatsapp, the script auto-detects from saved state.
All query commands output JSON by default. Use --format table for human-readable output.
# Full-text search
wa-pro search "<query>" [--sender <name>] [--group <name>] [--after <date>] [--before <date>] [--limit <N>]
# Chronological conversation history (oldest first, max 500)
wa-pro history <conversation-jid> [--account <id>] [--after <date>] [--before <date>] [--limit <N>]
# Recent messages (newest first)
wa-pro recent [--conversation <jid>] [--sender <name>] [--account <id>] [--limit <N>]
# List conversations with message counts and contact names
wa-pro conversations [--account <id>] [--limit <N>]
# Aggregate statistics with optional grouping
wa-pro stats [--account <id>] [--after <date>] [--group-by sender|group|day|hour]# Request older messages for a conversation (phone must be online)
wa-pro backfill <conversation-jid> --account <id> [--count <N>] [--requests <N>] [--wait <seconds>]
# Backfill all conversations for an account
wa-pro conversations --account michael --format json | \
jq -r '.[].conversationId' | \
while read jid; do wa-pro backfill "$jid" --account michael --requests 3 --wait 10; done# List all accounts with role, linked status, policies, and message counts
wa-pro accounts [--format table]
# List known contacts and groups
wa-pro contacts [--account <id>] [--groups] [--people] [--format table]Settings are stored in the observer database and take effect immediately — no gateway restart needed.
All config commands support --account <id> for per-account overrides. Without --account, they set/show global defaults. Use --reset --account <id> to remove an override and fall back to global.
# View settings (global or per-account)
wa-pro config show [--account <id>]
# Recording/retrieval mode
wa-pro config mode [<mode>] [--account <id>] [--reset]
# Allowlist management
wa-pro config allowlist list|add|remove|clear [<entry>] [--account <id>] [--reset]
# Blocklist management
wa-pro config blocklist list|add|remove|clear [<entry>] [--account <id>] [--reset]
# Retention period (days, 0 = forever)
wa-pro config retention [<days>] [--account <id>] [--reset]| Mode | What's Recorded | What's Queryable |
|---|---|---|
record-all-retrieve-all (default) |
All messages | All messages |
record-all-retrieve-filtered |
All messages | Only messages matching allowlist/blocklist |
record-filtered-retrieve-filtered |
Only allowed messages | Only allowed messages |
Modes are configurable per account with global defaults:
wa-pro config mode record-all-retrieve-all # global default
wa-pro config mode record-all-retrieve-filtered --account michael # per-account override
wa-pro config mode --reset --account michael # remove overrideThese modes are independent from the WhatsApp channel's dmPolicy / allowFrom settings, which control agent session access.
Account configuration uses the same shape as the native WhatsApp plugin:
{
"channels": {
"whatsapp-pro": {
"accounts": {
"personal": {
"enabled": true,
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"allowFrom": ["+4917600000001"]
},
"michael": {
"enabled": true
}
},
"observer": {
"accounts": ["michael"]
}
}
}
}Observer settings (mode, filters, retention) live in the SQLite database at ~/.openclaw/whatsapp-observer/messages.db. Manage via wa-pro config commands. Settings are scoped:
- Global defaults — apply to all accounts unless overridden
- Per-account overrides — take precedence over global for that account
On first startup, global defaults are seeded from openclaw.json if present. After that, the database is the source of truth.
openclaw-whatsapp-observer-plugin/
index.ts # Plugin entry point
openclaw.plugin.json # Plugin manifest (channels, skills)
src/
channel.ts # Channel plugin (adapters, gateway, observer tap)
channel-config.ts # Zod schema + typed config accessor
observer-config.ts # Observer config parsing
observer/
monitor.ts # Baileys listener + message processing (all accounts)
db.ts # SQLite database (messages + contacts + scoped settings + FTS5)
filter.ts # Allowlist/blocklist filtering
types.ts # Type definitions
inbound/
monitor.ts # Normal account Baileys listener (with observer tap)
auto-reply/ # Agent pipeline (unchanged from upstream)
cli/
bin/run.ts # CLI entry point (#!/usr/bin/env tsx)
src/
commands/ # oclif commands
search.ts, recent.ts, history.ts, conversations.ts, stats.ts
accounts.ts, contacts.ts, backfill.ts
config/ # show, mode, allowlist, blocklist, retention
lib/ # Shared utilities
skills/
wa-pro/SKILL.md # Agent skill definition
scripts/
install.sh # Install with config migration
uninstall.sh # Uninstall with config restoration
setup.sh # Account setup (normal/observer)
test/ # 74 Vitest tests
All accounts (observer and normal) log messages at the Baileys wire level:
WhatsApp --> Baileys messages.upsert --> processObserverMessage() --> SQLite DB
Normal accounts also run the agent pipeline in parallel:
WhatsApp --> Baileys --> Observer DB tap --> pipeline --> Agent --> reply
Observer accounts skip the pipeline entirely:
WhatsApp --> Baileys --> Observer DB (+ optional media) --> done
Observer accounts are prevented from sending messages at three layers:
- No send wiring — the observer Baileys socket never calls
sendMessage - No agent pipeline — observer messages bypass session creation entirely
message_sendinghook — a lifecycle hook blocks any outbound attempt on observer accounts (priority 9999)
message_type |
Description | ref_message_id |
|---|---|---|
message |
Regular text or media message | — |
reaction |
Emoji reaction | Original message ID |
poll |
Poll creation | — |
edit |
Edited message | Original message ID |
delete |
Deletion/revoke | Deleted message ID |
# Run all tests (74 tests across 4 suites)
npx vitest run
# Test CLI locally
cd cli && npx tsx bin/run.ts --help
cd cli && npx tsx bin/run.ts search "hello" --format table
# Quick deploy to server (without full reinstall)
git pull
cp -r . ~/.openclaw/extensions/whatsapp-pro/
openclaw gateway restartTo clear all messages, contacts, and settings and start fresh:
rm ~/.openclaw/whatsapp-observer/messages.db
openclaw gateway restartThe DB is recreated automatically on gateway startup.
MIT — see LICENSE.md.