A lightweight, self-hosted WhatsApp gateway for AI assistants. Built for OpenClaw and similar personal AI agent projects.
Production-tested: This code is extracted from and powers the WhatsApp integration at askjo.ai.
openclaw plugins install @askjo/wa_meowOr directly from GitHub:
openclaw plugins install https://github.com/jo-inc/wa_meowThen configure:
channels:
wa_meow:
serverUrl: http://localhost:8090
accounts:
main:
userId: 1
enabled: trueThis repo ships two different artifacts:
- Fly / Docker deploy — runs the Go HTTP bridge from
cmd/server/. - npm / GitHub plugin install — ships the OpenClaw plugin from
src/compiled intodist/, plus the bundledbin/binaries.
That means:
- Changes under
cmd/server/need a Fly deploy. - Changes under
src/,package.json, oropenclaw.plugin.jsonneed a new npm package release. - If a change touches both surfaces, release both.
Connect your AI assistant to WhatsApp in minutes. Send messages, receive events via SSE, and manage multiple sessions with a simple REST API.
If you're running OpenClaw or building your own AI assistant, you need a way to connect to WhatsApp. This bridge:
- Runs on your hardware - Your messages stay with you
- Simple REST API - No complex protocols to learn
- Real-time events - SSE streaming for instant message delivery
- Multi-user support - One instance handles multiple WhatsApp accounts
- Session persistence - Encrypted backup/restore across restarts
- More stable than Baileys - Built on whatsmeow (Go), which has better memory management and fewer session logout issues than the popular Baileys library
# Build the image locally
git clone https://github.com/jo-inc/wa_meow.git
cd wa_meow
docker build -t wa_meow .
# Run it
docker run -d \
--name wa_meow \
-p 8090:8090 \
-v wa_meow-data:/data/whatsapp \
wa_meow# Clone the repo
git clone https://github.com/jo-inc/wa_meow.git
cd wa_meow
# Run (requires Go 1.21+)
./run-server.sh# 1. Create a session
curl -X POST localhost:8090/sessions -d '{"user_id": 1}'
# 2. Get QR code (opens SSE stream)
curl localhost:8090/sessions/qr?user_id=1
# 3. Scan QR with your phone (WhatsApp > Linked Devices > Link a Device)
# 4. You're connected! Send a message:
curl -X POST localhost:8090/messages/send \
-H "Content-Type: application/json" \
-d '{"user_id": 1, "chat_jid": "1234567890@s.whatsapp.net", "text": "Hello from my AI!"}'| Endpoint | Method | Description |
|---|---|---|
/sessions |
POST | Create session ({"user_id": 123}) |
/sessions/qr?user_id=X |
GET | SSE stream of QR codes for login |
/sessions/status?user_id=X |
GET | Connection status |
/sessions/save?user_id=X |
POST | Persist session (requires encryption key) |
/sessions/delete?user_id=X |
DELETE | Disconnect and cleanup |
| Endpoint | Method | Description |
|---|---|---|
/messages/send |
POST | Send text message |
/messages/audio |
POST | Send audio/voice message |
/messages/document |
POST | Send document (PDF, etc.) |
/messages/react |
POST | React to a message with emoji |
/messages/typing |
POST | Send typing indicator |
/chats?user_id=X |
GET | List all chats (contacts + groups) |
/events?user_id=X |
GET | SSE stream of incoming messages |
/media/download |
POST | Download media from a message |
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
curl -X POST http://localhost:8090/messages/send \
-H "Content-Type: application/json" \
-d '{
"user_id": 1,
"chat_jid": "1234567890@s.whatsapp.net",
"text": "Hello!"
}'curl -X POST http://localhost:8090/messages/react \
-H "Content-Type: application/json" \
-d '{
"user_id": 1,
"chat_jid": "1234567890@s.whatsapp.net",
"message_id": "ABC123",
"emoji": "thumbsup"
}'curl -N http://localhost:8090/events?user_id=1Events are delivered as SSE:
event: message
data: {"type":"message","payload":{"id":"ABC123","chat_jid":"1234567890@s.whatsapp.net","sender_jid":"9876543210@s.whatsapp.net","sender_name":"John","text":"Hey there!","timestamp":1706745600,"is_from_me":false}}
| Environment Variable | Default | Description |
|---|---|---|
DATA_DIR |
/data/whatsapp |
SQLite database storage |
PORT |
8090 |
HTTP server port |
WHATSAPP_SESSION_KEY |
- | Base64 AES-256 key for encrypted session backup |
JO_BOT_URL |
- | Callback URL for session persistence |
JO_WHATSAPP_INTERNAL_TOKEN |
- | Auth token for session API calls |
SENTRY_DSN |
- | Sentry DSN for error tracking (optional) |
To persist sessions across container restarts or sync between instances:
# Generate a key
openssl rand -base64 32
# Set it as an environment variable
export WHATSAPP_SESSION_KEY="your-generated-key"fly apps create my-wa_meow
fly secrets set WHATSAPP_SESSION_KEY="$(openssl rand -base64 32)"
fly deploynpm version patch
npm publishThe package prepare step builds src/ into dist/, so both npm publishes and direct GitHub installs include the compiled plugin files.
version: '3.8'
services:
wa_meow:
build: .
ports:
- "8090:8090"
volumes:
- wa_meow-data:/data/whatsapp
environment:
- WHATSAPP_SESSION_KEY=your-secret-key
restart: unless-stopped
volumes:
wa_meow-data:Your AI Assistant
|
| REST API (JSON)
v
+------------------+
| WhatsApp Bridge | <-- This project
+------------------+
|
| WhatsApp Web Protocol (via whatsmeow)
v
+------------------+
| WhatsApp Servers |
+------------------+
- One SQLite database per user - Sessions are isolated
- whatsmeow - Battle-tested WhatsApp Web client library
- Server-Sent Events - Real-time message streaming
- AES-256-GCM - Optional session encryption for backup
# Install air for live-reload
go install github.com/air-verse/air@latest
# Run with auto-reload
./run-server.shQR code not appearing?
- Make sure you're using
curl -Nto disable buffering - The QR stream times out after 2 minutes
Session keeps disconnecting?
- WhatsApp may disconnect linked devices after 14 days of phone inactivity
- Keep your phone connected to the internet
Getting rate limited?
- WhatsApp has sending limits. Space out your messages.
- Don't spam or you'll get banned.
- This connects to your personal WhatsApp account
- Your session data is stored locally (or encrypted if
WHATSAPP_SESSION_KEYis set) - Never expose this bridge to the public internet without authentication
- Consider running behind a reverse proxy with auth
- No group chat support yet - You can list groups and send messages to group JIDs, but group-specific features (mentions, replies, admin actions) are not implemented
- No message history - Only receives messages while connected
This project is built on whatsmeow by @tulir - a robust, well-maintained Go library that handles all the WhatsApp Web protocol complexity. Without whatsmeow, this bridge wouldn't exist. If you find this project useful, consider starring whatsmeow on GitHub.
Inspired by the OpenClaw community and the growing ecosystem of self-hosted AI assistants.
MIT License - See LICENSE for details.