If you landed here you probably typed “OpenClaw Slack integration setup for team use” into a search bar and want the straight dope, not another marketing gloss. Here is the no-nonsense path I took to run our OpenClaw agent in Slack for the whole engineering org, including channel routing, threading, and an approval gate for anything that can break prod.

Why put OpenClaw inside Slack instead of keeping it as a personal bot?

OpenClaw already talks to WhatsApp, Telegram, and the web UI. Slack is different because:

  • It’s where teams collaborate and decisions are logged.
  • Message events are well-structured (timestamps, threads, files).
  • You get granular scopes for security reviews—critical when the agent can run shell commands.
  • The audit log API lets compliance folks sleep at night.

But a multi-user workspace also forces some design decisions: how do you keep per-user context, stop channel noise, and protect dangerous actions? The sections below cover exactly that.

Prerequisites and versions I actually used

  • OpenClaw daemon v0.24.3 (requires Node.js 22+).
  • Slack app platform v2 (a “manifest-based” Slack app).
  • Slack workspace on the paid plan (for audit logs & admin install). Free works, but you’ll miss the logs).
  • ngrok 3.5.0 for local HTTPS during development. In prod we front OpenClaw with Traefik + Let’s Encrypt.
  • A ClawCloud account if you’d rather avoid local infra. Everything below still applies.

Make sure your agent’s public URL is reachable by Slack; the Events API refuses plain HTTP.

Create the Slack app that fronts OpenClaw

1. Generate a basic manifest

Browse to api.slack.com/apps → “Create New App” → “From manifest”. Drop in this minimal YAML and tweak the domain name:

_display_information_: name: OpenClaw Bot description: Connects our OpenClaw agent to Slack features: bot_user: display_name: OpenClaw oauth_config: scopes: bot: - chat:write - channels:history - groups:history - im:history - mpim:history - commands - links:read - links:write settings: event_subscriptions: request_url: https://claw.yourdomain.com/slack/events bot_events: - message.channels - message.groups - message.im - message.mpim interactivity: is_enabled: true request_url: https://claw.yourdomain.com/slack/interactivity

Important bits:

  • chat:write so OpenClaw can answer.
  • commands lets us register /claw later.
  • Event request URLs must be HTTPS and public; Slack verifies them right away.

2. Install the app in a sandbox workspace first

Slack will hand you a Bot User OAuth Token that looks like xoxb-234.... Copy it; OpenClaw needs it in the next step.

3. Wire the token and signing secret into OpenClaw

Add a Slack provider block in agent.config.json (or via the UI in ClawCloud → Integrations → Slack):

