Group Chat Behavior Across Channels | OpenClaw

Learn how OpenClaw handles group chats on Discord, iMessage, Matrix, and more. This guide covers default restrictions, mention gating, and allowlisting for group access.

Read this when

  • Changing group chat behavior or mention gating
  • Scoping mentionPatterns to specific group conversations

OpenClaw enforces identical group rules on every channel that supports groups: Discord, iMessage, Matrix, Microsoft Teams, QQBot, Signal, Slack, Telegram, WhatsApp, and Zalo.

For rooms that stay on permanently and should remain unobtrusive unless the agent deliberately posts a visible message, check Ambient room events.

Beginner intro (2 minutes)

OpenClaw operates directly on your personal messaging accounts. No separate WhatsApp bot account exists: when you belong to a group, OpenClaw can access it and respond there.

What happens by default:

  • Groups start restricted (groupPolicy: "allowlist"); senders in groups are blocked until they get allowlisted.
  • A mention is required for replies unless mention gating gets turned off for that group.
  • The final reply text is posted to the room automatically (visibleReplies: "automatic").

In short: an allowlisted sender can activate OpenClaw by mentioning it.

Note

TL;DR

  • DM access hinges on *.allowFrom.
  • Group access hinges on *.groupPolicy plus allowlists (*.groups, *.groupAllowFrom).
  • Reply triggering hinges on mention gating (requireMention, /activation).

The path a group message takes:

groupPolicy? disabled -> drop
groupPolicy? allowlist -> group allowed? no -> drop
requireMention? yes -> mentioned? no -> store for context only
mention/reply/command/DM -> user request
always-on group chatter -> user request, or room event when configured

Visible replies

For standard group or channel requests, OpenClaw picks messages.groupChat.visibleReplies: "automatic" as its default: the assistant's final text lands in the room as the visible reply.

Reach for messages.groupChat.visibleReplies: "message_tool" when a shared room should let the agent choose when to speak by invoking message(action=send). Models that follow tools reliably, such as GPT-5.6 Sol, work best here. If the model skips the tool and produces meaningful final text, OpenClaw withholds that text from the room rather than posting it.

Use "automatic" for models or runtimes that cannot consistently honor tool-only delivery: ordinary text finals go straight to the room, and the agent can still call message(action=send) for files, images, or other attachments that cannot travel with the final text.

When the active tool policy blocks the message tool, OpenClaw switches to automatic visible replies instead of quietly dropping the response. openclaw doctor flags this mismatch.

For direct chats and every other source event, messages.visibleReplies: "message_tool" applies the same tool-only behavior globally; messages.groupChat.visibleReplies remains the narrower override for group and channel rooms. Internal WebChat direct turns default to automatic final-reply delivery, so Pi and Codex see the same visible-reply contract.

Tool-only mode replaces the older approach of forcing the model to answer NO_REPLY for most lurk-mode turns. In tool-only mode the prompt carries no NO_REPLY contract; staying silent simply means the message tool was never called.

Plugin-owned conversation bindings sit outside this rule. Once a plugin binds a thread and takes the inbound turn, the plugin's returned reply becomes the visible binding response; message(action=send) is not required. That reply comes from the plugin runtime, not from private model final text.

Typing indicators still fire for direct group requests. Ambient always-on room events, when switched on, remain strict and quiet unless the agent calls the message tool.

Sessions suppress verbose tool and progress summaries by default. Use /verbose on (or /verbose full) to surface them for the current session during debugging, and /verbose off to go back to final-reply-only behavior. Verbose state is tracked per session and behaves identically in direct chats, groups, channels, and forum topics.

To submit unmentioned always-on group chatter as quiet room context rather than user requests, use Ambient room events:

{
  messages: {
    groupChat: {
      unmentionedInbound: "room_event",
    },
  },
}

The default is unmentionedInbound: "user_request". Mentioned messages, commands, abort requests, and DMs continue to count as user requests.

To force visible output through the message tool for group or channel requests:

{
  messages: {
    groupChat: {
      visibleReplies: "message_tool",
    },
  },
}

To force it for every source chat:

{
  messages: {
    visibleReplies: "message_tool",
  },
}

The gateway picks up messages config changes without a restart once the file is saved. Restart only when config reload is off (gateway.reload.mode: "off").

Command turns bypass visibleReplies: "message_tool" and always reply visibly: native slash commands (Discord, Telegram, and other surfaces with native command support) and authorized text /... commands both post their response to the source chat. Unauthorized text /... turns in groups stay message-tool-only; ordinary chat turns follow the configured default.

