Help us improve
Share bugs, ideas, or general feedback.
From claudeclaw
Adds WhatsApp as a communication channel with QR code or pairing code authentication. Handles code merge, dependency installation, and environment detection.
npx claudepluginhub sbusso/claudeclawHow this skill is triggered — by the user, by Claude, or both
Slash command
/claudeclaw:add-whatsappThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill adds WhatsApp support to ClaudeClaw. It installs the WhatsApp channel code, dependencies, and guides through authentication, registration, and configuration.
Adds WhatsApp messaging channel to NanoClaw via git merge, npm deps (@whiskeysockets/baileys, qrcode), and env-aware auth (QR code browser/terminal or pairing code).
Guides WhatsApp channel setup for Claude Code: device linking, phone number config, and access control.
Guides interactive WhatsApp channel onboarding: device linking via QR code or pairing code, optional phone number config, and access control setup including pairing or allowlist.
Share bugs, ideas, or general feedback.
This skill adds WhatsApp support to ClaudeClaw. It installs the WhatsApp channel code, dependencies, and guides through authentication, registration, and configuration.
Check if WhatsApp is already configured. If store/auth/ exists with credential files, skip to Phase 4 (Registration) or Phase 5 (Verify).
ls store/auth/creds.json 2>/dev/null && echo "WhatsApp auth exists" || echo "No WhatsApp auth"
Check whether the environment is headless (no display server):
[[ -z "$DISPLAY" && -z "$WAYLAND_DISPLAY" && "$OSTYPE" != darwin* ]] && echo "IS_HEADLESS=true" || echo "IS_HEADLESS=false"
Use AskUserQuestion to collect configuration. Adapt auth options based on environment:
If IS_HEADLESS=true AND not WSL → AskUserQuestion: How do you want to authenticate WhatsApp?
Otherwise (macOS, desktop Linux, or WSL) → AskUserQuestion: How do you want to authenticate WhatsApp?
If they chose pairing code:
AskUserQuestion: What is your phone number? (Include country code without +, e.g., 1234567890)
Check if src/channels/whatsapp.ts already exists. If it does, skip to Phase 3 (Authentication).
git remote -v
If whatsapp is missing, add it:
git remote add whatsapp https://github.com/qwibitai/claudeclaw-whatsapp.git
git fetch whatsapp main
git merge whatsapp/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git merge --continue
}
This merges in:
src/channels/whatsapp.ts (WhatsAppChannel class with self-registration via registerChannel)src/channels/whatsapp.test.ts (41 unit tests)src/channels/whatsapp-auth.ts (standalone WhatsApp authentication script)setup/whatsapp-auth.ts (WhatsApp auth setup step)import './whatsapp.js' appended to the channel barrel file src/channels/index.ts'whatsapp-auth' step added to setup/index.ts@whiskeysockets/baileys, qrcode, qrcode-terminal npm dependencies in package.jsonASSISTANT_HAS_OWN_NUMBER in .env.exampleIf the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
npm install
npm run build
npx vitest run src/channels/whatsapp.test.ts
All tests must pass and build must be clean before proceeding.
rm -rf store/auth/
For QR code in browser (recommended):
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
(Bash timeout: 150000ms)
Tell the user:
A browser window will open with a QR code.
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Scan the QR code in the browser
- The page will show "Authenticated!" when done
For QR code in terminal:
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
Tell the user to run npm run auth in another terminal, then:
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Scan the QR code displayed in the terminal
For pairing code:
Tell the user to have WhatsApp open on Settings > Linked Devices > Link a Device, ready to tap "Link with phone number instead" — the code expires in ~60 seconds and must be entered immediately.
Run the auth process in the background and poll store/pairing-code.txt for the code:
rm -f store/pairing-code.txt && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
Then immediately poll for the code (do NOT wait for the background command to finish):
for i in $(seq 1 20); do [ -f store/pairing-code.txt ] && cat store/pairing-code.txt && break; sleep 1; done
Display the code to the user the moment it appears. Tell them:
Enter this code now — it expires in ~60 seconds.
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Tap Link with phone number instead
- Enter the code immediately
After the user enters the code, poll for authentication to complete:
for i in $(seq 1 60); do grep -q 'AUTH_STATUS: authenticated' /tmp/wa-auth.log 2>/dev/null && echo "authenticated" && break; grep -q 'AUTH_STATUS: failed' /tmp/wa-auth.log 2>/dev/null && echo "failed" && break; sleep 2; done
If failed: qr_timeout → re-run. logged_out → delete store/auth/ and re-run. 515 → re-run. timeout → ask user, offer retry.
test -f store/auth/creds.json && echo "Authentication successful" || echo "Authentication failed"
Channels auto-enable when their credentials are present — WhatsApp activates when store/auth/creds.json exists.
Sync to container environment:
mkdir -p data/env && cp .env data/env/env
Get the bot's WhatsApp number: node -e "const c=require('./store/auth/creds.json');console.log(c.me.id.split(':')[0].split('@')[0])"
AskUserQuestion: Is this a shared phone number (personal WhatsApp) or a dedicated number (separate device)?
AskUserQuestion: What trigger word should activate the assistant?
AskUserQuestion: What should the assistant call itself?
AskUserQuestion: Where do you want to chat with the assistant?
Shared number options:
Dedicated number options:
Self-chat: JID = your phone number with @s.whatsapp.net. Extract from auth credentials:
node -e "const c=JSON.parse(require('fs').readFileSync('store/auth/creds.json','utf-8'));console.log(c.me?.id?.split(':')[0]+'@s.whatsapp.net')"
DM with bot: Ask for the bot's phone number. JID = NUMBER@s.whatsapp.net
Group (solo, existing): Run group sync and list available groups:
npx tsx setup/index.ts --step groups
npx tsx setup/index.ts --step groups --list
The output shows JID|GroupName pairs. Present candidates as AskUserQuestion (names only, not JIDs).
npx tsx setup/index.ts --step register \
--jid "<jid>" \
--name "<chat-name>" \
--trigger "@<trigger>" \
--folder "whatsapp_main" \
--channel whatsapp \
--assistant-name "<name>" \
--is-main \
--no-trigger-required # Only for main/self-chat
For additional groups (trigger-required):
npx tsx setup/index.ts --step register \
--jid "<group-jid>" \
--name "<group-name>" \
--trigger "@<trigger>" \
--folder "whatsapp_<group-name>" \
--channel whatsapp
npm run build
Service name: Derived from the directory name:
com.claudeclaw.<dirname>(macOS) /claudeclaw-<dirname>(Linux). For example, if cwd ismy-assistant, the service iscom.claudeclaw.my-assistant. Determine the correct service name before running service commands below.
Restart the service:
# macOS (launchd)
launchctl kickstart -k gui/$(id -u)/com.claudeclaw
# Linux (systemd)
systemctl --user restart claudeclaw
# Linux (nohup fallback)
bash start-claudeclaw.sh
Tell the user:
Send a message to your registered WhatsApp chat:
- For self-chat / main: Any message works
- For groups: Use the trigger word (e.g., "@Andy hello")
The assistant should respond within a few seconds.
tail -f logs/claudeclaw.log
QR codes expire after ~60 seconds. Re-run the auth command:
rm -rf store/auth/ && npx tsx src/channels/whatsapp-auth.ts
Codes expire in ~60 seconds. To retry:
rm -rf store/auth/ && npx tsx src/channels/whatsapp-auth.ts --pairing-code --phone <phone>
Enter the code immediately when it appears. Also ensure:
+ (e.g., 1234567890)If pairing code keeps failing, switch to QR-browser auth instead:
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
This happens when two instances connect with the same credentials. Ensure only one ClaudeClaw process is running:
pkill -f "node dist/service.js"
# Then restart
Check:
ls store/auth/creds.jsonsqlite3 store/messages.db "SELECT * FROM registered_groups WHERE jid LIKE '%whatsapp%' OR jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"launchctl list | grep claudeclaw (macOS) or systemctl --user status claudeclaw (Linux)tail -50 logs/claudeclaw.logRun group metadata sync:
npx tsx setup/index.ts --step groups
This fetches all group names from WhatsApp. Runs automatically every 24 hours.
If running npm run dev while the service is active:
# macOS:
launchctl unload ~/Library/LaunchAgents/com.claudeclaw.plist
npm run dev
# When done testing:
launchctl load ~/Library/LaunchAgents/com.claudeclaw.plist
# Linux:
# systemctl --user stop claudeclaw
# npm run dev
# systemctl --user start claudeclaw
To remove WhatsApp integration:
rm -rf store/auth/sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"mkdir -p data/env && cp .env data/env/envnpm run build && launchctl kickstart -k gui/$(id -u)/com.claudeclaw (macOS) or npm run build && systemctl --user restart claudeclaw (Linux)