Skip to main content

WhatsApp channel — developer reference

This page covers what plugin authors need to know when writing plugins that interact with the whatsapp channel: channel identifier, hook filtering patterns, available message actions, and channel-specific behavior to account for in plugin code. For building a new channel adapter from scratch, see Channel adapters. For the admin configuration reference, see Admin: WhatsApp.

Channel identifier

The WhatsApp channel registers under the id "whatsapp". Use this when filtering hooks by channel:
ctx.channelId is always "whatsapp" for messages, hooks, and tool calls that originate from the WhatsApp channel.

Available hooks (WhatsApp-relevant)

All standard plugin hooks fire for WhatsApp events. The most commonly used for channel-specific behavior:

Filtering to a specific account

WhatsApp supports multiple linked accounts. Filter by account using ctx.accountId:

Message actions

WhatsApp supports the following message actions from agent tools or automation: Gating: channels.whatsapp.actions.reactions and channels.whatsapp.actions.polls must be true for those actions to be available.

Using message actions in a plugin tool

Direct message action dispatch from plugin tools is handled through the agent’s response pipeline. Return structured action payloads from your tool and let the channel adapter handle delivery.

Session keys

WhatsApp session keys follow these patterns: Use ctx.sessionKey in hook handlers — do not construct session keys manually.

Channel-specific behaviors to handle in plugins

Baileys dependency is lazy-loaded. The whatsapp bundled extension loads @whiskeysockets/baileys lazily at channel-activation time, not at gateway startup. Plugins that interact with the WhatsApp channel must not assume the Baileys library is available as a direct import — use only the openclaw/plugin-sdk surface. Self-chat. When the linked number is in allowFrom, self-chat safeguards are active: read receipts are skipped and the agent does not trigger on its own outbound messages. Hooks on message_received will not fire for self-sent messages. Socket lock. Only one Baileys socket can own an account’s credentials directory at a time (enforced by a .socket-owner.lock file). If a plugin attempts gateway-level WhatsApp operations, it must not open a second Baileys connection against the same credentials directory. Media placeholders. Inbound media-only messages arrive normalized: <media:image>, <media:video>, <media:audio>, <media:document>, <media:sticker>. The raw Baileys message payload is not passed to hooks. Status and broadcast chats are ignored. Messages from @status and @broadcast JIDs are dropped before routing. Hooks will not fire for these.

Async safety

Plugin tools and hook handlers run in the same Node.js event loop as the gateway. Do not use synchronous blocking I/O in tool execute() functions or hook handlers.