coderClaw

Slack

Status: production-ready for DMs + channels via Slack app integrations. Default mode is Socket Mode; HTTP Events API mode is also supported.

<CardGroup cols={3}>

Slack DMs default to pairing mode. Native command behavior and command catalog. Cross-channel diagnostics and repair playbooks.

</CardGroup>

Quick setup

In Slack app settings: - enable **Socket Mode** - create **App Token** (`xapp-...`) with `connections:write` - install app and copy **Bot Token** (`xoxb-...`) ```json5 { channels: { slack: { enabled: true, mode: "socket", appToken: "xapp-...", botToken: "xoxb-...", }, }, } ``` Env fallback (default account only): ```bash SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... ``` Subscribe bot events for: - `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` Also enable App Home **Messages Tab** for DMs. ```bash coderclaw gateway ``` - set mode to HTTP (`channels.slack.mode="http"`) - copy Slack **Signing Secret** - set Event Subscriptions + Interactivity + Slash command Request URL to the same webhook path (default `/slack/events`) ```json5 { channels: { slack: { enabled: true, mode: "http", botToken: "xoxb-...", signingSecret: "your-signing-secret", webhookPath: "/slack/events", }, }, } ``` Per-account HTTP mode is supported. Give each account a distinct `webhookPath` so registrations do not collide.

Token model

For actions/directory reads, user token can be preferred when configured. For writes, bot token remains preferred; user-token writes are only allowed when `userTokenReadOnly: false` and bot token is unavailable.

Access control and routing