"providers": { "slack": { "token": "xoxb-234...", "signingSecret": "8f1af...", "appLevelToken": "xapp-1-A123...", // optional but needed for Socket Mode fallback "verificationToken": "gone-in-v2" // leave blank; v2 uses signatures } }

Restart the daemon:

openclaw daemon restart

The logs should show: [slack] registered event handler on /slack/events.

Install the app for real and scope channels

When you push to the production workspace you’ll hit the first team decision: where should the bot live?

Channel vs DM model

  • Private DM (each user → @OpenClaw): clean, user-specific memory, but nobody sees answers.
  • #openclaw shared channel: transparent, encourages knowledge reuse, but can get noisy fast.
  • Mixed: let users DM for scratch work, then summon the bot in threads inside project channels (@OpenClaw build status?).

We ended up with the mixed model and enforced it via Slack’s allowlist channels feature so the bot only sees #openclaw plus whatever channel an admin invites it to.

From the workspace side run:

/invite @OpenClaw #openclaw

You can further limit history scopes with channels:history:read → “Only the channels the app is a member of”.

Routing messages: team commands vs personal chat

OpenClaw supports a router.js file in your agent root. The snippet below keeps personal DMs in a “user:id” memory namespace while routing channel threads to a “channel:id” namespace so context isn’t leaked:

module.exports = async ({ slackEvent }) => { if (!slackEvent) return 'default'; if (slackEvent.channel_type === 'im') { return `user:${slackEvent.user}`; // private brain per user } if (slackEvent.thread_ts) { return `thread:${slackEvent.thread_ts}`; // keep context inside a thread } return `channel:${slackEvent.channel}`; // fallback to channel-wide memory };

Drop this file, restart the daemon, and watch OpenClaw print which memory bucket it picked in DEBUG=claw:router mode.

Thread handling: stop spamming the channel

By default the agent responds in the same place it was pinged. In a busy channel that means main timeline noise. Two env vars shipped in v0.23.0 help:

  • SLACK_RESPOND_IN_THREAD=true – replies under the user’s original message.
  • SLACK_AUTO_THREAD_ROOTS=#openclaw,#support – messages posted in those channels get thread_ts set automatically even if the user didn’t start a thread.

We flipped both on. Result: a short “I’m thinking… ⏳” in the main channel, heavy multi-paragraph answer gets posted in the reply thread. Users thanked us three minutes later.

Setting up approval workflows for sensitive actions

The scary demo everyone saw on launch day was @OpenClaw deploy prod. Cute in a video, horrifying in a real workspace. Here’s how we made sure prod deploys require a human thumb-up.

1. Tag the dangerous commands

OpenClaw’s action schema supports requiresApproval. In actions.yaml:

- name: deploy description: Deploys the main branch parameters: environment: type: string enum: [staging, prod] requiresApproval: true

2. Choose an approval backend

  • Slack modal (fast, native, ships since OpenClaw v0.24.0).
  • GitHub PR comment (we use this for infra actions).

I picked the Slack modal. Add to agent.config.json:

"approvals": { "provider": "slack", "channel": "C05J9P6L2", // #ops-approvals channel ID "requiredRole": "admin" // Slack role name or user group }

Now when an action with requiresApproval hits the queue, OpenClaw DMs the approvers with Approve/Reject buttons. Only after someone clicks Approve does the task executor fire.

3. Test it end-to-end

@OpenClaw deploy prod

OpenClaw answers publicly: “Pending approval from #ops-approvals (timeout 30 min)”. In #ops-approvals, I see a message block:

  • Command: deploy prod
  • Requested by: @alice at 2024-05-01 13:03
  • [Approve] [Reject]

Click Approve, deployment webhook fires, output streams back to the original thread. No more rogue 2 AM prod deploys.

Operational tips: logging, rate limits, multi-workspace

Verbose logging

Slack payloads vanish once acknowledged. Capture them in dev:

DEBUG=claw:*,bolt:* openclaw daemon start

Turn that off in prod; Bolt can spit out 10 MB/min under high load.

Rate limits

Slack flags your bot at 50+ messages per minute. OpenClaw batches streamed responses by default but long answers still trip the limit. We added:

"slack": { "rateLimit": 40 }

in v0.24.3; set lower if you run multiple agents on the same token.

Multiple workspaces

You cannot reuse a single Slack app across workspaces if you want per-workspace approval channels and memory. Fork the manifest, generate a new token, and spin another agent instance (or use isolated ClawCloud projects). Costs us one extra vCPU but removes cross-tenant leakage risk.

What still felt rough

  • Slack modals don’t support streaming updates, so long-running approvals look frozen. We poll every 5 seconds and update the message, but it’s clunky.
  • No way to granularly scope im:history—Slack forces full IM access if you want DMs.
  • Enterprise Grid installation needs org-wide deploy; admin review cycles were slow. We documented a self-host fallback via Socket Mode but it adds latency.

If any of these bite you, jump into the #slack-integration channel on the OpenClaw Discord—several folks already patched custom workarounds.

Next step: automate onboarding with a slash command

Once the basic integration works, add a /claw slash command that DM-invites the user to the bot, seeds a starter prompt, and links our internal handbook. It cut “how do I start?” questions to zero.

To register:

/commands → New Command Command: /claw Request URL: https://claw.yourdomain.com/slack/commands/claw Usage hint: "ask <question> or /claw help"

Handle the POST in your OpenClaw routes/slash.js:

module.exports = async (req, res) => { const { user_id, text } = req.body; await openclaw.process({ provider: 'slack', user: user_id, text, channel: user_id, flags: { slash: true } }); res.send(); // empty 200 per Slack spec };

The router from earlier will drop the request into the user’s DM memory bucket automatically.

That’s it. You now have a Slack-native OpenClaw agent that understands team context, keeps threads tidy, and refuses to deploy without a second set of eyes. In practice the whole setup—from app creation to first approved deploy—took us about 90 minutes. If you hit an edge case not covered here, open an issue on GitHub; the maintainers are quick to respond.

Practical takeaway: Treat Slack as a shared shell. Give the bot the least privilege it needs, thread everything, and demand approvals for anything you wouldn’t let a junior engineer run without review.