openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
242 lines (218 loc) • 13.6 kB
Markdown
---
summary: "Per-channel config keys for Google Chat, Slack, Mattermost, and Microsoft Teams"
read_when:
- Configuring Google Chat, Slack, Mattermost, or Microsoft Teams
- Setting Slack tokens, thread scope, streaming, or exec approvals
- Enabling Mattermost native slash commands
title: "Configuration — workplace chat channels"
---
`channels.*` keys for the workplace chat channels: Google Chat, Slack, Mattermost, and Microsoft Teams.
## Google Chat
```json5
{
channels: {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
audienceType: "app-url", // app-url | project-number
audience: "https://gateway.example.com/googlechat",
webhookPath: "/googlechat",
botUser: "users/1234567890",
dmPolicy: "pairing",
allowFrom: ["users/1234567890"],
groupPolicy: "allowlist",
groups: {
"spaces/AAAA": { enabled: true, requireMention: true },
},
typingIndicator: "message",
mediaMaxMb: 20,
},
},
}
```
- Service account JSON: inline (`serviceAccount`) or file-based (`serviceAccountFile`).
- `serviceAccount` accepts a SecretRef directly.
- Env fallbacks: `GOOGLE_CHAT_SERVICE_ACCOUNT` or `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE` (default account only).
- Use `spaces/<spaceId>` or `users/<userId>` for delivery targets.
- `channels.googlechat.dangerouslyAllowNameMatching` re-enables mutable email principal matching (break-glass compatibility mode).
## Slack
```json5
{
channels: {
slack: {
enabled: true,
botToken: "xoxb-...",
appToken: "xapp-...",
dmPolicy: "pairing",
allowFrom: ["U123", "U456", "*"],
dm: { enabled: true, groupEnabled: false, groupChannels: ["G123"] },
channels: {
C123: { enabled: true, requireMention: true },
C456: {
enabled: true,
requireMention: true,
allowBots: false,
users: ["U123"],
skills: ["docs"],
systemPrompt: "Short answers only.",
},
},
historyLimit: 50,
allowBots: true,
reactionNotifications: "own",
reactionAllowlist: ["U123"],
replyToMode: "off", // off | first | all | batched
thread: {
historyScope: "thread", // thread | channel
inheritParent: false,
initialHistoryLimit: 20,
},
actions: {
reactions: true,
messages: true,
pins: true,
memberInfo: true,
emojiList: true,
},
slashCommand: {
enabled: true,
name: "openclaw",
sessionPrefix: "slack:slash",
ephemeral: true,
},
typingReaction: "hourglass_flowing_sand",
unfurlLinks: false,
unfurlMedia: false,
textChunkLimit: 4000,
streaming: {
mode: "partial", // off | partial | block | progress
chunkMode: "length", // length | newline
nativeTransport: true, // use Slack native streaming API when mode=partial
},
mediaMaxMb: 20,
execApprovals: {
enabled: "auto", // true | false | "auto"
approvers: ["U123"],
agentFilter: ["default"],
sessionFilter: ["slack:"],
target: "dm", // dm | channel | both
},
},
},
}
```
- **Socket mode** requires both `botToken` and `appToken` (`SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` for default account env fallback).
- **HTTP mode** requires `botToken` plus `signingSecret` (at root or per-account).
- `allowBots` defaults to `true` and controls whether other bots can trigger turns. Explicit `false` remains an opt-out; room settings override account and root settings. Normal access, mention, bot-room authorization, self-message filtering, and [bot loop protection](/channels/bot-loop-protection) still apply. Bot-authored history and thread context remain subject to context visibility rules independently of this flag. See [Slack access control](/channels/slack/access-control#access-control-and-routing).
- `channels.slack.joinIntro` defaults to `true`. When the bot joins an allowed channel, it posts one introduction using the channel name, purpose or topic, and available recent messages. Set this option to `false` to disable introductions, or use `channels.slack.accounts.<accountId>.joinIntro` for an account-specific override. Up to 100 recent messages are read, once per channel; see [group join introductions](/channels#group-join-introductions). Introductions never run in direct messages.
- **User identity** (`postAs: "user"`) posts and reads as the authorizing human. It requires `userToken` plus `appToken` in Socket Mode, or `userToken` plus `signingSecret` in HTTP mode. No bot token or bot user is required. See [User identity](/channels/slack/setup#user-identity-post-as-a-real-person) for user scopes and event subscriptions.
- Slack detects Enterprise Grid org-wide installations automatically from the
bot token with `auth.test`; no installation-mode setting is required.
Enterprise DMs support `disabled`, `open`, `allowlist`, and workspace-scoped
`pairing`. Channel policies require `team:<team-id>:channel:<channel-id>`.
User policies accept either an org-wide stable user ID or
`team:<team-id>:user:<user-id>` for workspace scope. Mutable names and
unsupported channel prefixes fail startup.
Mention-pattern channel scopes and static route-binding peers use
workspace-qualified Slack targets.
Direct Socket Mode or HTTP messages, mentions, workspace-qualified actions,
deferred delivery, proactive sends, supported event listeners and
interactions, static route bindings, and Slack-native approvals from
workspace-qualified turns are supported. Relay, channel-ID-change events,
App Home, Agent and Assistant lifecycle events, configured ACP bindings, and
runtime current-conversation bindings remain unavailable. See
[Enterprise Grid org-wide installs](/channels/slack/enterprise-grid#enterprise-grid-org-wide-installs)
for the least-privilege manifest, setup workflow, and complete restrictions.
- The retired `enterpriseOrgInstall` key is removed by `openclaw doctor --fix`
at the Slack root and account levels.
- `botToken`, `appToken`, `signingSecret`, and `userToken` accept plaintext
strings or SecretRef objects.
- Slack account snapshots expose per-credential source/status fields such as
`botTokenSource`, `botTokenStatus`, `userTokenSource`, `userTokenStatus`,
`appTokenStatus`, and, in HTTP mode, `signingSecretStatus`.
`configured_unavailable` means the account is
configured through SecretRef but the current command/runtime path could not
resolve the secret value.
- `configWrites: false` blocks Slack-initiated config writes.
- Optional `channels.slack.defaultAccount` overrides default account selection when it matches a configured account id.
- `dm.groupEnabled` and `dm.groupChannels` only filter Slack group DMs (MPDMs) the app is already a member of. They cannot make the app see an existing group DM it never joined; convert the group DM to a private channel and invite the app, or have the app open a new MPDM with `conversations.open`. See [Group DMs (MPDMs) and bots](/channels/slack/access-control#group-dms-mpdms-and-bots).
- `channels.slack.streaming.mode` is the canonical Slack stream mode key (default `"progress"`). `channels.slack.streaming.nativeTransport` controls Slack's native streaming transport (default `true`). Legacy `streamMode`, boolean `streaming`, `chunkMode`, `blockStreaming`, `blockStreamingCoalesce`, and `nativeStreaming` values are no longer read at runtime; run `openclaw doctor --fix` to migrate persisted config to `streaming.{mode,chunkMode,block.enabled,block.coalesce,nativeTransport}`.
- `unfurlLinks` and `unfurlMedia` pass Slack's `chat.postMessage` link and media unfurl booleans through for bot replies. `unfurlLinks` defaults to `false` so outbound bot links do not expand inline unless enabled; `unfurlMedia` is omitted unless configured. Set either value at `channels.slack.accounts.<accountId>` to override the top-level value for one account.
- Use `user:<id>` (DM) or `channel:<id>` for delivery targets.
**Reaction notification modes:** `off`, `own` (default), `all`, `allowlist` (from `reactionAllowlist`).
**Thread session isolation:** `thread.historyScope` is per-thread (default) or shared across channel. `thread.inheritParent` copies parent channel transcript to new threads. `thread.initialHistoryLimit` (default `20`) caps how many existing thread messages are fetched when a new thread session starts; `0` disables thread history fetching.
- Slack native streaming plus the Slack assistant-style "is typing..." thread status require a reply thread target. Default `progress` turns without a reply thread, including plain top-level DMs, post only the final answer. Setting `streaming.progress.style`, setting `streaming.progress.toolProgress`, or setting `streaming.progress.nativeTaskCards: false` selects a preview. Threaded turns, Agent View, Assistant View, and explicit `off`/`partial`/`block` modes retain their behavior. See [Slack message behavior](/channels/slack/messaging#text-streaming).
- `typingReaction` adds a temporary reaction to the inbound Slack message while a reply is running, then removes it on completion. Defaults to `"hourglass_flowing_sand"` for quiet top-level progress turns; configured shortcodes override it and `""` disables it.
- `channels.slack.execApprovals`: Slack-native approval-client delivery and exec approver authorization. Same schema as Discord: `enabled` (`true`/`false`/`"auto"`), `approvers` (Slack user IDs), `agentFilter`, `sessionFilter`, and `target` (`"dm"`, `"channel"`, or `"both"`). Plugin approvals can use this native-client path for Slack-origin requests when Slack plugin approvers resolve; Slack-native plugin approval delivery can also be enabled through `approvals.plugin` for Slack-origin sessions or Slack targets. Plugin approvals use `approvals.plugin.slack` reviewer lists when set, falling back to Slack account `allowFrom` and default routing when the default list is omitted. They do not use exec approvers.
- An effective nonempty `approvals.plugin.slack` reviewer list enables native Slack plugin approval delivery independently of `channels.slack.execApprovals` and plugin forwarding.
| Action group | Default | Notes |
| ------------ | ------- | ---------------------- |
| reactions | enabled | React + list reactions |
| messages | enabled | Read/send/edit/delete |
| pins | enabled | Pin/unpin/list |
| memberInfo | enabled | Member info |
| emojiList | enabled | List custom emoji |
## Mattermost
Mattermost installs as a separate plugin, the same way Discord, Slack, and WhatsApp do:
```bash
openclaw plugins install /mattermost
```
Check [npmjs.com/package//mattermost](https://www.npmjs.com/package/@openclaw/mattermost) for the current dist-tags before pinning a version.
```json5
{
channels: {
mattermost: {
enabled: true,
botToken: "mm-token",
baseUrl: "https://chat.example.com",
dmPolicy: "pairing",
chatmode: "oncall", // oncall | onmessage | onchar
oncharPrefixes: [">", "!"],
groups: {
"*": { requireMention: true },
"team-channel-id": { requireMention: false },
},
commands: {
native: true, // opt-in
nativeSkills: true,
callbackPath: "/api/channels/mattermost/command",
// Optional explicit URL for reverse-proxy/public deployments
callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
},
textChunkLimit: 4000,
streaming: { chunkMode: "length" },
},
},
}
```
Chat modes: `oncall` (respond on @-mention, default), `onmessage` (every message), `onchar` (messages starting with trigger prefix).
When Mattermost native commands are enabled:
- `commands.callbackPath` must be a path (for example `/api/channels/mattermost/command`), not a full URL.
- `commands.callbackUrl` must resolve to the OpenClaw gateway endpoint and be reachable from the Mattermost server.
- Native slash callbacks are authenticated with the per-command tokens returned
by Mattermost during slash command registration. If registration fails or no
commands are activated, OpenClaw rejects callbacks with
`Unauthorized: invalid command token.`
- For private/tailnet/internal callback hosts, Mattermost may require
`ServiceSettings.AllowedUntrustedInternalConnections` to include the callback host/domain.
Use host/domain values, not full URLs.
- `channels.mattermost.configWrites`: allow or deny Mattermost-initiated config writes.
- `channels.mattermost.requireMention`: require `` before replying in channels.
- `channels.mattermost.groups.<channelId>.requireMention`: per-channel mention-gating override (`"*"` for default).
- Optional `channels.mattermost.defaultAccount` overrides default account selection when it matches a configured account id.
## Microsoft Teams
Microsoft Teams is plugin-backed and configured under `channels.msteams`.
```json5
{
channels: {
msteams: {
enabled: true,
configWrites: true,
// appId, appPassword, tenantId, webhook, team/channel policies:
// see /channels/msteams
},
},
}
```
- Core key paths covered here: `channels.msteams`, `channels.msteams.configWrites`.
- Full Teams config (credentials, webhook, DM/group policy, per-team/per-channel overrides) is documented in [Microsoft Teams](/channels/msteams).