Slack
Slack configuration via Socket Mode or HTTP (Events API).
Socket Mode (Default)
#
Quick Setup for Beginners
1. Create a Slack App and enable <strong>Socket Mode</strong>.
2. Create an <strong>App Token</strong> (<code>xapp-...</code>) and a <strong>Bot Token</strong> (<code>xoxb-...</code>).
3. Configure the tokens in OpenClaw and start the gateway.
Minimal configuration:
{
channels: {
slack: {
enabled: true,
appToken: "xapp-...",
botToken: "xoxb-...",
},
},
}#
Detailed Setup
1. Create a Slack App at https://api.slack.com/apps (From scratch).
2. <strong>Socket Mode</strong> β Enable. Then go to <strong>Basic Information</strong> β <strong>App-Level Tokens</strong> β <strong>Generate Token and Scopes</strong>, add scope <code>connections:write</code>. Copy the <strong>App Token</strong> (<code>xapp-...</code>).
3. <strong>OAuth & Permissions</strong> β Add bot token scopes (recommend using the manifest below to ensure scopes match event subscriptions). Click <strong>Install to Workspace</strong>, copy the <strong>Bot User OAuth Token</strong> (<code>xoxb-...</code>).
4. Optional: <strong>OAuth & Permissions</strong> β Add <strong>User Token Scopes</strong> (read-only list below). Reinstall the app and copy the <strong>User OAuth Token</strong> (<code>xoxp-...</code>).
5. <strong>Event Subscriptions</strong> β Enable events and subscribe to:
- <code>message.*</code> (includes edit/delete/thread broadcasts)
- <code>app_mention</code>
- <code>reaction_added</code>, <code>reaction_removed</code>
- <code>member_joined_channel</code>, <code>member_left_channel</code>
- <code>channel_rename</code>
- <code>pin_added</code>, <code>pin_removed</code>
6. Invite the bot to the channels you want it to read.
7. Slash Commands: If you use <code>channels.slack.slashCommand</code>, create <code>/openclaw</code>. If you enable native commands, you need to create a slash command for each built-in command (same names as in <code>/help</code>). Slack will not enable native commands by default unless you explicitly set <code>channels.slack.commands.native: true</code> (global <code>commands.native</code> defaults to <code>"auto"</code>, which keeps Slack disabled).
8. App Home: Enable <strong>Messages Tab</strong> so users can DM the bot.
Recommend using the manifest below to avoid missing scopes/events.
Multi-account: Use '<code>'channels.slack.accounts'</code>' to configure tokens for each workspace (optional '<code>'name'</code>'). For shared structure, see '<a href="/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts">'/gateway/configuration'</a>'.
#
OpenClaw Configuration (Minimal)
Recommended using environment variables (suitable for servers):
- <code>SLACK_APP_TOKEN=xapp-...</code>
- <code>SLACK_BOT_TOKEN=xoxb-...</code>
Or configuration file:
{
channels: {
slack: {
enabled: true,
appToken: "xapp-...",
botToken: "xoxb-...",
},
},
}#
User Token (Optional)
OpenClaw can use a Slack user token (<code>xoxp-...</code>) for read operations (history, pins, reactions, emoji, member info, etc.). By default, it remains read-only: reads prioritize user token (if present), writes still prioritize bot token unless you explicitly allow user token writes. Even if you set <code>userTokenReadOnly: false</code>, as long as bot token is available, write operations will still prioritize bot token.
User token can only be set via configuration file (not supported via env). For multi-account, set '<code>'channels.slack.accounts.'<id>'.userToken'</code>'.
Example (bot + app + user tokens):
{
channels: {
slack: {
enabled: true,
appToken: "xapp-...",
botToken: "xoxb-...",
userToken: "xoxp-...",
},
},
}Allow user token writes (use with caution):
{
channels: {
slack: {
enabled: true,
appToken: "xapp-...",
botToken: "xoxb-...",
userToken: "xoxp-...",
userTokenReadOnly: false,
},
},
}##
Token Usage Rules
- Read operations (history, reactions list, pins list, emoji list, member info, search) prioritize user token (if configured), otherwise use bot token.
- Write operations (send/edit/delete messages, add/remove reactions, pin/unpin, file upload) default to bot token; only fallback to user token if <code>userTokenReadOnly: false</code> and bot token is unavailable.
#
History Context
- <code>channels.slack.historyLimit</code> (or <code>channels.slack.accounts.*.historyLimit</code>) controls how many recent channel/group messages are injected as context.
- Fallback to <code>messages.groupChat.historyLimit</code>. Set to <code>0</code> to disable (default 50).
HTTP Mode (Events API)
When your Gateway can be accessed by Slack via HTTPS (typical server deployment), HTTP webhook mode is more suitable. HTTP mode uses Events API + Interactivity + Slash Commands, sharing the same Request URL.
#
Setup
1. Create a Slack App and (optionally) disable Socket Mode (if you only use HTTP).
2. <strong>Basic Information</strong> β Copy the <strong>Signing Secret</strong>.
3. <strong>OAuth & Permissions</strong> β Install the app and copy the <strong>Bot User OAuth Token</strong> (<code>xoxb-...</code>).
4. <strong>Event Subscriptions</strong> β Enable events and point the <strong>Request URL</strong> to the gateway's webhook path (default <code>/slack/events</code>).
5. <strong>Interactivity & Shortcuts</strong> β Enable and set the same <strong>Request URL</strong>.
6. <strong>Slash Commands</strong> β Set the same <strong>Request URL</strong> for your commands.
Example public URL:
<code>https://gateway-host/slack/events</code>
#
OpenClaw Configuration (Minimal)
{
channels: {
slack: {
enabled: true,
mode: "http",
botToken: "xoxb-...",
signingSecret: "your-signing-secret",
webhookPath: "/slack/events",
},
},
}Multi-account HTTP mode: Set '<code>'channels.slack.accounts.'<id>'.mode = "http"'</code>' for each account, and provide a unique '<code>'webhookPath'</code>' for each account so each Slack App points to a different URL.
#
Manifest (Optional)
You can use a manifest to quickly create an app (change name/commands as needed). If you plan to configure user token, please add user scopes as well.
{
"display_information": {
"name": "OpenClaw",
"description": "Slack connector for OpenClaw"
},
"features": {
"bot_user": {
"display_name": "OpenClaw",
"always_online": false
},
"app_home": {
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
},
"slash_commands": [
{
"command": "/openclaw",
"description": "Send a message to OpenClaw",
"should_escape": false
}
]
},
"oauth_config": {
"scopes": {
"bot": [
"chat:write",
"channels:history",
"channels:read",
"groups:history",
"groups:read",
"groups:write",
"im:history",
"im:read",
"im:write",
"mpim:history",
"mpim:read",
"mpim:write",
"users:read",
"app_mentions:read",
"reactions:read",
"reactions:write",
"pins:read",
"pins:write",
"emoji:read",
"commands",
"files:read",
"files:write"
],
"user": [
"channels:history",
"channels:read",
"groups:history",
"groups:read",
"im:history",
"im:read",
"mpim:history",
"mpim:read",
"users:read",
"reactions:read",
"pins:read",
"emoji:read",
"search:read"
]
}
},
"settings": {
"socket_mode_enabled": true,
"event_subscriptions": {
"bot_events": [
"app_mention",
"message.channels",
"message.groups",
"message.im",
"message.mpim",
"reaction_added",
"reaction_removed",
"member_joined_channel",
"member_left_channel",
"channel_rename",
"pin_added",
"pin_removed"
]
}
}
}If you enable native commands, you need to add a <code>slash_commands</code> entry for each command you want to expose (consistent with the <code>/help</code> list), and use <code>channels.slack.commands.native</code> to override the default behavior.
Scopes (Required vs Optional)
Slack Conversations API splits scopes by conversation type: you only need the conversation type scopes you'll use (channels, groups, im, mpim). Overview: https://docs.slack.dev/apis/web-api/using-the-conversations-api/
#
Bot Token Scopes (Required)
- <code>chat:write</code> (send/update/delete messages)
https://docs.slack.dev/reference/methods/chat.postMessage
- <code>im:write</code> (open DM conversation, for user DMs)
https://docs.slack.dev/reference/methods/conversations.open
- <code>channels:history</code>, <code>groups:history</code>, <code>im:history</code>, <code>mpim:history</code>
https://docs.slack.dev/reference/methods/conversations.history
- <code>channels:read</code>, <code>groups:read</code>, <code>im:read</code>, <code>mpim:read</code>
https://docs.slack.dev/reference/methods/conversations.info
- <code>users:read</code> (user info query)
https://docs.slack.dev/reference/methods/users.info
- <code>reactions:read</code>, <code>reactions:write</code>
https://docs.slack.dev/reference/methods/reactions.get
https://docs.slack.dev/reference/methods/reactions.add
- <code>pins:read</code>, <code>pins:write</code>
https://docs.slack.dev/reference/scopes/pins.read
https://docs.slack.dev/reference/scopes/pins.write
- <code>emoji:read</code>
https://docs.slack.dev/reference/scopes/emoji.read
- <code>files:write</code> (upload)
https://docs.slack.dev/messaging/working-with-files/#upload
#
User Token Scopes (Optional, Read-Only by Default)
Only add to <strong>User Token Scopes</strong> when you configure <code>channels.slack.userToken</code>:
- <code>channels:history</code>, <code>groups:history</code>, <code>im:history</code>, <code>mpim:history</code>
- <code>channels:read</code>, <code>groups:read</code>, <code>im:read</code>, <code>mpim:read</code>
- <code>users:read</code>
- <code>reactions:read</code>
- <code>pins:read</code>
- <code>emoji:read</code>
- <code>search:read</code>
Configuration Example
{
"slack": {
"enabled": true,
"botToken": "xoxb-...",
"appToken": "xapp-...",
"groupPolicy": "allowlist",
"dm": {
"enabled": true,
"policy": "pairing",
"allowFrom": ["U123", "U456", "*"],
"groupEnabled": false,
"groupChannels": ["G123"],
"replyToMode": "all"
},
"channels": {
"C123": { "allow": true, "requireMention": true },
"#general": {
"allow": true,
"requireMention": true,
"users": ["U123"],
"skills": ["search", "docs"],
"systemPrompt": "Keep answers short."
}
},
"reactionNotifications": "own",
"reactionAllowlist": ["U123"],
"replyToMode": "off",
"actions": {
"reactions": true,
"reactionLevel": "minimal"
},
"commands": {
"native": false
}
}
}Ack reactions are controlled globally by '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'messages.ackReaction'</code>' + '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'messages.ackReactionScope'</code>'; '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'messages.removeAckAfterReply'</code>' can remove the ack reaction after a reply.
Threading
Slack supports threading replies. OpenClaw can reply in threads or at the top level.
- <code>channels.slack.replyToMode</code>: <code>off | all | first | last</code>
- <code>off</code>: Reply at top level (no thread).
- <code>all</code>: Reply in thread for all messages.
- <code>first</code>: Reply in thread for the first message only.
- <code>last</code>: Reply in thread for the last message only.
- Default: <code>off</code>.
- Per-channel override: '<code>'channels.slack.channels.'<id>'.replyToMode'</code>'.
- Per-channel users: '<code>'channels.slack.channels.'<id>'.users'</code>'.
- Per-channel skills: '<code>'channels.slack.channels.'<id>'.skills'</code>'.
- Per-channel system prompt: '<code>'channels.slack.channels.'<id>'.systemPrompt'</code>'.
Threading for Direct Messages
DMs can also use threading. Configure via <code>channels.slack.dm.replyToMode</code>.
- <code>off</code>: Reply at top level.
- <code>all</code>: Reply in thread for all messages.
- <code>first</code>: Reply in thread for the first message only.
- <code>last</code>: Reply in thread for the last message only.
- Default: <code>off</code>.
Configure Threading by Chat Type
Use <code>channels.slack.replyToModeByChatType</code>:
{
channels: {
slack: {
replyToMode: "off",
replyToModeByChatType: {
direct: "all",
group: "first",
},
},
},
}Supported chat types:
- <code>direct</code>: One-on-one DMs (Slack <code>im</code>)
- <code>group</code>: Group DMs/MPIM (Slack <code>mpim</code>)
- <code>channel</code>: Regular channels (public/private)
Priority:
1. 1. '<code>'replyToModeByChatType.'<chatType>''</code>'
2. 2. <code>replyToMode</code>
3. 3. Default value (<code>off</code>)
Legacy configuration <code>channels.slack.dm.replyToMode</code> is still accepted as a fallback for <code>direct</code>.
#
Manual Threading Tags
For finer-grained control, use in agent replies:
- <code>[[reply_to_current]]</code>
- '<code>'[[reply_to:'<id>']]'</code>'
Sessions and Routing
- DMs use the <code>main</code> session (similar to WhatsApp/Telegram).
- Channel session key: '<code>'agent:'<agentId>':slack:channel:'<channelId>''</code>'.
- Slash command session key: '<code>'agent:'<agentId>':slack:slash:'<userId>''</code>' (prefix can be configured via '<code>'channels.slack.slashCommand.sessionPrefix'</code>').
- If Slack doesn't provide <code>channel_type</code>, OpenClaw infers by channel id prefix (<code>D</code>/<code>C</code>/<code>G</code>), defaulting to <code>channel</code> to ensure session key stability.
- Native command registration: Global <code>commands.native</code> defaults to <code>"auto"</code> (Slack defaults to disabled), can be overridden with <code>channels.slack.commands.native</code>. Text commands require separate <code>/...</code> messages, can be disabled with <code>commands.text: false</code>. Slack slash commands are managed by the Slack App and won't be automatically removed. <code>commands.useAccessGroups: false</code> can bypass access-group checks.
- Command list and configuration: '<a href="/tools/slash-commands">'/tools/slash-commands'</a>'
DM Security (Pairing)
- Default: <code>channels.slack.dm.policy="pairing"</code> (unknown senders receive a pairing code, expires in 1 hour).
- Approve: '<code>'openclaw pairing approve slack '<code>''</code>'.
- To allow anyone to DM: <code>channels.slack.dm.policy="open"</code> and <code>channels.slack.dm.allowFrom=["*"]</code>.
- <code>channels.slack.dm.allowFrom</code> supports user id, @handle, or email (resolved at startup if tokens allow). The wizard will resolve username to id when possible.
Group Policy
- <code>channels.slack.groupPolicy</code> controls channel handling: <code>open|disabled|allowlist</code>.
- <code>allowlist</code> requires listing allowed channels in <code>channels.slack.channels</code>.
- If you only set <code>SLACK_BOT_TOKEN</code>/<code>SLACK_APP_TOKEN</code> but never create a <code>channels.slack</code> configuration section, the runtime default <code>groupPolicy</code> is <code>open</code>. Once you explicitly configure <code>channels.slack.groupPolicy</code>/<code>channels.defaults.groupPolicy</code> or a channel allowlist, it will be tightened.