> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openclaw.kr/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack

# 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}>
  <Card title="Pairing" icon="link" href="/channels/pairing">
    Slack DMs default to pairing mode.
  </Card>

  <Card title="Slash commands" icon="terminal" href="/tools/slash-commands">
    Native command behavior and command catalog.
  </Card>

  <Card title="Channel troubleshooting" icon="wrench" href="/channels/troubleshooting">
    Cross-channel diagnostics and repair playbooks.
  </Card>
</CardGroup>

## Quick setup

<Tabs>
  <Tab title="Socket Mode (default)">
    <Steps>
      <Step title="Create Slack app and tokens">
        In Slack app settings:

        * enable **Socket Mode**
        * create **App Token** (`xapp-...`) with `connections:write`
        * install app and copy **Bot Token** (`xoxb-...`)
      </Step>

      <Step title="Configure OpenClaw">
        ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
        {
          channels: {
            slack: {
              enabled: true,
              mode: "socket",
              appToken: "xapp-...",
              botToken: "xoxb-...",
            },
          },
        }
        ```

        Env fallback (default account only):

        ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
        SLACK_APP_TOKEN=xapp-...
        SLACK_BOT_TOKEN=xoxb-...
        ```
      </Step>

      <Step title="Subscribe app events">
        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.
      </Step>

      <Step title="Start gateway">
        ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
        openclaw gateway
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="HTTP Events API mode">
    <Steps>
      <Step title="Configure Slack app for HTTP">
        * 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`)
      </Step>

      <Step title="Configure OpenClaw HTTP mode">
        ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
        {
          channels: {
            slack: {
              enabled: true,
              mode: "http",
              botToken: "xoxb-...",
              signingSecret: "your-signing-secret",
              webhookPath: "/slack/events",
            },
          },
        }
        ```
      </Step>

      <Step title="Use unique webhook paths for multi-account HTTP">
        Per-account HTTP mode is supported.

        Give each account a distinct `webhookPath` so registrations do not collide.
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Token model

* `botToken` + `appToken` are required for Socket Mode.
* HTTP mode requires `botToken` + `signingSecret`.
* Config tokens override env fallback.
* `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` env fallback applies only to the default account.
* `userToken` (`xoxp-...`) is config-only (no env fallback) and defaults to read-only behavior (`userTokenReadOnly: true`).
* Optional: add `chat:write.customize` if you want outgoing messages to use the active agent identity (custom `username` and icon). `icon_emoji` uses `:emoji_name:` syntax.

<Tip>
  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.
</Tip>

## Access control and routing

<Tabs>
  <Tab title="DM policy">
    `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)

    Multi-account precedence:

    * `channels.slack.accounts.default.allowFrom` applies only to the `default` account.
    * Named accounts inherit `channels.slack.allowFrom` when their own `allowFrom` is unset.
    * Named accounts do not inherit `channels.slack.accounts.default.allowFrom`.

    Pairing in DMs uses `openclaw pairing approve slack <code>`.
  </Tab>

  <Tab title="Channel policy">
    `channels.slack.groupPolicy` controls channel handling:

    * `open`
    * `allowlist`
    * `disabled`

    Channel allowlist lives under `channels.slack.channels` and should use stable channel IDs.

    Runtime note: if `channels.slack` is completely missing (env-only setup), runtime falls back to `groupPolicy="allowlist"` and logs a warning (even if `channels.defaults.groupPolicy` is set).

    Name/ID resolution:

    * channel allowlist entries and DM allowlist entries are resolved at startup when token access allows
    * unresolved channel-name entries are kept as configured but ignored for routing by default
    * inbound authorization and channel routing are ID-first by default; direct username/slug matching requires `channels.slack.dangerouslyAllowNameMatching: true`
  </Tab>

  <Tab title="Mentions and channel users">
    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>`; names only via startup resolution or `dangerouslyAllowNameMatching`):

    * `requireMention`
    * `users` (allowlist)
    * `allowBots`
    * `skills`
    * `systemPrompt`
    * `tools`, `toolsBySender`
    * `toolsBySender` key format: `id:`, `e164:`, `username:`, `name:`, or `"*"` wildcard
      (legacy unprefixed keys still map to `id:` only)
  </Tab>
</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 (`/<command>` names), with one exception:
  * register `/agentstatus` for the status command (Slack reserves `/status`)
* 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.

## Interactive replies

Slack can render agent-authored interactive reply controls, but this feature is disabled by default.

Enable it globally:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  channels: {
    slack: {
      capabilities: {
        interactiveReplies: true,
      },
    },
  },
}
```

Or enable it for one Slack account only:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  channels: {
    slack: {
      accounts: {
        ops: {
          capabilities: {
            interactiveReplies: true,
          },
        },
      },
    },
  },
}
```

When enabled, agents can emit Slack-only reply directives:

* `[[slack_buttons: Approve:approve, Reject:reject]]`
* `[[slack_select: Choose a target | Canary:canary, Production:production]]`

