coderClaw

WhatsApp (Web channel)

Status: production-ready via WhatsApp Web (Baileys). Gateway owns linked session(s).

<CardGroup cols={3}>

Default DM policy is pairing for unknown senders. Cross-channel diagnostics and repair playbooks. Full channel config patterns and examples.

</CardGroup>

Quick setup

```json5 { channels: { whatsapp: { dmPolicy: "pairing", allowFrom: ["+15551234567"], groupPolicy: "allowlist", groupAllowFrom: ["+15551234567"], }, }, } ``` ```bash coderclaw channels login --channel whatsapp ``` For a specific account: ```bash coderclaw channels login --channel whatsapp --account work ``` ```bash coderclaw gateway ``` ```bash coderclaw pairing list whatsapp coderclaw pairing approve whatsapp ``` Pairing requests expire after 1 hour. Pending requests are capped at 3 per channel. </Step> </Steps> CoderClaw recommends running WhatsApp on a separate number when possible. (The channel metadata and onboarding flow are optimized for that setup, but personal-number setups are also supported.) ## Deployment patterns This is the cleanest operational mode: - separate WhatsApp identity for CoderClaw - clearer DM allowlists and routing boundaries - lower chance of self-chat confusion Minimal policy pattern: ```json5 { channels: { whatsapp: { dmPolicy: "allowlist", allowFrom: ["+15551234567"], }, }, } ``` Onboarding supports personal-number mode and writes a self-chat-friendly baseline: - `dmPolicy: "allowlist"` - `allowFrom` includes your personal number - `selfChatMode: true` In runtime, self-chat protections key off the linked self number and `allowFrom`. The messaging platform channel is WhatsApp Web-based (`Baileys`) in current CoderClaw channel architecture. There is no separate Twilio WhatsApp messaging channel in the built-in chat-channel registry. ## Runtime model - Gateway owns the WhatsApp socket and reconnect loop. - Outbound sends require an active WhatsApp listener for the target account. - Status and broadcast chats are ignored (`@status`, `@broadcast`). - Direct chats use DM session rules (`session.dmScope`; default `main` collapses DMs to the agent main session). - Group sessions are isolated (`agent::whatsapp:group:`). ## Access control and activation `channels.whatsapp.dmPolicy` controls direct chat access: - `pairing` (default) - `allowlist` - `open` (requires `allowFrom` to include `"*"`) - `disabled` `allowFrom` accepts E.164-style numbers (normalized internally). Multi-account override: `channels.whatsapp.accounts..dmPolicy` (and `allowFrom`) take precedence over channel-level defaults for that account. Runtime behavior details: - pairings are persisted in channel allow-store and merged with configured `allowFrom` - if no allowlist is configured, the linked self number is allowed by default - outbound `fromMe` DMs are never auto-paired </Tab> Group access has two layers: 1. **Group membership allowlist** (`channels.whatsapp.groups`) - if `groups` is omitted, all groups are eligible - if `groups` is present, it acts as a group allowlist (`"*"` allowed) 2. **Group sender policy** (`channels.whatsapp.groupPolicy` + `groupAllowFrom`) - `open`: sender allowlist bypassed - `allowlist`: sender must match `groupAllowFrom` (or `*`) - `disabled`: block all group inbound Sender allowlist fallback: - if `groupAllowFrom` is unset, runtime falls back to `allowFrom` when available Note: if no `channels.whatsapp` block exists at all, runtime group-policy fallback is effectively `open`. Group replies require mention by default. Mention detection includes: - explicit WhatsApp mentions of the bot identity - configured mention regex patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`) - implicit reply-to-bot detection (reply sender matches bot identity) Session-level activation command: - `/activation mention` - `/activation always` `activation` updates session state (not global config). It is owner-gated. </Tabs> ## Personal-number and self-chat behavior When the linked self number is also present in `allowFrom`, WhatsApp self-chat safeguards activate: - skip read receipts for self-chat turns - ignore mention-JID auto-trigger behavior that would otherwise ping yourself - if `messages.responsePrefix` is unset, self-chat replies default to `[{identity.name}]` or `[coderclaw]` ## Message normalization and context Incoming WhatsApp messages are wrapped in the shared inbound envelope. If a quoted reply exists, context is appended in this form: ```text [Replying to id:] [/Replying] ``` Reply metadata fields are also populated when available (`ReplyToId`, `ReplyToBody`, `ReplyToSender`, sender JID/E.164). </Accordion> Media-only inbound messages are normalized with placeholders such as: - `` - `` - `` - `` - `` Location and contact payloads are normalized into textual context before routing. </Accordion> For groups, unprocessed messages can be buffered and injected as context when the bot is finally triggered. - default limit: `50` - config: `channels.whatsapp.historyLimit` - fallback: `messages.groupChat.historyLimit` - `0` disables Injection markers: - `[Chat messages since your last reply - for context]` - `[Current message - respond to this]` Read receipts are enabled by default for accepted inbound WhatsApp messages. Disable globally: ```json5 { channels: { whatsapp: { sendReadReceipts: false, }, }, } ``` Per-account override: ```json5 { channels: { whatsapp: { accounts: { work: { sendReadReceipts: false, }, }, }, }, } ``` Self-chat turns skip read receipts even when globally enabled. </AccordionGroup> ## Delivery, chunking, and media - default chunk limit: `channels.whatsapp.textChunkLimit = 4000` - `channels.whatsapp.chunkMode = "length" | "newline"` - `newline` mode prefers paragraph boundaries (blank lines), then falls back to length-safe chunking - supports image, video, audio (PTT voice-note), and document payloads - `audio/ogg` is rewritten to `audio/ogg; codecs=opus` for voice-note compatibility - animated GIF playback is supported via `gifPlayback: true` on video sends - captions are applied to the first media item when sending multi-media reply payloads - media source can be HTTP(S), `file://`, or local paths - inbound media save cap: `channels.whatsapp.mediaMaxMb` (default `50`) - outbound media cap for auto-replies: `agents.defaults.mediaMaxMb` (default `5MB`) - images are auto-optimized (resize/quality sweep) to fit limits - on media send failure, first-item fallback sends text warning instead of dropping the response silently ## Acknowledgment reactions WhatsApp supports immediate ack reactions on inbound receipt via `channels.whatsapp.ackReaction`. ```json5 { channels: { whatsapp: { ackReaction: { emoji: "👀", direct: true, group: "mentions", // always | mentions | never }, }, }, } ``` Behavior notes: - sent immediately after inbound is accepted (pre-reply) - failures are logged but do not block normal reply delivery - group mode `mentions` reacts on mention-triggered turns; group activation `always` acts as bypass for this check - WhatsApp uses `channels.whatsapp.ackReaction` (legacy `messages.ackReaction` is not used here) ## Multi-account and credentials - account ids come from `channels.whatsapp.accounts` - default account selection: `default` if present, otherwise first configured account id (sorted) - account ids are normalized internally for lookup - current auth path: `~/.coderclaw/credentials/whatsapp//creds.json` - backup file: `creds.json.bak` - legacy default auth in `~/.coderclaw/credentials/` is still recognized/migrated for default-account flows </Accordion> `coderclaw channels logout --channel whatsapp [--account ]` clears WhatsApp auth state for that account. In legacy auth directories, `oauth.json` is preserved while Baileys auth files are removed. </Accordion> </AccordionGroup> ## Tools, actions, and config writes - Agent tool support includes WhatsApp reaction action (`react`). - Action gates: - `channels.whatsapp.actions.reactions` - `channels.whatsapp.actions.polls` - Channel-initiated config writes are enabled by default (disable via `channels.whatsapp.configWrites=false`). ## Troubleshooting Symptom: channel status reports not linked. Fix: ```bash coderclaw channels login --channel whatsapp coderclaw channels status ``` Symptom: linked account with repeated disconnects or reconnect attempts. Fix: ```bash coderclaw doctor coderclaw logs --follow ``` If needed, re-link with `channels login`. Outbound sends fail fast when no active gateway listener exists for the target account. Make sure gateway is running and the account is linked. Check in this order: - `groupPolicy` - `groupAllowFrom` / `allowFrom` - `groups` allowlist entries - mention gating (`requireMention` + mention patterns) WhatsApp gateway runtime should use Node. Bun is flagged as incompatible for stable WhatsApp/Telegram gateway operation. ## Configuration reference pointers Primary reference: - [Configuration reference - WhatsApp](/gateway/configuration-reference#whatsapp) High-signal WhatsApp fields: - access: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups` - delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `sendReadReceipts`, `ackReaction` - multi-account: `accounts..enabled`, `accounts..authDir`, account-level overrides - operations: `configWrites`, `debounceMs`, `web.enabled`, `web.heartbeatSeconds`, `web.reconnect.*` - session behavior: `session.dmScope`, `historyLimit`, `dmHistoryLimit`, `dms..historyLimit` ## Related - [Pairing](/channels/pairing) - [Channel routing](/channels/channel-routing) - [Multi-agent routing](/concepts/multi-agent) - [Troubleshooting](/channels/troubleshooting)