Skip to content

Setting Up the WhatsApp Web Channel

I’ve found that managing chat integrations can be a real pain when the connection isn’t stable. You want something that stays linked and handles the reconnecting for you without a massive headache. If you are looking to get your logic onto WhatsApp, the built-in Web channel is the most direct path I recommend.

It uses a linked session via the Baileys library, meaning the gateway owns the connection. I suggest using a dedicated number for this to keep your personal chats and your bot logic separate. It makes the onboarding flow much cleaner and prevents you from accidentally talking to yourself.

  • A WhatsApp account (dedicated number recommended)
  • The OpenClaw CLI
  • A phone to scan a QR code
  • Access to your configuration file

Setting this up takes about five minutes. Here is the path to get your gateway running.

First, you need to tell the gateway how to handle incoming messages. Open your config and add the WhatsApp channel settings.

{
channels: {
whatsapp: {
dmPolicy: "pairing",
allowFrom: ["+15551234567"],
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
},
}

Run the login command to generate a QR code. Scan this with your WhatsApp mobile app just like you would for WhatsApp Web.

Terminal window
openclaw channels login --channel whatsapp

If you are managing multiple accounts, you can specify which one you are linking:

Terminal window
openclaw channels login --channel whatsapp --account work

Once linked, start the gateway process. This process owns the socket and handles the reconnect loop.

Terminal window
openclaw gateway

If you chose the pairing policy in step 1, you need to manually approve the first request from a new sender.

Terminal window
openclaw pairing list whatsapp
openclaw pairing approve whatsapp <CODE>

If things aren’t working quite right, check these two common limits:

  • Expired Requests: Pairing requests expire after 1 hour. If you miss the window, the sender will need to try again.
  • Request Cap: The gateway caps pending requests at 3 per channel. You’ll need to approve or clear existing ones before new ones show up.

If you are using a personal number instead of a dedicated one, the system supports a selfChatMode: true setting. This helps the gateway identify your own number to prevent loops or confusion during runtime.

Need a hand with your specific configuration? Talk to the AI Setup Assistant.

Managing bot interactions can get messy fast. I have often found myself in situations where a bot starts replying to everyone in a group or ignores my direct messages because of a misconfigured allowlist. It is frustrating when you just want the bot to talk to specific people or act a certain way when you message yourself.

I want to show you how to set up access control so your bot only responds when and where you want it to.

  • A linked WhatsApp account.
  • Access to your configuration file (specifically the channels.whatsapp block).
  • The E.164-style phone numbers you want to allow.
  1. Set your DM Policy: In your config, define channels.whatsapp.dmPolicy. Use pairing if you want it to remember users you’ve interacted with.
  2. Define Allowlists: Add authorized numbers to allowFrom using the E.164 format.
  3. Configure Groups: Use channels.whatsapp.groups to limit which groups the bot joins.
  4. Use Activation Commands: In any chat, type /activation always or /activation mention to change how the bot triggers in that specific session.

The channels.whatsapp.dmPolicy setting is your primary tool for controlling direct chat access. I recommend looking at these four options:

  • pairing (default): Remembers successful interactions.
  • allowlist: Only numbers in your allowFrom list get through.
  • open: Everyone can message the bot (this requires allowFrom to include "*").
  • disabled: Blocks all direct messages.

If you are using allowFrom, make sure your numbers are in E.164 format. The system normalizes these internally. A few things I’ve noticed about how this works at runtime:

  • Pairings are saved in the channel allow-store and merged with your allowFrom config.
  • If you don’t configure an allowlist, your own linked number is allowed by default.
  • Outbound fromMe DMs do not trigger auto-pairing.

Group access works in two distinct layers. First, there is the Group membership allowlist found at channels.whatsapp.groups. If you leave this out, the bot can join any group. If you include it, it acts as a filter (though you can use "*" to allow all).

Second, you have the Group sender policy via channels.whatsapp.groupPolicy and groupAllowFrom:

  • open: Anyone in the group can trigger the bot.
  • allowlist: Only senders matching groupAllowFrom (or *) can trigger it.
  • disabled: The bot ignores all inbound messages in groups.

If you don’t set groupAllowFrom, the system falls back to your standard allowFrom list. If you don’t have a channels.whatsapp block at all, the group policy defaults to open.

By default, the bot expects a mention in group chats. It looks for a few specific things:

  • Explicit WhatsApp mentions of the bot’s identity.
  • Regex patterns defined in agents.list[].groupChat.mentionPatterns (or the fallback messages.groupChat.mentionPatterns).
  • Implicit detection when you reply directly to one of the bot’s messages.

You can change this behavior on the fly using session-level activation commands. These are owner-gated, so only you can run them:

  • /activation mention
  • /activation always

Note that these commands only update the state for that specific session; they do not change your global configuration file.

If you add your own linked number to the allowFrom list, the system handles self-chats differently to avoid loops. It will skip read receipts for your own messages and ignore JID auto-triggers that would cause the bot to ping itself. Also, if you haven’t set a messages.responsePrefix, self-chat replies will default to using [{identity.name}] or [openclaw].

The bot isn’t responding to my personal number in a group. Check if you have restricted the groupAllowFrom list. If that is unset, it falls back to allowFrom. Ensure your number is correctly formatted in E.164.

The bot is replying to everyone in a new group. If your channels.whatsapp block is missing, the policy defaults to open. Define a groupPolicy of allowlist and specify authorized senders to fix this.

My activation command didn’t save after a restart. The /activation command only updates the session state. To make a change permanent across all sessions, you must update your global configuration file.

Self-chat replies have a strange prefix. This happens by design when messages.responsePrefix is unset. You can customize this by defining your own prefix in the messages config block.

For more help setting up your environment, check out the AI Setup Assistant.

Parsing raw message data is often a nightmare. When you are building a bot, you want clean text, not a pile of complex metadata that varies every time someone sends a photo or replies to a thread. I found that having a consistent format makes life much easier because you don’t have to write custom logic for every possible message type.

Here is how we handle message normalization and context to keep your bot’s logic simple and predictable.

  • An active WhatsApp channel integration.
  • Access to your configuration settings (JSON5).

Inbound messages are wrapped in a shared envelope to keep things uniform. Here is how to handle the most common scenarios.

When a user replies to a specific message, we append the context directly to the body. You will see it formatted like this:

[Replying to <sender> id:<stanzaId>]
<quoted body or media placeholder>
[/Replying]

Beyond the text markers, the system populates metadata fields like ReplyToId, ReplyToBody, ReplyToSender, and the sender JID/E.164. This gives you two ways to track the conversation thread.

If someone sends a file or a location, we don’t leave the message body empty. We use specific placeholders:

  • <media:image>
  • <media:video>
  • <media:audio>
  • <media:document>

Stickers are marked as <media:sticker>. If a user sends a location or a contact, those payloads are converted into textual context before they even reach your routing logic.

For group chats, you can provide the bot with previous messages so it understands the conversation flow. We buffer unprocessed messages and inject them using these markers:

  • [Chat messages since your last reply - for context]
  • [Current message - respond to this]

You can control how many messages are sent by adjusting the history limit. The default is 50. Use channels.whatsapp.historyLimit in your config, or the fallback messages.groupChat.historyLimit. Setting this to 0 will disable the feature.

By default, the system sends read receipts for accepted messages. If you want to change this, you have two options.

To disable them for everyone:

{
channels: {
whatsapp: {
sendReadReceipts: false,
},
},
}

If you only want to disable them for a specific account (like a “work” account), use an override:

{
channels: {
whatsapp: {
accounts: {
work: {
sendReadReceipts: false,
},
},
}
}
}

Note that self-chat turns always skip read receipts, even if you have them turned on globally.

The bot is receiving too much context in group chats If the message history is overwhelming your logic, check your channels.whatsapp.historyLimit. You can lower this from the default 50 to a smaller number or 0 to stop history injection entirely.

Read receipts are still appearing in self-chats Actually, the system is designed to skip read receipts for self-chats automatically. If you see them elsewhere and want them gone, ensure your sendReadReceipts flag is set to false in the correct account block.

If you have questions about specific parameters or need help with your configuration, check out the AI Setup Assistant.

I have spent a lot of time watching bots fail because a message was too long or a media file was just a bit too heavy. It is frustrating when a response simply disappears because of a technical limit you didn’t see coming. Dealing with message delivery shouldn’t feel like guesswork, so I want to walk you through how to handle text chunking and media properly.

  • WhatsApp account IDs added to channels.whatsapp.accounts
  • Credentials stored in ~/.openclaw/credentials/whatsapp/
  • Access to your agent configuration file

You can get your delivery settings dialed in by following these four steps:

  1. Set your chunking preference: Choose between length or newline in your config.
  2. Configure reactions: Add the ackReaction block to give users immediate feedback.
  3. Define media limits: Set your mediaMaxMb values for both inbound and outbound content.
  4. Verify auth paths: Ensure your account credentials are in the correct subfolders.

WhatsApp has limits on how much text you can send in one go. By default, the limit is 4000 characters, set via channels.whatsapp.textChunkLimit.

I recommend looking at channels.whatsapp.chunkMode. You have two choices:

  • length: Cuts the text strictly based on the character count.
  • newline: This is usually the better experience. It tries to break messages at paragraph boundaries (blank lines) first. If it can’t find a good break point, it falls back to the length limit.

When you send media, you can use HTTP(S) links, file:// URIs, or local paths. The system handles images, video, audio (PTT voice-notes), and documents.

Here are a few specific behaviors to keep in mind:

  • Voice Notes: If you send audio/ogg, it is automatically rewritten to audio/ogg; codecs=opus for better compatibility.
  • GIFs: If you want a video to play as an animated GIF, set gifPlayback: true.
  • Captions: When you send multiple media items in one payload, the caption attaches to the first item.
  • Auto-Optimization: Images are resized and quality-adjusted automatically to fit size limits.

The default size limits are 50MB for inbound media (channels.whatsapp.mediaMaxMb) and 5MB for outbound auto-replies (agents.defaults.mediaMaxMb).

You can make your bot feel more responsive by using immediate “ack” reactions. These happen as soon as the message is received, before the bot even finishes generating a reply.

{
channels: {
whatsapp: {
ackReaction: {
emoji: "👀",
direct: true,
group: "mentions", // always | mentions | never
},
},
},
}

This uses channels.whatsapp.ackReaction. Note that the legacy messages.ackReaction is ignored here. If you are in a group, you can set the mode to mentions so it only reacts when the bot is tagged, or always to bypass that check.

If you have multiple accounts, the system looks at channels.whatsapp.accounts. It defaults to the account named default, or the first one it finds if you haven’t specified a default.

Your credentials live at ~/.openclaw/credentials/whatsapp/<accountId>/creds.json. If you ever need to start fresh, you can use the CLI:

Terminal window
openclaw channels logout --channel whatsapp [--account <id>]

This clears the authentication state for that specific account. It removes the Baileys auth files but keeps your oauth.json if you are using legacy directories.

The agent can use a react tool to send reactions as an action. You can control these capabilities with specific gates:

  • channels.whatsapp.actions.reactions
  • channels.whatsapp.actions.polls

By default, the channel can write configuration changes. If you want to stop this, set channels.whatsapp.configWrites=false.

  • Media Send Failures: If a media file fails to send, the system doesn’t just stay silent. It triggers a fallback that sends a text warning instead.
  • Reaction Failures: If an acknowledgment reaction fails, it is logged, but it won’t block the actual reply from being delivered.

If you hit a wall with your configuration, check the AI Setup Assistant for help.

I’ve been there—you spend time setting up your gateway, you’re ready to see those messages fly, and then nothing happens. Debugging communication tools can be frustrating because there are so many moving parts between your code and the actual chat platform.

I want to help you get past these hurdles quickly. Most of the time, the fix is just a single CLI command or a small tweak in your configuration file. Let’s look at how to get your gateway back on track.

  • The openclaw CLI installed and configured.
  • An active WhatsApp or Telegram gateway setup.
  • Access to your terminal to run diagnostic commands.

If things aren’t working, follow this 5-minute path to find the cause:

  1. Check your link status: Run openclaw channels status to see if your account is actually connected.
  2. Run the diagnostic tool: Execute openclaw doctor to identify environment or runtime issues.
  3. Watch the logs: Use openclaw logs --follow to see real-time errors when you try to send or receive messages.

If your channel status reports that you are “not linked,” the gateway can’t communicate with the provider. You usually need to scan a QR code to fix this.

Fix:

Terminal window
openclaw channels login --channel whatsapp
openclaw channels status

Linked but disconnected or in a reconnect loop

Section titled “Linked but disconnected or in a reconnect loop”

You might see that your account is linked, but it keeps dropping the connection or attempting to reconnect repeatedly.

Fix: First, check your environment health, then watch the logs for specific error codes:

Terminal window
openclaw doctor
openclaw logs --follow

If the loop persists, try re-linking your account using channels login.

If your outbound sends are failing immediately, it usually means there is no active gateway listener for that specific account. Ensure the gateway process is actually running and that the account is properly linked.

If your gateway is working for direct messages but ignoring groups, check your configuration fields in this specific order:

  • groupPolicy
  • groupAllowFrom / allowFrom
  • groups allowlist entries
  • Mention gating settings (requireMention and your mention patterns)

If you are trying to run the WhatsApp gateway using Bun, you will run into stability issues. The WhatsApp gateway runtime should use Node. Bun is currently flagged as incompatible for stable WhatsApp or Telegram gateway operations.

When you need to fine-tune your setup, these are the high-signal fields in your Configuration reference - WhatsApp:

  • Access Control: dmPolicy, allowFrom, groupPolicy, groupAllowFrom, groups
  • Delivery Settings: textChunkLimit, chunkMode, mediaMaxMb, sendReadReceipts, ackReaction
  • Multi-account: accounts.<id>.enabled, accounts.<id>.authDir, and various account-level overrides
  • Operations: configWrites, debounceMs, web.enabled, web.heartbeatSeconds, web.reconnect.*
  • Session Behavior: session.dmScope, historyLimit, dmHistoryLimit, dms.<id>.historyLimit

If you are still stuck, you can get help from the AI Setup Assistant.

OpenClaw

OpenClaw Expert

Still stuck?

If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.