Context visibility and allowlists

Group safety relies on two separate controls:

  • Trigger authorization: who gets to activate the agent (groupPolicy, groups, groupAllowFrom, channel-specific allowlists).
  • Context visibility: what supplemental context gets injected into the model (reply or quote text, thread history, forwarded metadata).

By default OpenClaw passes context through as received: allowlists decide who can trigger actions, not which quoted or historical snippets reach the model. To filter supplemental context as well, set contextVisibility:

ModeBehavior
"all" (default)Keep supplemental context as received.
"allowlist"Only inject history/thread/quote/forwarded context from allowlisted senders.
"allowlist_quote"allowlist, plus keep the explicitly quoted/replied-to message from any sender.

Apply it per channel (channels.<channel>.contextVisibility), per account (channels.<channel>.accounts.<accountId>.contextVisibility), or globally (channels.defaults.contextVisibility). Channels that fetch supplemental context (Discord, Feishu, iMessage, Matrix, Mattermost, Microsoft Teams, QQBot, Signal, Slack, Telegram, WhatsApp) apply the policy when building inbound context; unknown policy combinations fail closed and omit the context.

These modes filter channel-supplied supplemental context only. Tool policy and the owner-only tool inventory are still selected from the current turn's originating requester, not every sender represented in the prompt. See Requester-scoped controls and prompt context.

Group message flow

If you want...

GoalWhat to set
Allow all groups but only reply on @mentionsgroups: { "*": { requireMention: true } }
Disable all group repliesgroupPolicy: "disabled"
Only specific groupsgroups: { "<group-id>": { ... } } (no "*" key)
Only you can trigger in groupsgroupPolicy: "allowlist", groupAllowFrom: ["+1555..."]
Reuse one trusted sender set across channelsgroupAllowFrom: ["accessGroup:operators"]

For reusable sender allowlists, see Access groups.

Session keys

  • Group sessions default to agent:<agentId>:<channel>:group:<id> session keys, whereas rooms and channels rely on agent:<agentId>:<channel>:channel:<id>.
  • Telegram forum topics append :topic:<threadId> to the group id, giving each topic its own dedicated session.
  • Direct chats operate on the main session, or on per-sender sessions when session.dmScope is enabled.
  • Heartbeats execute in the designated heartbeat session, which defaults to the agent's main session; group sessions never run their own heartbeats.

When a trusted room should share the agent's main conversation, assign session.groupScope to "main" on that binding:

{
  bindings: [
    {
      agentId: "main",
      match: { channel: "slack", peer: { kind: "channel", id: "C0123TEAM" } },
      session: { groupScope: "main" },
    },
  ],
}

The global session.groupScope accepts either "per-group" (the default) or "main". This setting has no effect on group admission, mention gating, or reply routing.

Pattern: personal DMs + public groups (single agent)

Absolutely, this setup suits you if your "personal" traffic consists of DMs and your "public" traffic is made up of groups.

Here's the reasoning: in single-agent mode, DMs typically land in the main session key (agent:main:main), while groups use non-main session keys (agent:main:<channel>:group:<id>) under the default groupScope: "per-group". Enabling sandboxing with mode: "non-main" routes those group sessions to the configured sandbox backend, leaving your main DM session on the host. If you don't specify a backend, Docker is used by default.

Warning

A room set with groupScope: "main" counts as a main session and falls outside the sandbox's mode: "non-main" protection. Avoid merging untrusted or public rooms into main when you depend on that sandbox boundary.

The result is a single agent "brain" (shared workspace plus memory) operating in two distinct modes:

  • DMs: full tool access on the host
  • Groups: sandboxed with restricted tools

Note

For genuinely separate workspaces or personas, where "personal" and "public" must stay completely isolated, deploy a second agent with its own bindings. See Multi-Agent Routing.

DMs on host, groups sandboxed

{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main", // groups/channels are non-main -> sandboxed
        scope: "session", // strongest isolation (one container per group/channel)
        workspaceAccess: "none",
      },
    },
  },
  tools: {
    sandbox: {
      tools: {
        // If allow is non-empty, everything else is blocked (deny still wins).
        allow: ["group:messaging", "group:sessions"],
        deny: ["group:runtime", "group:fs", "group:ui", "nodes", "cron", "gateway"],
      },
    },
  },
}

Groups see only an allowlisted folder

Prefer "groups can only see folder X" over "no host access"? Retain workspaceAccess: "none" and mount only allowlisted paths into the sandbox:

