Skip to content

Setting up Slack with OpenClaw

I know the feeling of trying to connect a new tool to a chat platform. It usually involves a lot of back-and-forth between dashboards and config files. If you want to get your bot talking in DMs and channels, I’ll show you how to do it without the usual headache.

I recommend using Socket Mode for most setups since it is the default and handles connections easily without needing a public URL.

  • A Slack app integration.
  • Access to your Slack app settings.

You can set this up using either Socket Mode or the HTTP Events API. I’ll cover the default Socket Mode first.

  1. Create Slack app and tokens: In your Slack app settings, enable Socket Mode. Create an App Token (xapp-...) with the connections:write scope. Install the app to your workspace and copy the Bot Token (xoxb-...).
  2. Configure OpenClaw: Update your configuration file.
{
channels: {
slack: {
enabled: true,
mode: "socket",
appToken: "xapp-...",
botToken: "xoxb-...",
},
},
}

If you prefer environment variables for your default account, use these:

Terminal window
SLACK_APP_TOKEN=xapp-...
SLACK_BOT_TOKEN=xoxb-...
  1. Subscribe to app events: You need to tell Slack which events to send to OpenClaw. Subscribe to these 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

    Make sure to enable the Messages Tab in the App Home settings so DMs work.

  2. Start the gateway: Run the command to get everything running.

Terminal window
openclaw gateway

If you prefer using webhooks, follow these steps:

  1. Configure Slack app for HTTP: Set the mode to HTTP in your config (channels.slack.mode="http"). Copy your Signing Secret from the Slack dashboard. Set the Request URL for Event Subscriptions, Interactivity, and Slash commands to your webhook path (the default is /slack/events).
  2. Update OpenClaw config:
{
channels: {
slack: {
enabled: true,
mode: "http",
botToken: "xoxb-...",
signingSecret: "your-signing-secret",
webhookPath: "/slack/events",
},
},
}

If you are running multiple accounts, give each one a unique webhookPath so the registrations do not collide.

If you run into issues with channels, I suggest checking the cross-channel diagnostics. Slack DMs use pairing mode by default, so if messages aren’t appearing, verify your App Home settings. For more complex issues, use the repair playbooks found in the troubleshooting guide.

If you hit a wall during the process, check the AI Setup Assistant for help.

I have spent plenty of time staring at configuration files, wondering why a bot isn’t responding or why a permission error keeps popping up. It is frustrating when you just want to get your integration running but the authentication layer feels like a wall. I want to walk you through how the token model works so you can get your setup right on the first try.

To get started, you will need to have these four pieces of information ready from your Slack App settings:

  1. botToken (The xoxb-... token)
  2. appToken (The xapp-... token for Socket Mode)
  3. signingSecret (For HTTP mode)
  4. userToken (Optional xoxp-... token)

The quickest way to get your bot online depends on how it communicates with Slack.

If you use Socket Mode, you must provide both the botToken and the appToken. If you prefer HTTP mode, you need the botToken and the signingSecret.

You can set these using environment variables for your default account. The system looks for SLACK_BOT_TOKEN and SLACK_APP_TOKEN. If you define these tokens directly in your configuration file, those values will override the environment variables.

User tokens (starting with xoxp-...) behave differently. They are config-only, so there is no environment variable fallback for them. By default, they operate with userTokenReadOnly: true.

I recommend using user tokens for reading directories or performing actions when they are available. For writing messages, the bot token is the primary choice. The system only uses a user token for writes if you set userTokenReadOnly: false and the bot token is missing.

Once your tokens are set, you need to decide who can actually talk to your bot.

You control Direct Message access through channels.slack.dm.policy. You have four options:

  • pairing (This is the default)
  • allowlist
  • open (This requires dm.allowFrom to include "*")
  • disabled

If you use the pairing policy, you can approve access by running: openclaw pairing approve slack <code>

You can also manage group DMs by setting dm.groupEnabled (which is false by default) or by defining specific allowlists in dm.groupChannels.

For public or private channels, use channels.slack.groupPolicy. You can set this to open, allowlist, or disabled.

If you leave the channels.slack section entirely out of your config and don’t set a default group policy, the runtime defaults to open and logs a warning to let you know. Your channel allowlist lives under channels.slack.channels.

In group settings, the bot is mention-gated by default. It looks for four specific triggers:

  1. Explicit app mentions like <@botId>
  2. Regex patterns defined in agents.list[].groupChat.mentionPatterns
  3. Fallback patterns in messages.groupChat.mentionPatterns
  4. Implicit replies within a thread the bot started

