--- summary: "Slack setup for socket or HTTP webhook mode" read_when: "Setting up Slack or debugging Slack socket/HTTP mode" --- # Slack ## Socket mode (default) ### Quick setup (beginner) 1) Create a Slack app and enable **Socket Mode**. 2) Create an **App Token** (`xapp-...`) and **Bot Token** (`xoxb-...`). 3) Set tokens for Clawdbot and start the gateway. Minimal config: ```json5 { channels: { slack: { enabled: true, appToken: "xapp-...", botToken: "xoxb-..." } } } ``` ### Setup 1) Create a Slack app (From scratch) in https://api.slack.com/apps. 2) **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`). 3) **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`). 4) Optional: **OAuth & Permissions** → add **User Token Scopes** (see the read-only list below). Reinstall the app and copy the **User OAuth Token** (`xoxp-...`). 5) **Event Subscriptions** → enable events and subscribe to: - `message.*` (includes edits/deletes/thread broadcasts) - `app_mention` - `reaction_added`, `reaction_removed` - `member_joined_channel`, `member_left_channel` - `channel_rename` - `pin_added`, `pin_removed` 6) Invite the bot to channels you want it to read. 7) Slash Commands → create `/clawd` if you use `channels.slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `channels.slack.commands.native: true` (global `commands.native` is `"auto"` which leaves Slack off). 8) App Home → enable the **Messages Tab** so users can DM the bot. Use the manifest below so scopes and events stay in sync. Multi-account support: use `channels.slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. ### Clawdbot config (minimal) Set tokens via env vars (recommended): - `SLACK_APP_TOKEN=xapp-...` - `SLACK_BOT_TOKEN=xoxb-...` Or via config: ```json5 { channels: { slack: { enabled: true, appToken: "xapp-...", botToken: "xoxb-..." } } } ``` ### User token (optional) Clawdbot can use a Slack user token (`xoxp-...`) for read operations (history, pins, reactions, emoji, member info). By default this stays read-only: reads prefer the user token when present, and writes still use the bot token unless you explicitly opt in. Even with `userTokenReadOnly: false`, the bot token stays preferred for writes when it is available. User tokens are configured in the config file (no env var support). For multi-account, set `channels.slack.accounts..userToken`. Example with bot + app + user tokens: ```json5 { channels: { slack: { enabled: true, appToken: "xapp-...", botToken: "xoxb-...", userToken: "xoxp-..." } } } ``` Example with userTokenReadOnly explicitly set (allow user token writes): ```json5 { channels: { slack: { enabled: true, appToken: "xapp-...", botToken: "xoxb-...", userToken: "xoxp-...", userTokenReadOnly: false } } } ``` #### Token usage - Read operations (history, reactions list, pins list, emoji list, member info, search) prefer the user token when configured, otherwise the bot token. - Write operations (send/edit/delete messages, add/remove reactions, pin/unpin, file uploads) use the bot token by default. If `userTokenReadOnly: false` and no bot token is available, Clawdbot falls back to the user token. ### History context - `channels.slack.historyLimit` (or `channels.slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt. - Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). ## HTTP mode (Events API) Use HTTP webhook mode when your Gateway is reachable by Slack over HTTPS (typical for server deployments). HTTP mode uses the Events API + Interactivity + Slash Commands with a shared request URL. ### Setup 1) Create a Slack app and **disable Socket Mode** (optional if you only use HTTP). 2) **Basic Information** → copy the **Signing Secret**. 3) **OAuth & Permissions** → install the app and copy the **Bot User OAuth Token** (`xoxb-...`). 4) **Event Subscriptions** → enable events and set the **Request URL** to your gateway webhook path (default `/slack/events`). 5) **Interactivity & Shortcuts** → enable and set the same **Request URL**. 6) **Slash Commands** → set the same **Request URL** for your command(s). Example request URL: `https://gateway-host/slack/events` ### Clawdbot config (minimal) ```json5 { channels: { slack: { enabled: true, mode: "http", botToken: "xoxb-...", signingSecret: "your-signing-secret", webhookPath: "/slack/events" } } } ``` Multi-account HTTP mode: set `channels.slack.accounts..mode = "http"` and provide a unique `webhookPath` per account so each Slack app can point to its own URL. ### Manifest (optional) Use this Slack app manifest to create the app quickly (adjust the name/command if you want). Include the user scopes if you plan to configure a user token. ```json { "display_information": { "name": "Clawdbot", "description": "Slack connector for Clawdbot" }, "features": { "bot_user": { "display_name": "Clawdbot", "always_online": false }, "app_home": { "messages_tab_enabled": true, "messages_tab_read_only_enabled": false }, "slash_commands": [ { "command": "/clawd", "description": "Send a message to Clawdbot", "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, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `channels.slack.commands.native`. ## Scopes (current vs optional) Slack's Conversations API is type-scoped: you only need the scopes for the conversation types you actually touch (channels, groups, im, mpim). See https://docs.slack.dev/apis/web-api/using-the-conversations-api/ for the overview. ### Bot token scopes (required) - `chat:write` (send/update/delete messages via `chat.postMessage`) https://docs.slack.dev/reference/methods/chat.postMessage - `im:write` (open DMs via `conversations.open` for user DMs) https://docs.slack.dev/reference/methods/conversations.open - `channels:history`, `groups:history`, `im:history`, `mpim:history` https://docs.slack.dev/reference/methods/conversations.history - `channels:read`, `groups:read`, `im:read`, `mpim:read` https://docs.slack.dev/reference/methods/conversations.info - `users:read` (user lookup) https://docs.slack.dev/reference/methods/users.info - `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`) https://docs.slack.dev/reference/methods/reactions.get https://docs.slack.dev/reference/methods/reactions.add - `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`) https://docs.slack.dev/reference/scopes/pins.read https://docs.slack.dev/reference/scopes/pins.write - `emoji:read` (`emoji.list`) https://docs.slack.dev/reference/scopes/emoji.read - `files:write` (uploads via `files.uploadV2`) https://docs.slack.dev/messaging/working-with-files/#upload ### User token scopes (optional, read-only by default) Add these under **User Token Scopes** if you configure `channels.slack.userToken`. - `channels:history`, `groups:history`, `im:history`, `mpim:history` - `channels:read`, `groups:read`, `im:read`, `mpim:read` - `users:read` - `reactions:read` - `pins:read` - `emoji:read` - `search:read` ### Not needed today (but likely future) - `mpim:write` (only if we add group-DM open/DM start via `conversations.open`) - `groups:write` (only if we add private-channel management: create/rename/invite/archive) - `chat:write.public` (only if we want to post to channels the bot isn't in) https://docs.slack.dev/reference/scopes/chat.write.public - `users:read.email` (only if we need email fields from `users.info`) https://docs.slack.dev/changelog/2017-04-narrowing-email-access - `files:read` (only if we start listing/reading file metadata) ## Config Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens: ```json { "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, "messages": true, "pins": true, "memberInfo": true, "emojiList": true }, "slashCommand": { "enabled": true, "name": "clawd", "sessionPrefix": "slack:slash", "ephemeral": true }, "textChunkLimit": 4000, "mediaMaxMb": 20 } } ``` Tokens can also be supplied via env vars: - `SLACK_BOT_TOKEN` - `SLACK_APP_TOKEN` Ack reactions are controlled globally via `messages.ackReaction` + `messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the ack reaction after the bot replies. ## Limits - Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000). - Optional newline chunking: set `channels.slack.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Media uploads are capped by `channels.slack.mediaMaxMb` (default 20). ## Reply threading By default, Clawdbot replies in the main channel. Use `channels.slack.replyToMode` to control automatic threading: | Mode | Behavior | | --- | --- | | `off` | **Default.** Reply in main channel. Only thread if the triggering message was already in a thread. | | `first` | First reply goes to thread (under the triggering message), subsequent replies go to main channel. Useful for keeping context visible while avoiding thread clutter. | | `all` | All replies go to thread. Keeps conversations contained but may reduce visibility. | The mode applies to both auto-replies and agent tool calls (`slack sendMessage`). ### Per-chat-type threading You can configure different threading behavior per chat type by setting `channels.slack.replyToModeByChatType`: ```json5 { channels: { slack: { replyToMode: "off", // default for channels replyToModeByChatType: { direct: "all", // DMs always thread group: "first" // group DMs/MPIM thread first reply }, } } } ``` Supported chat types: - `direct`: 1:1 DMs (Slack `im`) - `group`: group DMs / MPIMs (Slack `mpim`) - `channel`: standard channels (public/private) Precedence: 1) `replyToModeByChatType.` 2) `replyToMode` 3) Provider default (`off`) Legacy `channels.slack.dm.replyToMode` is still accepted as a fallback for `direct` when no chat-type override is set. Examples: Thread DMs only: ```json5 { channels: { slack: { replyToMode: "off", replyToModeByChatType: { direct: "all" } } } } ``` Thread group DMs but keep channels in the root: ```json5 { channels: { slack: { replyToMode: "off", replyToModeByChatType: { group: "first" } } } } ``` Make channels thread, keep DMs in the root: ```json5 { channels: { slack: { replyToMode: "first", replyToModeByChatType: { direct: "off", group: "off" } } } } ``` ### Manual threading tags For fine-grained control, use these tags in agent responses: - `[[reply_to_current]]` — reply to the triggering message (start/continue thread). - `[[reply_to:]]` — reply to a specific message id. ## Sessions + routing - DMs share the `main` session (like WhatsApp/Telegram). - Channels map to `agent::slack:channel:` sessions. - Slash commands use `agent::slack:slash:` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`). - If Slack doesn’t provide `channel_type`, Clawdbot infers it from the channel ID prefix (`D`, `C`, `G`) and defaults to `channel` to keep session keys stable. - Native command registration uses `commands.native` (global default `"auto"` → Slack off) and can be overridden per-workspace with `channels.slack.commands.native`. Text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands. - Full command list + config: [Slash commands](/tools/slash-commands) ## DM security (pairing) - Default: `channels.slack.dm.policy="pairing"` — unknown DM senders get a pairing code (expires after 1 hour). - Approve via: `clawdbot pairing approve slack `. - To allow anyone: set `channels.slack.dm.policy="open"` and `channels.slack.dm.allowFrom=["*"]`. - `channels.slack.dm.allowFrom` accepts user IDs, @handles, or emails (resolved at startup when tokens allow). The wizard accepts usernames and resolves them to ids during setup when tokens allow. ## Group policy - `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`). - `allowlist` requires channels to be listed in `channels.slack.channels`. - If you only set `SLACK_BOT_TOKEN`/`SLACK_APP_TOKEN` and never create a `channels.slack` section, the runtime defaults `groupPolicy` to `open`. Add `channels.slack.groupPolicy`, `channels.defaults.groupPolicy`, or a channel allowlist to lock it down. - The configure wizard accepts `#channel` names and resolves them to IDs when possible (public + private); if multiple matches exist, it prefers the active channel. - On startup, Clawdbot resolves channel/user names in allowlists to IDs (when tokens allow) and logs the mapping; unresolved entries are kept as typed. - To allow **no channels**, set `channels.slack.groupPolicy: "disabled"` (or keep an empty allowlist). Channel options (`channels.slack.channels.` or `channels.slack.channels.`): - `allow`: allow/deny the channel when `groupPolicy="allowlist"`. - `requireMention`: mention gating for the channel. - `allowBots`: allow bot-authored messages in this channel (default: false). - `users`: optional per-channel user allowlist. - `skills`: skill filter (omit = all skills, empty = none). - `systemPrompt`: extra system prompt for the channel (combined with topic/purpose). - `enabled`: set `false` to disable the channel. ## Delivery targets Use these with cron/CLI sends: - `user:` for DMs - `channel:` for channels ## Tool actions Slack tool actions can be gated with `channels.slack.actions.*`: | 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 | Custom emoji list | ## Security notes - Writes default to the bot token so state-changing actions stay scoped to the app's bot permissions and identity. - Setting `userTokenReadOnly: false` allows the user token to be used for write operations when a bot token is unavailable, which means actions run with the installing user's access. Treat the user token as highly privileged and keep action gates and allowlists tight. - If you enable user-token writes, make sure the user token includes the write scopes you expect (`chat:write`, `reactions:write`, `pins:write`, `files:write`) or those operations will fail. ## Notes - Mention gating is controlled via `channels.slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions. - Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. - Reaction notifications follow `channels.slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`). - Bot-authored messages are ignored by default; enable via `channels.slack.allowBots` or `channels.slack.channels..allowBots`. - Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`. - For the Slack tool, reaction removal semantics are in [/tools/reactions](/tools/reactions). - Attachments are downloaded to the media store when permitted and under the size limit.