Skip to main content
The Slack adapter connects NanoClaw to a Slack app you create in your workspace. It’s built on the Chat SDK bridge (@chat-adapter/slack 4.26.0, pinned) and receives events over webhooks: Slack POSTs to /webhook/slack on NanoClaw’s shared webhook server. Unlike Discord or Telegram, that means Slack needs a public URL that reaches your machine. The bot works in public channels, private channels, DMs, and threads.

Prerequisites

  • A Slack workspace where you can install apps
  • A working NanoClaw install (quickstart)
  • A Slack app created From scratch at api.slack.com/apps with these Bot Token Scopes (under OAuth & Permissions): chat:write, im:write, im:history, channels:read, channels:history, groups:read, groups:history, users:read, reactions:write, files:read, files:write
  • App Home → enable the Messages Tab and check “Allow users to send slash commands and messages from the messages tab” — without this you can’t DM the bot
  • Two credentials to paste: the Bot User OAuth Token (xoxb-…, shown after Install to Workspace) and the Signing Secret (under Basic Information). No app-level token — v2 doesn’t use Socket Mode.
  • A way to expose port 3000 publicly — ngrok, Cloudflare Tunnel, or a reverse proxy on a VPS

Install

Slack is offered in the first-run setup wizard, or add it later by running /add-slack in Claude Code. The wizard flow:
1

Create the Slack app

The wizard opens api.slack.com/apps and walks you through the app creation checklist above: scopes, Messages Tab, signing secret, and workspace install.
2

Paste the bot token and signing secret

Both are password prompts. The wizard format-validates them (xoxb- prefix, hex secret), then calls auth.test to confirm Slack accepts the token and resolve your workspace and bot identity. If SLACK_BOT_TOKEN or SLACK_SIGNING_SECRET already exist in .env, it offers to reuse them.
3

Adapter install

setup/add-slack.sh copies the adapter from the channels branch, installs the pinned package, builds, writes SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET to .env (synced to data/env/env), and restarts the service.
4

Identify yourself

The wizard asks for your Slack member ID (U… — in Slack, click your profile picture → ProfileCopy member ID), then calls conversations.open to get a DM channel with you.
5

Name the agent and get the welcome DM

The wizard asks for your operator role and an agent name (default Nano), wires the DM to your first agent group, and sends a welcome message. The DM is delivered outbound via chat.postMessage, so it arrives even before webhooks are configured — but the bot can’t hear your replies yet.
6

Expose the webhook server and finish in Slack

Make port 3000 publicly reachable (ngrok, Cloudflare Tunnel, or a reverse proxy), then in your Slack app:
  • Event Subscriptions → enable, set the Request URL to https://<your-public-host>/webhook/slack (Slack sends a verification challenge that must pass), and subscribe to the bot events message.channels, message.groups, message.im, and app_mention
  • Interactivity & Shortcuts → enable, same Request URL
  • Reinstall the app when Slack prompts you to apply the new settings
To wire workspace channels or more DMs later, run /manage-channels. Channels are identified as slack:<channelId> (right-click the channel name → View channel details — the ID starts with C and sits at the bottom); DMs as slack:<dmId> (starts with D). Add the bot to a channel before wiring it.

Platform notes

  • Webhook delivery — the bridge registers Slack on the shared webhook server (port 3000, configurable via WEBHOOK_PORT), which routes /webhook/slack to the adapter. Requests are authenticated with your signing secret. The public URL must stay reachable — if your tunnel dies, the bot silently stops hearing messages.
  • Threads — the adapter sets supportsThreads: true, so in group channels the router forces per-thread sessions: each Slack thread gets its own agent session (unless the wiring uses agent-shared, which keeps one session across all of an agent’s messaging groups). DMs collapse sub-threads into one session. See the entity model.
  • Mentions and engagement — @mentioning the bot in an unwired channel reaches the router as a platform-confirmed mention (the app_mention event); in mention-sticky wirings the bot then sticks to that thread (plain mention wirings respond per mention without subscribing). DMs are always treated as addressed to the bot.
  • Interactive questions — when an agent asks a multiple-choice question, it renders as a card with buttons. Clicks arrive through the Interactivity request URL (same /webhook/slack route) and the card updates in place to show the selection. If buttons do nothing, Interactivity isn’t configured.
  • Attachments and reactions — file uploads are downloaded and passed to the agent as data (files:read), and the agent can react to messages (reactions:write).
  • Formatting — Slack uses mrkdwn, not standard Markdown (*bold* not **bold**, <url|text> links, no headings). The slack-formatting container skill is mounted into agent containers with a full mrkdwn reference.
  • No outbound chunking — the adapter doesn’t set the bridge’s maxTextLength, so long replies are posted as a single message. Slack’s message limit is high enough that this rarely matters.

Troubleshooting

  • “Slack didn’t accept that token”auth.test rejected it (invalid_auth or token_revoked). Copy the token again from OAuth & Permissions and retry setup. “Couldn’t reach Slack” instead means a network problem.
  • Welcome DM arrived but the bot never replies — outbound works without webhooks; inbound doesn’t. Check that your public URL is up, Event Subscriptions is enabled with a verified Request URL, and the bot events are subscribed. Slack’s URL verification fails if the signing secret in .env doesn’t match the app.
  • missing_scope when opening the DM — your app lacks im:write. Add it under OAuth & Permissions, reinstall the app to the workspace, then retry setup.
  • Webhook returns 404 Unknown adapter: slack — the adapter never registered, usually because SLACK_BOT_TOKEN is missing from the environment the service reads (the factory returns null without it). Verify .env and data/env/env, then restart the service.
For service-level checks (logs, restarts, wiring queries), see troubleshooting.
Last modified on June 10, 2026