OpenClawSkills
GitHub
Channels β€’ TutorialHeader.readTime

Slack

Slack configuration via Socket Mode or HTTP (Events API).

Tutorial.step

Socket Mode (Default)

#

Tutorial.step

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:

Json5
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
    },
  },
}

#

Tutorial.step

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>'.

#

Tutorial.step

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:

Json5
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
    },
  },
}

#

Tutorial.step

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):

Json5
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
      userToken: "xoxp-...",
    },
  },
}

Allow user token writes (use with caution):

Json5
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
      userToken: "xoxp-...",
      userTokenReadOnly: false,
    },
  },
}

##

Tutorial.step

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.

#

Tutorial.step

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).

Tutorial.step

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.

#

Tutorial.step

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>

#

Tutorial.step

OpenClaw Configuration (Minimal)

Json5
{
  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.

#

Tutorial.step

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.

Json
{
  "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.

Tutorial.step

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/

#

Tutorial.step

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

#

Tutorial.step

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>

Tutorial.step

Configuration Example

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,
      "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.

Tutorial.step

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>'.

Tutorial.step

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

Tutorial.step

Configure Threading by Chat Type

Use <code>channels.slack.replyToModeByChatType</code>:

Json5
{
  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>.

#

Tutorial.step

Manual Threading Tags

For finer-grained control, use in agent replies:

- <code>[[reply_to_current]]</code>

- '<code>'[[reply_to:'<id>']]'</code>'

Tutorial.step

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>'

Tutorial.step

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.

Tutorial.step

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.