Slack
Status: ✅ Supported (OpenClaw, Hermes)
Mode: Socket Mode (default) — outbound WebSocket, no public endpoint needed.
Slack channel allows your agent to operate as a bot in Slack workspaces, responding to mentions, DMs, and thread messages.
Token Model
Socket Mode requires two tokens:
| Token | Prefix | Secret Name | Purpose |
|---|---|---|---|
| Bot Token | xoxb- | SLACK_BOT_TOKEN | Authenticates bot actions (send messages, read channels) |
| App Token | xapp- | SLACK_APP_TOKEN | Authenticates the WebSocket connection to Slack |
Both tokens use SecretRef objects in config and fall back to environment variables.
Env Fallback
For the default account, tokens resolve from environment if not in config:
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
⚠️ Critical Prerequisites for DMs
Before creating your Slack app, understand that these permissions are non-negotiable for direct messages to work:
Required OAuth Scopes
Without these, you cannot DM the bot:
- ✅
im:history- Bot MUST be able to read DM history - ✅
im:read- Bot MUST be able to access DM metadata - ✅
im:write- Bot MUST be able to write to DMs (⚠️ see security note below — applies to both OpenClaw and Hermes) - ✅
chat:write- Bot MUST be able to send messages
⚠️ Security note on
im:write(applies to OpenClaw and Hermes): This scope lets the bot initiate unsolicited DMs to any workspace member — neither OpenClaw'sallowFromnor Hermes'SLACK_ALLOWED_USERSgate outbound messages. A compromised agent host or leakedxoxb-token can DM-spam or phish the entire workspace. Mitigations: keep the host hardened; treat the bot token as a high-value secret; rotate the token immediately on suspicion (Slack API → OAuth & Permissions → Rotate Tokens → update the channel record (clawctl channel registry edit <channel-name> --token-stdin <<<"$NEW_BOT_TOKEN"thenclawctl channel registry edit <channel-name> --app-token "$NEW_APP_TOKEN") andclawctl agent sync <name>); auditusers:readcalls in your workspace's Slack audit log if you suspect abuse.
Required Event Subscriptions
Without these, the bot will not receive your DMs:
- ✅
message.im- Fires when you send a DM to the bot (CRITICAL) - ✅
app_mention- Fires when you @-mention the bot in channels
What Happens Without These?
- Missing
im:*scopes: Slack UI won't let you message the bot (Message button disabled/missing in Slack app directory) - Missing
message.imevent: Bot receives no notification when you DM it (messages sent but bot never sees them) - Missing
chat:writescope: Bot can receive messages but cannot respond
Verification After Setup:
- Go to https://api.slack.com/apps → Select your app
- Navigate to Event Subscriptions → Subscribe to bot events
- Confirm
message.imis listed - If not, add it and reinstall the app to your workspace (required for changes to take effect)
Setup
Step 1: Create a Slack App
- Go to https://api.slack.com/apps/new
- Choose From a manifest
- Select your workspace
- Paste this manifest (includes all required scopes and events for DM support):
{
"display_information": {
"name": "OpenClaw",
"description": "Slack connector for OpenClaw"
},
"features": {
"bot_user": {
"display_name": "OpenClaw",
"always_online": true
},
"app_home": {
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
}
},
"oauth_config": {
"scopes": {
"bot": [
"app_mentions:read",
"channels:history",
"channels:read",
"chat:write",
"im:history",
"im:read",
"im:write",
"mpim:history",
"mpim:read",
"mpim:write",
"reactions:read",
"reactions:write",
"users:read",
"pins:read"
]
}
},
"settings": {
"socket_mode_enabled": true,
"event_subscriptions": {
"bot_events": [
"app_mention",
"message.channels",
"message.im",
"message.mpim",
"reaction_added",
"member_joined_channel"
]
}
}
}
Note: The manifest includes
im:history,im:read,im:write(DM scopes) andmessage.im(DM event) which are required for direct messaging. Without these, users cannot send DMs to your bot.
- Click Create
Step 2: Generate App-Level Token
This is the xapp- token required for Socket Mode.
- Go to Basic Information (sidebar) > App-Level Tokens
- Click Generate Token and Scopes
- Name it:
socket-mode - Add scope:
connections:write - Click Generate
- Copy the token — it starts with
xapp- - Save this — you'll enter it as
SLACK_APP_TOKENduring configuration
Step 3: Verify Event Subscriptions (Critical for DMs)
Before installing, verify the event subscriptions are correct:
- Go to Event Subscriptions (sidebar)
- Confirm Enable Events is ON
- Scroll to Subscribe to bot events
- Verify
message.imis in the list (required for DMs) - If
message.imis missing:- Click Add Bot User Event
- Select
message.im - Click Save Changes
Why this matters: Without
message.im, your bot will never receive direct messages. This is the #1 reason DMs don't work.
Step 4: Install App to Workspace
- Go to OAuth & Permissions (sidebar)
- Click Install to Workspace
- Authorize the app
- Copy the Bot User OAuth Token — it starts with
xoxb- - Save this — you'll enter it as
SLACK_BOT_TOKENduring configuration
Step 5: Invite Bot to a Channel
In Slack, go to the channel where you want the bot and type:
@OpenClaw
Slack will prompt you to invite the bot to the channel.
Step 6: Get Your User ID
- In Slack, click your profile picture (top right)
- Click ⋯ (three dots) > Copy Member ID
- The ID starts with
U(e.g.,U01ABC2DEF) - This goes in the
allowFromlist during configuration
Step 7: Register and attach in Clawrium
# 1. Register the channel in the registry
clawctl channel registry create <channel-name> --type slack \
--token-stdin <<<"$SLACK_BOT_TOKEN" \
--app-token "$SLACK_APP_TOKEN" \
--allowed-user U01ABC2DEF3 \
--home-channel C01234567890
# 2. Attach to the agent and sync
clawctl agent channel attach <channel-name> --agent <agent-name>
clawctl agent sync <agent-name>
Configuration Structure
After setup, the Slack config in your agent's openclaw.json looks like:
{
"channels": {
"slack": {
"enabled": true,
"mode": "socket",
"botToken": {
"source": "env",
"provider": "default",
"id": "SLACK_BOT_TOKEN"
},
"appToken": {
"source": "env",
"provider": "default",
"id": "SLACK_APP_TOKEN"
},
"allowFrom": ["U01ABC2DEF"],
"groupPolicy": "allowlist",
"dmPolicy": "pairing"
}
}
}
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Enable Slack channel |
mode | string | "socket" | Connection mode (Socket Mode) |
botToken | SecretRef | — | Bot token reference (xoxb-...) |
appToken | SecretRef | — | App-level token reference (xapp-...) |
allowFrom | string[] | [] | User IDs allowed to interact with the bot |
groupPolicy | string | "allowlist" | Group access policy: open, allowlist, disabled |
dmPolicy | string | "pairing" | DM access policy: pairing, allowlist, open, disabled |
SecretRef Object
Tokens use SecretRef objects instead of plaintext values. The secret value is stored in Clawrium's encrypted secrets storage, not in config files.
{
"source": "env",
"provider": "default",
"id": "SLACK_BOT_TOKEN"
}
At runtime, OpenClaw resolves the token from the environment file written by Clawrium.
Hermes Configuration
Hermes uses a simpler configuration model — env vars rendered directly into ~/.hermes/.env. There are no SecretRef objects; tokens are written as plain values in the env file (mode 0600 on the agent host).
Env vars rendered by clawctl
| Variable | Required | Description |
|---|---|---|
SLACK_BOT_TOKEN | yes | Bot User OAuth Token (xoxb-...) |
SLACK_APP_TOKEN | yes | App-Level Token for Socket Mode (xapp-...) |
SLACK_ALLOWED_USERS | yes | Comma-separated Slack Member IDs (e.g., U01ABC2DEF3,U04XYZ7GHI8) |
SLACK_HOME_CHANNEL | no | Channel ID for cron/scheduled messages (e.g., C01234567890) |
SLACK_HOME_CHANNEL_NAME | no | Display name for the home channel |
Required scopes (minimal set for Hermes)
⚠️ Critical for DMs: The three
im:*scopes below are required for direct messaging. Without them, users cannot DM the bot in Slack.
| Scope | Required For | What Breaks Without It |
|---|---|---|
app_mentions:read | Channels | Bot won't see @-mentions in channels |
chat:write | All | Bot cannot send any messages (DMs or channels) |
channels:read | Channels | Bot cannot list/join public channels |
groups:read | Private Channels | Bot cannot list private channels it's in |
im:history | DMs | Bot cannot read DM history (DMs fail) |
im:read | DMs | Bot cannot access DM metadata (DMs fail) |
im:write | DMs | Slack won't allow users to message the bot |
users:read | All | Bot cannot look up user info for allowlist checks |
⚠️ Security note on
im:write: This scope also permits the bot to initiate unsolicited DMs to any workspace member — theSLACK_ALLOWED_USERSallowlist only gates inbound commands, not outbound messages. A compromised agent host or leaked bot token could DM-spam or phish the entire workspace. Keep the agent host hardened and thexoxb-bot token insecrets.jsononly. On suspicion of leakage: rotate via Slack API → OAuth & Permissions → Rotate Tokens, then update the channel record (clawctl channel registry edit <channel-name> --token-stdin <<<"$NEW_BOT_TOKEN"thenclawctl channel registry edit <channel-name> --app-token "$NEW_APP_TOKEN") andclawctl agent sync <name>.
Required event subscriptions
⚠️ Critical for DMs: Without
message.im, the bot will never receive your direct messages even if you can send them.
| Event | Required For | What Breaks Without It |
|---|---|---|
app_mention | Channels | Bot won't respond to @-mentions |
message.im | DMs | Bot receives no notification when you DM it |
Access control differences from OpenClaw
| Aspect | OpenClaw | Hermes |
|---|---|---|
| Config location | openclaw.json (SecretRef objects) | ~/.hermes/.env (plain env vars) |
| DM policy | Configurable (pairing, allowlist, open, disabled) | SLACK_ALLOWED_USERS allowlist only |
| Group policy | Configurable (open, allowlist, disabled) | Channel membership controls access (invite-only) |
| Token storage | secrets.json → rendered to env at runtime | secrets.json → rendered to .env at configure time |
Hermes uses Socket Mode — the bot maintains an outbound WebSocket to Slack, so no public endpoint or ingress is required on the agent host.
Setup (Hermes)
# 1. Register the channel in the registry
clawctl channel registry create <channel-name> --type slack \
--token-stdin <<<"$SLACK_BOT_TOKEN" \
--app-token "$SLACK_APP_TOKEN" \
--allowed-user U01ABC2DEF3 \
--home-channel C01234567890
# 2. Attach and sync
clawctl agent channel attach <channel-name> --agent <hermes-name>
clawctl agent sync <hermes-name>
Flags accepted by clawctl channel registry create --type slack:
| Flag | Required | Notes |
|---|---|---|
--token / --token-stdin | yes | Bot User OAuth Token. Stored in secrets.json under channel:<channel-name>:SLACK_BOT_TOKEN. Must start with xoxb-. |
--app-token | yes | App-Level Token for Socket Mode. Stored in secrets.json under channel:<channel-name>:SLACK_APP_TOKEN. Must start with xapp-. |
--allowed-user <id> (repeatable) | yes | Slack Member IDs (format: U + 8+ alphanumeric chars). |
--home-channel <id> | optional | Channel ID for cron/scheduled messages. Format: C + alphanumeric. |
clawctl agent sync re-renders ~/.hermes/.env with the SLACK_* block from the attached channel's registry record and restarts hermes-<name>.service.
Resulting on-disk shape (Hermes)
channels.json (non-sensitive — one record per chat surface):
{
"<channel-name>": {
"name": "<channel-name>",
"type": "slack",
"config": {
"allowed_users": ["U01ABC2DEF3"],
"home_channel": "C01234567890"
}
}
}
hosts.json carries the attachment list under agents.<name>.channels[].
secrets.json:
"channel:<channel-name>": {
"SLACK_BOT_TOKEN": {"value": "xoxb-...", "description": "Slack bot token"},
"SLACK_APP_TOKEN": {"value": "xapp-...", "description": "Slack app token"}
}
Both tokens never land in channels.json or hosts.json — clawctl channel registry create stores them exclusively in secrets.json (B3 invariant). Re-creating the channel record with the same tokens reuses them byte-identical.
Rendered .env (Slack block, Hermes)
After configure, the relevant section of ~/.hermes/.env on the agent host looks like:
# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
SLACK_ALLOWED_USERS=U01ABC2DEF3
SLACK_HOME_CHANNEL=C01234567890
SLACK_HOME_CHANNEL_NAME=general
Removal (Hermes)
clawctl agent channel detach <channel-name> --agent <hermes-name> then clawctl agent sync <hermes-name> drops the SLACK_* block from ~/.hermes/.env. To wipe the channel record and its tokens entirely: clawctl channel registry delete <channel-name> --yes --force. To rotate tokens without dropping the attachment: clawctl channel registry edit <channel-name> --token-stdin <<<"$NEW_BOT_TOKEN" and clawctl agent sync <hermes-name>.
Hermes-specific troubleshooting
Bot connects but gets missing_scope errors
Hermes logs will show errors like slack_bolt: missing_scope: channels:read. Go to your Slack app's OAuth & Permissions > Scopes and add the missing scope. Then reinstall the app to your workspace (Slack requires reinstall after scope changes). You do NOT need to re-create the channel record — the tokens remain valid after reinstall.
Bot gets not_in_channel error for home channel
The bot must be a member of the home channel. In Slack, go to that channel and type /invite @Hermes. The SLACK_HOME_CHANNEL setting only tells hermes where to post scheduled/cron messages — it doesn't auto-join.
❌ CRITICAL: Bot doesn't respond to DMs
This is the most common issue. Follow these steps in order:
1. Can you even send a DM to the bot?
- In Slack, go to Apps in the sidebar
- Find your bot
- Try to click Message
If you can't click Message or it's grayed out:
→ Your Slack app is missing the im:write scope. Go to Slack API → OAuth & Permissions → Add im:write → Reinstall app to workspace.
If you can send a DM but bot doesn't respond: → Continue to step 2.
2. Verify Event Subscription
- Go to https://api.slack.com/apps
- Select your app
- Go to Event Subscriptions
- Scroll to Subscribe to bot events
- Confirm
message.imis in the list
If message.im is missing:
→ Click Add Bot User Event → Select message.im → Save Changes → Reinstall app to workspace (required!)
3. Verify OAuth Scopes
Go to OAuth & Permissions → Scopes → Bot Token Scopes and confirm:
- ✅
im:history - ✅
im:read - ✅
im:write - ✅
chat:write
If any are missing: Add them → Reinstall app to workspace.
4. Verify User Allowlist (Hermes only)
Your Slack Member ID must be in the channel record's allowed_users list:
clawctl channel registry describe <channel-name>
Find your Member ID: Slack profile → ⋯ (three dots) → Copy Member ID
If your ID is missing: Re-create the channel record (clawctl channel registry delete <channel-name> --yes --force then clawctl channel registry create <channel-name> --type slack ... --allowed-user <your-id>), re-attach if needed, and clawctl agent sync <name>.
5. Check Agent Logs
ssh <agent-host> "sudo journalctl -u hermes-<name> -f"
Send a test DM and watch for errors. Common errors:
missing_scope: im:history→ Add scope and reinstall appnot_authed→ Token expired, regenerate and reconfigure- Silent (no logs) → Event subscription
message.imis missing
Bot doesn't respond in channels
- The bot only listens for
app_mentionevents in channels — you must @-mention it. - Confirm the bot has been invited to the channel (
/invite @Hermes). - Verify
app_mentions:readandchannels:readscopes are enabled.
Socket Mode not connecting
- Verify Socket Mode is enabled in the Slack app settings.
- Confirm
SLACK_APP_TOKENstarts withxapp-and has theconnections:writescope. - Check the journal:
ssh <agent-host> "sudo journalctl -u hermes-<name>.service -n 200 --no-pager | grep -iE 'slack|socket'".
Access Control
DM Policy (dmPolicy)
| Policy | Behavior |
|---|---|
pairing (default) | New DM users must approve via clawctl pairing approve slack <code> |
allowlist | Only users in allowFrom can DM |
open | Anyone can DM (requires allowFrom: ["*"]) |
disabled | No DMs allowed |
Group Policy (groupPolicy)
| Policy | Behavior |
|---|---|
allowlist (default) | Bot only responds in explicitly allowed channels |
open | Bot responds in all channels it's invited to |
disabled | No channel responses |
Troubleshooting
Bot not responding in channels
- Verify
groupPolicyis notdisabled - Check bot is invited to the channel (
@OpenClawin channel) - Verify
app_mentions:readandchannels:historyscopes are enabled
Bot not responding to DMs
- Check
dmPolicy— default ispairing, new users must be approved first - Run
clawctl pairing list slackto see pending approvals
Socket Mode not connecting
- Verify both
SLACK_BOT_TOKENandSLACK_APP_TOKENare set - Check that Socket Mode is enabled in Slack app settings
- Verify the App-Level Token has
connections:writescope
"Invalid bot token format" error
- Bot token must start with
xoxb-and contain only alphanumeric characters and hyphens - Copy the full token from OAuth & Permissions in Slack app settings
"Invalid app token format" error
- App token must start with
xapp-1-followed by alphanumeric and hex characters - Copy the full token from Basic Information > App-Level Tokens