You can fine-tune individual channels using the channels.slack.channels.&lt;id|name&gt; configuration. This allows you to control requireMention, users allowlists, allowBots, and even specific skills or systemPrompt settings for that specific channel.

  • Unresolved Channel Entries: If you see allowlist entries that don’t seem to resolve, don’t worry. The system attempts to resolve names and IDs at startup based on your token access. If it can’t resolve them, it keeps them exactly as you configured them.
  • Defaulting to Open: If your bot is responding everywhere and you didn’t intend it to, check if your channels.slack config is missing. As mentioned, it falls back to open if the configuration is empty.

If you hit a wall that isn’t covered here, check the AI Setup Assistant for real-time help.

I have spent plenty of hours trying to make chat bots feel less like a basic script and more like a part of the team. One of the biggest hurdles is getting the bot to respond to specific commands without cluttering the entire channel or losing track of who said what. Slack has its own way of doing things, especially when it comes to slash commands and threading.

In this guide, I will show you how to set up native commands, manage those tricky thread sessions, and handle file uploads so your integration works exactly how you expect.

  • An active Slack app with permissions to add slash commands.
  • Your agent ID and configuration access.
  • A basic understanding of how Slack handles channel and user IDs.
  • Access to your media store for file handling.

If you want to get your Slack commands running in under 5 minutes, follow these steps.

  1. Enable Native Commands: By default, Slack native command auto-mode is off. You need to explicitly turn it on in your config:

    channels.slack.commands.native: true

    Alternatively, you can use the global commands.native: true.

  2. Register in Slack: Once enabled, go to your Slack App settings and register the matching slash commands (e.g., /<your-command>).

  3. Configure Slash Defaults: If you aren’t using native commands, you can run a single configured command via channels.slack.slashCommand. Here are the default settings:

    • enabled: false
    • name: "openclaw"
    • sessionPrefix: "slack:slash"
    • ephemeral: true
  4. Check Session Keys: Slash sessions use isolated keys to keep things clean. The format looks like this: agent:<agentId>:slack:slash:<userId>

Managing conversation flow is vital. I find that Slack’s different chat types (DMs, channels, and group chats) can be confusing if you don’t know how they route.

  • Routing: DMs route as direct, channels as channel, and MPIMs as group.
  • DMs: With the default session.dmScope=main, Slack DMs collapse into the agent’s main session.
  • Channels: These use a specific format: agent:<agentId>:slack:channel:<channelId>.

Thread replies can create suffixes like :thread:<threadTs>. By default, channels.slack.thread.historyScope is set to thread, and thread.inheritParent is false.

If you want to change how the bot replies, use channels.slack.replyToMode. You can set this to off, first, or all. If you need more granular control, use channels.slack.replyToModeByChatType for specific settings across direct, group, or channel chats.

I also recommend using manual reply tags when you need to be precise:

  • [[reply_to_current]]
  • [[reply_to:<id>]]

Handling files and long text blocks requires specific settings to avoid errors.

Slack file attachments are downloaded from private URLs using token-authenticated flows. The default inbound size limit is 20MB. You can change this using channels.slack.mediaMaxMb.

For sending text, the default chunk limit is 4000 characters. If you want your bot to split messages by paragraphs, set channels.slack.chunkMode="newline".

When you need to send a message to a specific place, use these explicit targets:

  • user:<id> for DMs
  • channel:<id> for channels

Slack actions are controlled by channels.slack.actions.*. Most of these are on by default, so you usually don’t have to touch them unless you want to disable something.

GroupDefault
messagesenabled
reactionsenabled
pinsenabled
memberInfoenabled
emojiListenabled

The system also maps Slack events into system events automatically. This includes message edits, deletes, thread broadcasts, and reaction changes. Even channel changes like joins, leaves, or renames are tracked. If you have configWrites enabled, channel_id_changed can even migrate your configuration keys for you.

Native commands aren’t working? Double-check your config. Setting commands.native: "auto" does not enable native commands for Slack. You must use channels.slack.commands.native: true.

Files are failing to upload? Check the file size. If it exceeds 20MB and you haven’t adjusted channels.slack.mediaMaxMb, the transfer will fail. Also, ensure your media store is correctly configured to receive the fetch.

Commands are executing in the wrong session? Remember that slash sessions use isolated keys (agent:<agentId>:slack:slash:<userId>), but they still route execution against the CommandTargetSessionKey.

That’s it for the Slack command and threading setup. If you run into specific issues with your configuration, check the AI Setup Assistant for real-time help.