`channels.slack.dmPolicy` controls DM access (legacy: `channels.slack.dm.policy`): - `pairing` (default) - `allowlist` - `open` (requires `channels.slack.allowFrom` to include `"*"`; legacy: `channels.slack.dm.allowFrom`) - `disabled` DM flags: - `dm.enabled` (default true) - `channels.slack.allowFrom` (preferred) - `dm.allowFrom` (legacy) - `dm.groupEnabled` (group DMs default false) - `dm.groupChannels` (optional MPIM allowlist) Pairing in DMs uses `coderclaw pairing approve slack `. </Tab> `channels.slack.groupPolicy` controls channel handling: - `open` - `allowlist` - `disabled` Channel allowlist lives under `channels.slack.channels`. Runtime note: if `channels.slack` is completely missing (env-only setup) and `channels.defaults.groupPolicy` is unset, runtime falls back to `groupPolicy="open"` and logs a warning. Name/ID resolution: - channel allowlist entries and DM allowlist entries are resolved at startup when token access allows - unresolved entries are kept as configured Channel messages are mention-gated by default. Mention sources: - explicit app mention (`<@botId>`) - mention regex patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`) - implicit reply-to-bot thread behavior Per-channel controls (`channels.slack.channels.<id|name>`): - `requireMention` - `users` (allowlist) - `allowBots` - `skills` - `systemPrompt` - `tools`, `toolsBySender` </Tabs> ## Commands and slash behavior - Native command auto-mode is **off** for Slack (`commands.native: "auto"` does not enable Slack native commands). - Enable native Slack command handlers with `channels.slack.commands.native: true` (or global `commands.native: true`). - When native commands are enabled, register matching slash commands in Slack (`/` names). - If native commands are not enabled, you can run a single configured slash command via `channels.slack.slashCommand`. - Native arg menus now adapt their rendering strategy: - up to 5 options: button blocks - 6-100 options: static select menu - more than 100 options: external select with async option filtering when interactivity options handlers are available - if encoded option values exceed Slack limits, the flow falls back to buttons - For long option payloads, Slash command argument menus use a confirm dialog before dispatching a selected value. Default slash command settings: - `enabled: false` - `name: "coderclaw"` - `sessionPrefix: "slack:slash"` - `ephemeral: true` Slash sessions use isolated keys: - `agent::slack:slash:` and still route command execution against the target conversation session (`CommandTargetSessionKey`). ## Threading, sessions, and reply tags - DMs route as `direct`; channels as `channel`; MPIMs as `group`. - With default `session.dmScope=main`, Slack DMs collapse to agent main session. - Channel sessions: `agent::slack:channel:`. - Thread replies can create thread session suffixes (`:thread:`) when applicable. - `channels.slack.thread.historyScope` default is `thread`; `thread.inheritParent` default is `false`. - `channels.slack.thread.initialHistoryLimit` controls how many existing thread messages are fetched when a new thread session starts (default `20`; set `0` to disable). Reply threading controls: - `channels.slack.replyToMode`: `off|first|all` (default `off`) - `channels.slack.replyToModeByChatType`: per `direct|group|channel` - legacy fallback for direct chats: `channels.slack.dm.replyToMode` Manual reply tags are supported: - `[[reply_to_current]]` - `[[reply_to:]]` Note: `replyToMode="off"` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored. ## Media, chunking, and delivery Slack file attachments are downloaded from Slack-hosted private URLs (token-authenticated request flow) and written to the media store when fetch succeeds and size limits permit. Runtime inbound size cap defaults to `20MB` unless overridden by `channels.slack.mediaMaxMb`. - text chunks use `channels.slack.textChunkLimit` (default 4000) - `channels.slack.chunkMode="newline"` enables paragraph-first splitting - file sends use Slack upload APIs and can include thread replies (`thread_ts`) - outbound media cap follows `channels.slack.mediaMaxMb` when configured; otherwise channel sends use MIME-kind defaults from media pipeline Preferred explicit targets: - `user:` for DMs - `channel:` for channels Slack DMs are opened via Slack conversation APIs when sending to user targets. </Accordion> </AccordionGroup> ## Actions and gates Slack actions are controlled by `channels.slack.actions.*`. Available action groups in current Slack tooling: | Group | Default | | ---------- | ------- | | messages | enabled | | reactions | enabled | | pins | enabled | | memberInfo | enabled | | emojiList | enabled | ## Events and operational behavior - Message edits/deletes/thread broadcasts are mapped into system events. - Reaction add/remove events are mapped into system events. - Member join/leave, channel created/renamed, and pin add/remove events are mapped into system events. - Assistant thread status updates (for "is typing..." indicators in threads) use `assistant.threads.setStatus` and require bot scope `assistant:write`. - `channel_id_changed` can migrate channel config keys when `configWrites` is enabled. - Channel topic/purpose metadata is treated as untrusted context and can be injected into routing context. - Block actions and modal interactions emit structured `Slack interaction: ...` system events with rich payload fields: - block actions: selected values, labels, picker values, and `workflow_*` metadata - modal `view_submission` and `view_closed` events with routed channel metadata and form inputs ## Ack reactions `ackReaction` sends an acknowledgement emoji while CoderClaw is processing an inbound message. Resolution order: - `channels.slack.accounts..ackReaction` - `channels.slack.ackReaction` - `messages.ackReaction` - agent identity emoji fallback (`agents.list[].identity.emoji`, else "👀") Notes: - Slack expects shortcodes (for example `"eyes"`). - Use `""` to disable the reaction for a channel or account. ## Manifest and scope checklist ```json { "display_information": { "name": "CoderClaw", "description": "Slack connector for CoderClaw" }, "features": { "bot_user": { "display_name": "CoderClaw", "always_online": false }, "app_home": { "messages_tab_enabled": true, "messages_tab_read_only_enabled": false }, "slash_commands": [ { "command": "/coderclaw", "description": "Send a message to CoderClaw", "should_escape": false } ] }, "oauth_config": { "scopes": { "bot": [ "chat:write", "channels:history", "channels:read", "groups:history", "im:history", "mpim:history", "users:read", "app_mentions:read", "assistant:write", "reactions:read", "reactions:write", "pins:read", "pins:write", "emoji:read", "commands", "files:read", "files:write" ] } }, "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 configure `channels.slack.userToken`, typical read scopes are: - `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` (if you depend on Slack search reads) ## Troubleshooting Check, in order: - `groupPolicy` - channel allowlist (`channels.slack.channels`) - `requireMention` - per-channel `users` allowlist Useful commands: ```bash coderclaw channels status --probe coderclaw logs --follow coderclaw doctor ``` Check: - `channels.slack.dm.enabled` - `channels.slack.dmPolicy` (or legacy `channels.slack.dm.policy`) - pairing approvals / allowlist entries ```bash coderclaw pairing list slack ``` Validate bot + app tokens and Socket Mode enablement in Slack app settings. Validate: - signing secret - webhook path - Slack Request URLs (Events + Interactivity + Slash Commands) - unique `webhookPath` per HTTP account Verify whether you intended: - native command mode (`channels.slack.commands.native: true`) with matching slash commands registered in Slack - or single slash command mode (`channels.slack.slashCommand.enabled: true`) Also check `commands.useAccessGroups` and channel/user allowlists. ## Text streaming CoderClaw supports Slack native text streaming via the Agents and AI Apps API. By default, streaming is enabled. Disable it per account: ```yaml channels: slack: streaming: false ``` ### Requirements 1. Enable **Agents and AI Apps** in your Slack app settings. 2. Ensure the app has the `assistant:write` scope. 3. A reply thread must be available for that message. Thread selection still follows `replyToMode`. ### Behavior - First text chunk starts a stream (`chat.startStream`). - Later text chunks append to the same stream (`chat.appendStream`). - End of reply finalizes stream (`chat.stopStream`). - Media and non-text payloads fall back to normal delivery. - If streaming fails mid-reply, CoderClaw falls back to normal delivery for remaining payloads. ## Configuration reference pointers Primary reference: - [Configuration reference - Slack](/gateway/configuration-reference#slack) High-signal Slack fields: - mode/auth: `mode`, `botToken`, `appToken`, `signingSecret`, `webhookPath`, `accounts.*` - DM access: `dm.enabled`, `dmPolicy`, `allowFrom` (legacy: `dm.policy`, `dm.allowFrom`), `dm.groupEnabled`, `dm.groupChannels` - channel access: `groupPolicy`, `channels.*`, `channels.*.users`, `channels.*.requireMention` - threading/history: `replyToMode`, `replyToModeByChatType`, `thread.*`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit` - delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb` - ops/features: `configWrites`, `commands.native`, `slashCommand.*`, `actions.*`, `userToken`, `userTokenReadOnly` ## Related - [Pairing](/channels/pairing) - [Channel routing](/channels/channel-routing) - [Troubleshooting](/channels/troubleshooting) - [Configuration](/gateway/configuration) - [Slash commands](/tools/slash-commands)