WhatsApp (Web channel)

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

Quick setup

1

Configure WhatsApp access policy

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

Link WhatsApp (QR)

bash
mayros channels login --channel whatsapp

For a specific account:

bash
mayros channels login --channel whatsapp --account work
3

Start the gateway

bash
mayros gateway
4

Approve first pairing request (if using pairing mode)

bash
mayros pairing list whatsapp
mayros pairing approve whatsapp <CODE>

Pairing requests expire after 1 hour. Pending requests are capped at 3 per channel.

Mayros 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

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:<agentId>:whatsapp:group:<jid>).

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.<id>.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

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 [mayros]

Message normalization and context

Delivery, chunking, and media

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

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

Configuration reference pointers

Primary reference:

High-signal WhatsApp fields:

  • access: dmPolicy, allowFrom, groupPolicy, groupAllowFrom, groups
  • delivery: textChunkLimit, chunkMode, mediaMaxMb, sendReadReceipts, ackReaction
  • multi-account: accounts.<id>.enabled, accounts.<id>.authDir, account-level overrides
  • operations: configWrites, debounceMs, web.enabled, web.heartbeatSeconds, web.reconnect.*
  • session behavior: session.dmScope, historyLimit, dmHistoryLimit, dms.<id>.historyLimit