I have spent way too much time clicking through the Slack developer dashboard trying to find the right checkbox for a specific permission. It is a common headache for developers when a bot fails to respond just because a single scope was missed during the initial setup. To make things easier, I have put together this checklist and manifest template so you can get your app running without the guesswork.

  1. A Slack App created in the Slack API dashboard.
  2. Access to the App Manifest section in your Slack settings.

Setting up your manifest takes about five minutes if you follow these steps:

  1. Go to your Slack App settings and select App Manifest.
  2. Choose the JSON tab to edit the configuration directly.
  3. Copy the manifest code provided below and paste it into the editor.
  4. Save your changes and reinstall the app to your workspace to apply the new scopes.
{
"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",
"mpim:history",
"users:read",
"app_mentions:read",
"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 are trying to perform specific read operations and find that the standard bot scopes are not enough, you might need to look at your user tokens.

Problem: You need advanced read operations If you configure channels.slack.userToken, you should verify that you have the correct typical read scopes assigned. I recommend checking for these:

  • 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 (required if you depend on Slack search reads)

If you have questions about specific permissions, you can ask the AI Setup Assistant.

I have spent many hours staring at a terminal, wondering why a bot isn’t responding to messages. It is frustrating when you think the setup is perfect, but the integration remains silent. I know the feeling of sending a message to a channel and getting absolutely nothing back.

Usually, the issue is a small configuration mismatch or a missing permission in the Slack dashboard. I will walk you through how to find exactly what is wrong so you can get back to building.

Before we start digging into the logs, make sure you have these four things ready:

  1. Access to your Slack App settings dashboard.
  2. Your local configuration file (specifically the Slack section).
  3. The OpenClaw CLI installed and authenticated.
  4. Your Bot and App tokens.

If you are in a hurry, I recommend running these two commands first to see the current state of your setup:

Terminal window
openclaw doctor
openclaw logs --follow

The doctor command checks for common configuration errors, while the logs will show you real-time events as they hit your server.

If your bot is sitting in a channel but ignoring everyone, I suggest checking these four settings in order:

  1. groupPolicy: Ensure this isn’t blocking the channel type.
  2. channels.slack.channels: Check your channel allowlist.
  3. requireMention: Verify if the bot expects a direct @mention to respond.
  4. users: Check the per-channel user allowlist.

You can also use this command to see if the bot can actually “see” the channels:

Terminal window
openclaw channels status --probe

When direct messages aren’t working, I usually look at the DM-specific policies. Check these four areas:

  1. channels.slack.dm.enabled: Is it set to true?
  2. channels.slack.dm.policy: Does the policy allow the current user?
  3. Pairing approvals: Check if the user needs to be paired first.
  4. Allowlist entries: Ensure the user is on the permitted list.

To see who is currently paired, run:

Terminal window
openclaw pairing list slack

If you are using Socket Mode and nothing is happening, the connection itself is likely failing. I recommend validating your bot and app tokens. You should also verify that Socket Mode is explicitly enabled in your Slack app settings.

For those running in HTTP mode, the issue is often the handshake between Slack and your server. Check these four items:

  1. Signing secret: This must match the one in your Slack dashboard.
  2. Webhook path: Ensure your server is listening on the right endpoint.
  3. Slack Request URLs: Check the Events, Interactivity, and Slash Commands sections in Slack.
  4. webhookPath: Ensure each HTTP account has a unique path.

If you type a command and nothing happens, I suggest checking which mode you intended to use. You might have native command mode on (channels.slack.commands.native: true) but forgot to register the commands in Slack. Alternatively, you might be trying to use single slash command mode (channels.slack.slashCommand.enabled: true).

I also recommend checking commands.useAccessGroups and your channel or user allowlists to ensure you have permission to run the command.

If you need to check specific fields, I recommend looking at these Slack-specific areas in your config:

  • Mode and Auth: mode, botToken, appToken, signingSecret, webhookPath, accounts.*
  • DM Access: dm.enabled, dm.policy, dm.allowFrom, dm.groupEnabled, dm.groupChannels
  • Channel Access: groupPolicy, channels.*, channels.*.users, channels.*.requireMention
  • Threading and History: replyToMode, replyToModeByChatType, thread.*, historyLimit, dmHistoryLimit
  • Delivery: textChunkLimit, chunkMode, mediaMaxMb
  • Ops and Features: configWrites, commands.native, slashCommand.*, actions.*, userToken

If you are still stuck, you can talk to the AI Setup Assistant for real-time help.

OpenClaw

OpenClaw Expert

Still stuck?

If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.