{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main",
        scope: "session",
        workspaceAccess: "none",
        docker: {
          binds: [
            // hostPath:containerPath:mode
            "/home/user/FriendsShared:/data:ro",
          ],
        },
      },
    },
  },
}

Related:

Display labels

  • UI labels prefer displayName when it's available, displayed as <channel>:<token>.
  • #room is set aside for rooms and channels; group chats use g-<slug> (lowercase, spaces become -, keep #@+._-). Very long opaque ids are condensed into a stable token, so full route ids never leak into the UI.

Group policy

Control per-channel handling of group and room messages:

{
  channels: {
    whatsapp: {
      groupPolicy: "disabled", // "open" | "disabled" | "allowlist"
      groupAllowFrom: ["+15551234567"],
    },
    telegram: {
      groupPolicy: "disabled",
      groupAllowFrom: ["123456789"], // numeric Telegram user id (setup resolves @username)
    },
    signal: {
      groupPolicy: "disabled",
      groupAllowFrom: ["+15551234567"],
    },
    imessage: {
      groupPolicy: "disabled",
      groupAllowFrom: ["chat_id:123"],
    },
    msteams: {
      groupPolicy: "disabled",
      groupAllowFrom: ["user@org.com"],
    },
    discord: {
      groupPolicy: "allowlist",
      guilds: {
        GUILD_ID: { channels: { help: { enabled: true } } },
      },
    },
    slack: {
      groupPolicy: "allowlist",
      channels: { "#general": { enabled: true } },
    },
    matrix: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["@owner:example.org"],
      groups: {
        "!roomId:example.org": { enabled: true },
        "#alias:example.org": { enabled: true },
      },
    },
  },
}
PolicyBehavior
"open"Groups skip allowlists, but mention-gating still applies.
"disabled"Reject all group messages outright.
"allowlist"Permit only groups or rooms that fit the configured allowlist.

Per-channel notes

  • groupPolicy operates independently of mention-gating, which only reacts to @mentions.
  • For WhatsApp, Telegram, Signal, iMessage, Microsoft Teams, and Zalo, rely on groupAllowFrom (with explicit allowFrom as the alternative).
  • Signal: groupAllowFrom can match either the incoming group id from the inbound message or the sender's phone number/UUID.
  • DM pairing approvals, stored via *-allowFrom, govern access solely for DMs; group sender authorization remains tied to explicit group allowlists.
  • Discord's allowlist is defined by channels.discord.guilds.<id>.channels.
  • Slack's allowlist relies on channels.slack.channels.
  • Matrix's allowlist uses channels.matrix.groups. Room IDs work here (!room:server, or the suffixless !room variant on room version 12+), as do aliases (#alias:server); room-name keys only match with channels.matrix.dangerouslyAllowNameMatching: true, and unresolved entries get ignored at runtime. To limit senders, apply channels.matrix.groupAllowFrom; per-room users allowlists are also an option.
  • Group DMs get their own controls (channels.discord.dm.*, channels.slack.dm.*: groupEnabled, groupChannels).
  • Telegram: sender allowlists accept only numeric user IDs ("123456789"; the telegram:/tg: prefixes are removed case-insensitively). @username entries never match at runtime and trigger a warning log; setup converts @username into IDs. Negative chat IDs fall under channels.telegram.groups, not sender allowlists.
  • The default is groupPolicy: "allowlist"; an empty group allowlist results in blocked group messages.
  • Runtime safety: if a provider block is entirely absent (no channels.<provider>), group policy fails closed to allowlist rather than adopting channels.defaults.groupPolicy, and the gateway logs this fallback once per account.

Quick mental model (evaluation order for group messages):

groupPolicy

groupPolicy (open/disabled/allowlist).

Group allowlists

Group allowlists (*.groups, *.groupAllowFrom, channel-specific allowlist).

Mention gating

Mention gating (requireMention, /activation).

Mention gating (default)

Group messages need a mention unless a per-group override exists. Defaults are set per subsystem under *.groups."*".

Supported implicit mention facts vary by channel:

FactCurrent built-in producers
Reply to the botDiscord, Microsoft Teams, QQBot, Slack, Telegram
Quote of the botLINE, WhatsApp, Zalo personal
Bot joined the threadMattermost, Slack, Tlon

