> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nanoclaw.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack

> Connect NanoClaw to a Slack workspace via the Chat SDK adapter — app creation, OAuth scopes, webhook URL, and thread sessions.

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.29.0, pinned) and supports two delivery modes:

* **Socket Mode** (the setup default) — the bot opens an outbound WebSocket to Slack, so events arrive over that connection and NanoClaw needs **no public URL**. Ideal for local dev or a host behind NAT. Setting `SLACK_APP_TOKEN` (`xapp-…`) is what flips the adapter into this mode.
* **Webhook mode** — Slack POSTs to `/webhook/slack` on NanoClaw's shared webhook server, which needs a **public HTTPS URL** that reaches your machine. The adapter uses this mode whenever `SLACK_APP_TOKEN` is unset.

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](/quickstart))
* A Slack app created **From scratch** at [api.slack.com/apps](https://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
* The **Bot User OAuth Token** (`xoxb-…`, shown after **Install to Workspace**), plus the credential for your chosen mode: an **App-Level Token** (`xapp-…` with the `connections:write` scope, under **Basic Information** → **App-Level Tokens**) for Socket Mode, or the **Signing Secret** (under **Basic Information**) for webhook mode. In Socket Mode the signing secret is optional — Slack signs socket frames separately.
* **Webhook mode only:** a way to expose port 3000 publicly — ngrok, Cloudflare Tunnel, or a reverse proxy on a VPS. Socket Mode needs none of this.

## Install

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

<Steps>
  <Step title="Create the Slack app">
    The wizard opens [api.slack.com/apps](https://api.slack.com/apps) and walks you through the app creation checklist above: scopes, Messages Tab, signing secret, and workspace install.
  </Step>

  <Step title="Choose a delivery mode and paste credentials">
    The wizard first asks **how Slack should deliver events** — **Socket Mode** (default; no public URL) or **Public webhook**. Then it collects the **Bot User OAuth Token** plus the mode credential: an **App-Level Token** (`xapp-…`) for Socket Mode, or the **Signing Secret** for webhook mode. All are password prompts, format-validated (`xoxb-`/`xapp-` prefixes, hex secret); the wizard then calls `auth.test` to confirm Slack accepts the token and resolve your workspace and bot identity. Existing values in `.env` are offered for reuse.
  </Step>

  <Step title="Adapter install">
    The `/add-slack` skill copies the adapter from the `channels` branch, installs the pinned package, builds, writes `SLACK_BOT_TOKEN` plus either `SLACK_APP_TOKEN` (Socket Mode) or `SLACK_SIGNING_SECRET` (webhook) to `.env` (synced to `data/env/env`), and restarts the service.
  </Step>

  <Step title="Identify yourself">
    The wizard asks for your Slack member ID (`U…` — in Slack, click your profile picture → **Profile** → **⋮** → **Copy member ID**), then calls `conversations.open` to get a DM channel with you.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Finish in Slack">
    **Socket Mode:** in your Slack app, go to **Socket Mode** → toggle **Enable Socket Mode** on. Keep **Event Subscriptions** enabled with the bot events below — under Socket Mode no Request URL is required. That's it; just DM the bot.

    **Webhook mode:** make port 3000 publicly reachable (ngrok, Cloudflare Tunnel, or a reverse proxy), then:

    * **Event Subscriptions** → enable, set the Request URL to `https://<your-public-host>/webhook/slack` (Slack sends a verification challenge that must pass)
    * **Interactivity & Shortcuts** → enable, same Request URL
    * Reinstall the app when Slack prompts you to apply the new settings

    Either mode subscribes to the same bot events: `message.channels`, `message.groups`, `message.im`, and `app_mention`.
  </Step>
</Steps>

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

* **Event delivery** — in **Socket Mode** the bot holds an outbound WebSocket to Slack, so nothing inbound needs to be reachable. In **webhook mode** the bridge registers Slack on the shared webhook server (port 3000, configurable via `WEBHOOK_PORT`), routes `/webhook/slack` to the adapter, and authenticates requests with your signing secret — the public URL must stay reachable, or the bot silently stops hearing messages if your tunnel dies.
* **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](/concepts/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 over the same channel as events — the socket in Socket Mode, the `/webhook/slack` route (via **Interactivity & Shortcuts**) in webhook mode — and the card updates in place to show the selection. If buttons do nothing in webhook mode, 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](https://api.slack.com/reference/surfaces/formatting), 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 regardless; inbound depends on your mode. In **Socket Mode**, confirm `SLACK_APP_TOKEN` is set and **Enable Socket Mode** is toggled on in the app. In **webhook mode**, check that your public URL is up, Event Subscriptions is enabled with a verified Request URL, and the bot events are subscribed (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](/operate/troubleshooting).
