- Add userToken and userTokenReadOnly (default: true) config fields - Implement token routing: reads prefer user token, writes use bot token - Add tests for token routing logic - Update documentation with required OAuth scopes User tokens enable reading DMs and private channels without requiring bot membership. The userTokenReadOnly flag (true by default) ensures the user token can only be used for reads, preventing accidental sends as the user. Required user token scopes: - 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
394 lines
14 KiB
Markdown
394 lines
14 KiB
Markdown
---
|
|
summary: "Slack socket mode setup and Clawdbot config"
|
|
read_when: "Setting up Slack or debugging Slack socket mode"
|
|
---
|
|
|
|
# Slack (socket mode)
|
|
|
|
## 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.channels.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.<id>.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).
|
|
|
|
## 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://api.channels.slack.com/docs/conversations-api for the overview.
|
|
|
|
### Bot token scopes (required)
|
|
- `chat:write` (send/update/delete messages via `chat.postMessage`)
|
|
https://api.channels.slack.com/methods/chat.postMessage
|
|
- `im:write` (open DMs via `conversations.open` for user DMs)
|
|
https://api.channels.slack.com/methods/conversations.open
|
|
- `channels:history`, `groups:history`, `im:history`, `mpim:history`
|
|
https://api.channels.slack.com/methods/conversations.history
|
|
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
|
|
https://api.channels.slack.com/methods/conversations.info
|
|
- `users:read` (user lookup)
|
|
https://api.channels.slack.com/methods/users.info
|
|
- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`)
|
|
https://api.channels.slack.com/methods/reactions.get
|
|
https://api.channels.slack.com/methods/reactions.add
|
|
- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`)
|
|
https://api.channels.slack.com/scopes/pins:read
|
|
https://api.channels.slack.com/scopes/pins:write
|
|
- `emoji:read` (`emoji.list`)
|
|
https://api.channels.slack.com/scopes/emoji:read
|
|
- `files:write` (uploads via `files.uploadV2`)
|
|
https://api.channels.slack.com/messaging/files/uploading
|
|
|
|
### 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://api.channels.slack.com/scopes/chat:write.public
|
|
- `users:read.email` (only if we need email fields from `users.info`)
|
|
https://api.channels.slack.com/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"]
|
|
},
|
|
"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).
|
|
- 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`).
|
|
|
|
### 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:<id>]]` — reply to a specific message id.
|
|
|
|
## Sessions + routing
|
|
- DMs share the `main` session (like WhatsApp/Telegram).
|
|
- Channels map to `agent:<agentId>:slack:channel:<channelId>` sessions.
|
|
- Slash commands use `agent:<agentId>:slack:slash:<userId>` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`).
|
|
- 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 <code>`.
|
|
- To allow anyone: set `channels.slack.dm.policy="open"` and `channels.slack.dm.allowFrom=["*"]`.
|
|
|
|
## Group policy
|
|
- `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).
|
|
- `allowlist` requires channels to be listed in `channels.slack.channels`.
|
|
|
|
Channel options (`channels.slack.channels.<id>` or `channels.slack.channels.<name>`):
|
|
- `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:<id>` for DMs
|
|
- `channel:<id>` 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.<id>.allowBots`.
|
|
- Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels.<id>.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels.<id>.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.
|