Each fact is enabled by default when the channel produces it. Among bundled channels, LINE, Mattermost, Slack, and Tlon check the implicitMentions flag; setting it to false prevents that fact from bypassing mention gating. LINE only reads this from the shared channels.defaults.implicitMentions block; it does not accept a channel- or account-scoped implicitMentions block of its own. Native explicit mentions are unaffected. The other bundled producers listed do not currently read implicitMentions, so their facts always count as mentions and the flag cannot disable them. A flag also has no effect on channels that do not produce that fact.

{
  channels: {
    whatsapp: {
      groups: {
        "*": { requireMention: true },
        "123@g.us": { requireMention: false },
      },
    },
    telegram: {
      groups: {
        "*": { requireMention: true },
        "123456789": { requireMention: false },
      },
    },
    imessage: {
      groups: {
        "*": { requireMention: true },
        "123": { requireMention: false },
      },
    },
  },
  agents: {
    entries: {
      main: {
        default: true,
        groupChat: {
          mentionPatterns: ["@openclaw", "openclaw", "\\+15555550123"],
          historyLimit: 50,
        },
      },
    },
  },
}

Scope configured mention patterns

Configured mentionPatterns act as regex fallback triggers. Use them when the platform lacks a native bot mention, or when plain text like openclaw: should be treated as a mention. Native platform mentions are distinct: when Discord, Slack, Telegram, Matrix, Signal, or another channel can confirm the message explicitly mentioned the bot, that native mention still fires even if configured regex patterns are disallowed.

By default, configured mention patterns apply everywhere the channel passes provider and conversation facts into mention detection. To stop broad patterns from triggering the agent in every group, scope them per channel with channels.<channel>.mentionPatterns.

Apply mode: "deny" when regex mention patterns should be disabled by default for a channel, then enable specific rooms with allowIn:

{
  messages: {
    groupChat: {
      mentionPatterns: ["\\bopenclaw\\b", "\\bops bot\\b"],
    },
  },
  channels: {
    slack: {
      mentionPatterns: {
        mode: "deny",
        allowIn: ["C0123OPS"],
      },
    },
  },
}

Use the default mode: "allow" (or omit mode) when regex mention patterns should apply widely, then disable them in noisy rooms with denyIn:

{
  messages: {
    groupChat: {
      mentionPatterns: ["\\bopenclaw\\b"],
    },
  },
  channels: {
    telegram: {
      mentionPatterns: {
        denyIn: ["-1001234567890", "-1001234567890:topic:42"],
      },
    },
  },
}

Policy resolution:

FieldEffect
mode: "allow"Regex mention patterns stay active by default, except when the conversation ID appears in denyIn.
mode: "deny"Regex mention patterns remain off unless the conversation ID is listed in allowIn.
allowInConversation IDs that enable regex mention patterns while in deny mode.
denyInConversation IDs that turn off regex mention patterns. If an ID shows up in both, denyIn takes priority over allowIn.

Here is the currently supported scoped regex policy:

ChannelIDs used in allowIn / denyIn
DiscordDiscord channel IDs.
MatrixMatrix room IDs.
SlackSlack channel IDs.
TelegramGroup chat IDs, or chatId:topic:threadId for forum topics.
WhatsAppWhatsApp conversation IDs such as 123@g.us.

When a channel handles multiple accounts, the same policy can be applied at the account level under channels.<channel>.accounts.<accountId>.mentionPatterns. For that account, the account-level policy overrides the top-level channel policy.

Mention gating notes

  • mentionPatterns are case-insensitive safe regex patterns; invalid patterns and unsafe nested-repetition forms are ignored (with a warning).
  • Pattern precedence: agents.entries.*.groupChat.mentionPatterns (useful when multiple agents share a group) overrides messages.groupChat.mentionPatterns; when neither is set, patterns are derived from the agent identity name/emoji.
  • Mention gating is only enforced when mention detection is possible (native mentions or mentionPatterns are configured).
  • Allowlisting a group or sender does not disable mention gating; set that group's requireMention to false when all messages should trigger.
  • Automatic group chat prompt context carries the resolved silent-reply instruction every turn; workspace files should not duplicate NO_REPLY mechanics.
  • Groups where automatic silent replies are allowed treat clean empty or reasoning-only model turns as silent, equivalent to NO_REPLY. Direct chats never receive NO_REPLY guidance, and message-tool-only group replies stay quiet by not calling message(action=send).
  • Ambient always-on group chatter uses user-request semantics by default. Set messages.groupChat.unmentionedInbound: "room_event" to submit it as quiet context instead. See Ambient room events for setup examples.
  • Room events are not stored as fake user requests, and private assistant text from no-message-tool room events is not replayed as chat history.
  • Discord defaults live in channels.discord.guilds."*" (overridable per guild/channel).
  • Group history context is wrapped uniformly across channels. Mention-gated groups keep pending skipped messages; always-on groups may also retain recent processed room messages when the channel supports it. Use messages.groupChat.historyLimit for the global default and channels.<channel>.historyLimit (or channels.<channel>.accounts.*.historyLimit) for overrides. Set 0 to disable.

