Connecting OpenClaw to Slack (Socket Mode, two tokens, no exposed ports)
The app manifest, the bot token vs app-level token confusion, pairing for DMs, and the channel-ID gotcha that silently blocks messages.
OpenClaw connects to Slack as a Slack app in Socket Mode: create the app from a manifest at api.slack.com/apps, collect two tokens (the Bot User OAuth Token and an App-Level Token with connections:write), and enable channels.slack in config. Socket Mode connects outward, so nothing on your machine gets exposed — no public URL required.
Two tokens, one connection
Slack’s app model confuses first-timers because it wants two different credentials for one bot. The Bot User OAuth Token is the bot’s identity — what it can read and post, governed by its scopes. The App-Level Token (created with the connections:write scope) is what opens the Socket Mode connection itself. Create the app from a manifest so the scopes and event subscriptions arrive pre-declared, install it to your workspace, and copy both tokens. Then:
{
channels: {
slack: {
enabled: true,
mode: "socket",
botToken: { source: "env", provider: "default", id: "SLACK_BOT_TOKEN" },
appToken: { source: "env", provider: "default", id: "SLACK_APP_TOKEN" },
},
},
}There’s also an HTTP mode (signing secret + a public events URL) with feature parity — but it requires exposing an endpoint, which is exactly what a personal agent setup shouldn’t do casually. Socket Mode is the right default for the same reason the Gateway itself stays private: connections go outward.
DMs and channels behave differently — on purpose
Direct messages default to dmPolicy: "pairing": an unknown sender gets a code, and nothing happens until you approve it (openclaw pairing approve slack <code>). Channels default to mention-gated — the agent answers when @-mentioned in channels you’ve configured. Group DMs are off entirely until you enable them. For a bot living inside a workspace with other people, these defaults are the correct paranoia; loosen them deliberately, not because a tutorial told you to.
The gotcha that silently eats messages
Configure channels by Slack channel ID (C0123456789 — visible in the channel’s details), never by name. The lookup is ID-first, and a name-based key under groupPolicy: "allowlist" never matches — the result is a channel where every message is silently blocked and nothing tells you why. If your Slack agent “just doesn’t respond” in one channel, this is the first thing to check.
One more, for teams running serious setups
Slack may hold multiple Socket Mode connections for one app and deliver any given event to any of them. If you ever run two gateways sharing one Slack app — a test box and a real one, say — they’ll each get a random subset of messages. Use separate Slack apps per gateway. It’s an obscure failure mode until the day it isn’t.