These directives compile into Slack Block Kit and route clicks or selections back through the existing Slack interaction event path.

Notes:

* This is Slack-specific UI. Other channels do not translate Slack Block Kit directives into their own button systems.
* The interactive callback values are OpenClaw-generated opaque tokens, not raw agent-authored values.
* If generated interactive blocks would exceed Slack Block Kit limits, OpenClaw falls back to the original text reply instead of sending an invalid blocks payload.

Default slash command settings:

* `enabled: false`
* `name: "openclaw"`
* `sessionPrefix: "slack:slash"`
* `ephemeral: true`

Slash sessions use isolated keys:

* `agent:<agentId>:slack:slash:<userId>`

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:<agentId>:slack:channel:<channelId>`.
* Thread replies can create thread session suffixes (`:thread:<threadTs>`) 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:<id>]]`

Note: `replyToMode="off"` disables **all** reply threading in Slack, including explicit `[[reply_to_*]]` tags. This differs from Telegram, where explicit tags are still honored in `"off"` mode. The difference reflects the platform threading models: Slack threads hide messages from the channel, while Telegram replies remain visible in the main chat flow.

## Media, chunking, and delivery

<AccordionGroup>
  <Accordion title="Inbound attachments">
    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`.
  </Accordion>

  <Accordion title="Outbound text and files">
    * 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
  </Accordion>

  <Accordion title="Delivery targets">
    Preferred explicit targets:

    * `user:<id>` for DMs
    * `channel:<id>` 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 OpenClaw is processing an inbound message.

Resolution order:

* `channels.slack.accounts.<accountId>.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 the Slack account or globally.

## Typing reaction fallback

`typingReaction` adds a temporary reaction to the inbound Slack message while OpenClaw is processing a reply, then removes it when the run finishes. This is a useful fallback when Slack native assistant typing is unavailable, especially in DMs.

Resolution order:

* `channels.slack.accounts.<accountId>.typingReaction`
* `channels.slack.typingReaction`

Notes:

* Slack expects shortcodes (for example `"hourglass_flowing_sand"`).
* The reaction is best-effort and cleanup is attempted automatically after the reply or failure path completes.

## Manifest and scope checklist

<AccordionGroup>
  <Accordion title="Slack app manifest example">
    ```json theme={"theme":{"light":"min-light","dark":"min-dark"}}
    {
      "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",
            "im:history",
            "im:read",
            "im:write",
            "mpim:history",
            "mpim:read",
            "mpim:write",
            "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"
          ]
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Optional user-token scopes (read operations)">
    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)
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="No replies in channels">
    Check, in order:

    * `groupPolicy`
    * channel allowlist (`channels.slack.channels`)
    * `requireMention`
    * per-channel `users` allowlist

    Useful commands:

    ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
    openclaw channels status --probe
    openclaw logs --follow
    openclaw doctor
    ```
  </Accordion>

  <Accordion title="DM messages ignored">
    Check:

    * `channels.slack.dm.enabled`
    * `channels.slack.dmPolicy` (or legacy `channels.slack.dm.policy`)
    * pairing approvals / allowlist entries

    ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
    openclaw pairing list slack
    ```
  </Accordion>

  <Accordion title="Socket mode not connecting">
    Validate bot + app tokens and Socket Mode enablement in Slack app settings.
  </Accordion>

  <Accordion title="HTTP mode not receiving events">
    Validate:

    * signing secret
    * webhook path
    * Slack Request URLs (Events + Interactivity + Slash Commands)
    * unique `webhookPath` per HTTP account
  </Accordion>

  <Accordion title="Native/slash commands not firing">
    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.
  </Accordion>
</AccordionGroup>

## Text streaming

OpenClaw supports Slack native text streaming via the Agents and AI Apps API.

`channels.slack.streaming` controls live preview behavior:

* `off`: disable live preview streaming.
* `partial` (default): replace preview text with the latest partial output.
* `block`: append chunked preview updates.
* `progress`: show progress status text while generating, then send final text.

`channels.slack.nativeStreaming` controls Slack's native streaming API (`chat.startStream` / `chat.appendStream` / `chat.stopStream`) when `streaming` is `partial` (default: `true`).

Disable native Slack streaming (keep draft preview behavior):

```yaml theme={"theme":{"light":"min-light","dark":"min-dark"}}
channels:
  slack:
    streaming: partial
    nativeStreaming: false
```

Legacy keys:

* `channels.slack.streamMode` (`replace | status_final | append`) is auto-migrated to `channels.slack.streaming`.
* boolean `channels.slack.streaming` is auto-migrated to `channels.slack.nativeStreaming`.

### 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, OpenClaw 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`
  * compatibility toggle: `dangerouslyAllowNameMatching` (break-glass; keep off unless needed)
  * channel access: `groupPolicy`, `channels.*`, `channels.*.users`, `channels.*.requireMention`
  * threading/history: `replyToMode`, `replyToModeByChatType`, `thread.*`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit`
  * delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `streaming`, `nativeStreaming`
  * 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)