Group/channel tool restrictions (optional)

Certain channel configurations let you limit which tools are usable within a specific group, room, or channel.

  • tools: allow or deny tools for the entire group (allow, alsoAllow, deny; deny wins).
  • toolsBySender: per-sender overrides inside the group. Use explicit key prefixes: channel:<channelId>:<senderId>, id:<senderId>, e164:<phone>, username:<handle>, name:<displayName>, and "*" wildcard. Channel ids use canonical OpenClaw channel ids; aliases such as teams normalize to msteams. Legacy unprefixed keys are still accepted, matched as id: only, and log a deprecation warning.

Resolution order (most specific wins):

Group toolsBySender

Group/channel toolsBySender match.

Group tools

Group/channel tools.

Default toolsBySender

Default ("*") toolsBySender match.

Default tools

Default ("*") tools.

Example (Telegram):

{
  channels: {
    telegram: {
      groups: {
        "*": { tools: { deny: ["exec"] } },
        "-1001234567890": {
          tools: { deny: ["exec", "read", "write"] },
          toolsBySender: {
            "id:123456789": { alsoAllow: ["exec"] },
          },
        },
      },
    },
  },
}

Note

Group/channel tool restrictions are applied in addition to global/agent tool policy (deny still wins). Some channels use different nesting for rooms/channels (e.g., Discord guilds.*.channels.*, Slack channels.*, Microsoft Teams teams.*.channels.*).

Group allowlists

When channels.whatsapp.groups, channels.telegram.groups, or channels.imessage.groups is set, the keys function as a group allowlist. To permit every group while still establishing default mention behavior, use "*".

Note

A frequent misunderstanding: approving DM pairing differs from authorizing groups. On channels that support DM pairing, the pairing store only unlocks direct messages. Group commands still need explicit group sender authorization from config allowlists like groupAllowFrom or the documented config fallback for that channel.

Common intents (copy/paste):

Disable all group replies

{
  channels: { whatsapp: { groupPolicy: "disabled" } },
}

Allow only specific groups (WhatsApp)

{
  channels: {
    whatsapp: {
      groups: {
        "123@g.us": { requireMention: true },
        "456@g.us": { requireMention: false },
      },
    },
  },
}

Allow all groups but require mention

{
  channels: {
    whatsapp: {
      groups: { "*": { requireMention: true } },
    },
  },
}

Owner-only triggers (WhatsApp)

{
  channels: {
    whatsapp: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15551234567"],
      groups: { "*": { requireMention: true } },
    },
  },
}

Activation (owner-only)

Group owners can toggle per-group activation with a standalone message:

  • /activation mention
  • /activation always

/activation is a core owner-gated command and only applies in group chats. Owner means the sender matches commands.ownerAllowFrom; channel allowFrom lists only control ordinary channel and command access. The stored mode overrides that group's requireMention on channels that consult it (Google Chat, QQBot, Telegram, WhatsApp), and the group system-prompt intro reflects the active mode everywhere.

Context fields

Group inbound payloads set:

  • ChatType=group
  • GroupSubject (if known)
  • GroupMembers (if known)
  • WasMentioned (mention gating result)
  • Telegram forum topics also include MessageThreadId and IsForum.

The agent system prompt includes a group intro on the first turn of a new group session (and after /activation changes). It reminds the model to respond like a human, minimize empty lines and follow normal chat spacing, and avoid typing literal \n sequences. Channels whose declared table mode does not preserve native or raw tables also discourage Markdown tables. Channel-sourced group names and participant labels are rendered as fenced untrusted metadata, not inline system instructions.

iMessage specifics

  • Prefer chat_id:<id> when routing or allowlisting.
  • List chats: imsg chats --limit 20.
  • Group replies always go back to the same chat_id.

WhatsApp system prompts

See WhatsApp for the canonical WhatsApp system prompt rules, including group and direct prompt resolution, wildcard behavior, and account override semantics.

WhatsApp specifics

See Group messages for WhatsApp-only behavior (history injection, mention handling details).

3,469 words · updated Sep 1, 2026