Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Slack

Run your ZeroClaw agent as a Slack bot. This guide walks you through it step by step. By the end you’ll have a bot in your workspace that answers when people message it or @-mention it.

Who can talk to the agent

Inbound senders are gated against the peer set resolved for the bound agent, drawn from the peer_groups config the agent belongs to. Matching strips a leading @ and is case-insensitive against the channel’s native sender identifier. An empty set denies everyone; a set containing "*" accepts anyone; otherwise only the listed external peers (and peer agents) are accepted. This is separate from gateway pairing (gateway.require_pairing), which authenticates HTTP/WebSocket clients, not chat-channel senders.

A peer group for slack sets channel to slack, lists the allowed senders in external_peers (for slack, the Slack user ID; ["*"] accepts anyone), optionally names peer agents for cross-agent dispatch, an ignore blocklist, and an output_modality (mirror, voice, or text). See Peer Groups for the field reference.

Where to set this:

Gateway dashboard

Open /config/peer_groups in the web dashboard.

zerocode

In the Config pane, under Peer groups.

Quickstart

Slack needs two tokens: a bot token (what the bot speaks with) and an app token (lets the bot connect without you hosting a public URL). Both come from the same app page.

1. Create the Slack app

  1. Go to api.slack.com/apps and click Create New App -> From scratch.
  2. Name it, pick your workspace, and Create App.

2. Add the permissions the bot needs

  1. In the left sidebar, open OAuth & Permissions.
  2. Under Scopes -> Bot Token Scopes, add: app_mentions:read, channels:history, chat:write, and channels:read. (Add im:history and im:write too if you want direct messages.)

3. Turn on Socket Mode and get the app token

  1. In the left sidebar, open Socket Mode and toggle it on.
  2. Slack prompts you to create an app-level token. Name it, give it the connections:write scope, and Generate.
  3. Copy the token that starts with xapp-. This is your app_token.

Socket Mode lets the bot hold an outbound connection to Slack, so you don’t need a public webhook URL or any port forwarding. This is the easy path.

4. Install the app and get the bot token

  1. Open Install App in the sidebar and click Install to Workspace, then Allow.
  2. Back on OAuth & Permissions, copy the Bot User OAuth Token that starts with xoxb-. This is your bot_token.

5. Tell ZeroClaw about both tokens

Both tokens are secrets, so set them through a surface that encrypts them:

Gateway dashboard

Open /config/channels/slack in the web dashboard.

zerocode

In the Config pane, under Channels.

channels.slack.<alias>.bot_token is a secret. Stored encrypted, never in plain config.toml. Set it through one of these, which encrypt on write:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.bot_token field there.

zerocode

In the Config pane, set the channels.slack.<alias>.bot_token field (input is masked).

zeroclaw config

zeroclaw config set channels.slack.<alias>.bot_token    # prompts for masked input, stores encrypted

Set app_token the same way (it’s the xapp- token from step 3).

6. Invite the bot and test

In Slack, go to a channel and type /invite @YourBotName. Then send a message or @-mention the bot. Start ZeroClaw (zeroclaw service restart or zeroclaw daemon) and it should reply. If not, see Troubleshooting.

Configuration

The full field list, derived from the live schema. For a basic Socket Mode bot you only set bot_token and app_token.

app_token 🔑 secret · default

Slack app-level token for Socket Mode (xapp-…).

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.app_token field.

zerocode

In the Config pane, set the channels.slack.<alias>.app_token field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.app_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__app_token=
approval_timeout_secs integer · default 300

Seconds to wait for operator approval on always_ask tools before auto-denying.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.approval_timeout_secs field.

zerocode

In the Config pane, set the channels.slack.<alias>.approval_timeout_secs field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.approval_timeout_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__approval_timeout_secs=
bot_token* 🔑 secret · default

Slack bot OAuth token (xoxb-…).

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.bot_token field.

zerocode

In the Config pane, set the channels.slack.<alias>.bot_token field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.bot_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__bot_token=
cancel_reaction string? · default null

Emoji reaction name (without colons) that cancels an in-flight request. For example, "x" means reacting with :x: cancels the task. Leave unset to disable reaction-based cancellation.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.cancel_reaction field.

zerocode

In the Config pane, set the channels.slack.<alias>.cancel_reaction field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.cancel_reaction <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__cancel_reaction=
channel_ids string[] · default []

Explicit list of channel IDs to watch. Empty = listen across all accessible channels. Migrated from the legacy channel_id singular field.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.channel_ids field.

zerocode

In the Config pane, set the channels.slack.<alias>.channel_ids field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.channel_ids <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__channel_ids=
draft_update_interval_ms integer · default 1200

Minimum interval (ms) between draft message edits to avoid Slack rate limits.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.draft_update_interval_ms field.

zerocode

In the Config pane, set the channels.slack.<alias>.draft_update_interval_ms field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.draft_update_interval_ms <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__draft_update_interval_ms=
excluded_tools string[] · default []

Tools excluded from this channel’s tool spec. When set, these tools are not exposed to the model when responding via this channel.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.excluded_tools field.

zerocode

In the Config pane, set the channels.slack.<alias>.excluded_tools field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.excluded_tools <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__excluded_tools=
interrupt_on_new_message bool · default false

When true, a newer Slack message from the same sender in the same channel cancels the in-flight request and starts a fresh response with preserved history.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.interrupt_on_new_message field.

zerocode

In the Config pane, set the channels.slack.<alias>.interrupt_on_new_message field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.interrupt_on_new_message <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__interrupt_on_new_message=
mention_only bool · default false

When true, only respond to messages that @-mention the bot in groups. Direct messages remain allowed.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.mention_only field.

zerocode

In the Config pane, set the channels.slack.<alias>.mention_only field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.mention_only <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__mention_only=
proxy_url string? · default null

Per-channel proxy URL (http, https, socks5, socks5h). Overrides the global [proxy] setting for this channel only.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.proxy_url field.

zerocode

In the Config pane, set the channels.slack.<alias>.proxy_url field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.proxy_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__proxy_url=
reply_min_interval_secs integer · default 0

Per-(channel, recipient) outbound pacing floor in seconds. Range: 0..=REPLY_MIN_INTERVAL_MAX_SECS (0 disables).

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.reply_min_interval_secs field.

zerocode

In the Config pane, set the channels.slack.<alias>.reply_min_interval_secs field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.reply_min_interval_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__reply_min_interval_secs=
reply_queue_depth_max integer · default 0

Per-(channel, recipient) outbound pacing queue depth. Range: 0..=REPLY_QUEUE_DEPTH_CEILING. When reply_min_interval_secs > 0 and this value is 0, the pacing wrapper substitutes DEFAULT_REPLY_QUEUE_DEPTH (16). When the queue is full, the newest send is dropped and a WARN is logged.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.reply_queue_depth_max field.

zerocode

In the Config pane, set the channels.slack.<alias>.reply_queue_depth_max field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.reply_queue_depth_max <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__reply_queue_depth_max=
stream_drafts bool · default false

Enable progressive draft message streaming via chat.update.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.stream_drafts field.

zerocode

In the Config pane, set the channels.slack.<alias>.stream_drafts field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.stream_drafts <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__stream_drafts=
strict_mention_in_thread bool · default false

When true (and mention_only is also true), messages inside a Slack thread must also @-mention the bot to trigger a response. By default, thread replies are allowed through without a mention so the bot can keep a back-and-forth going without the user repeating @-mentions. Set this to true in channels shared with human discussion where the bot should stay silent unless explicitly addressed.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.strict_mention_in_thread field.

zerocode

In the Config pane, set the channels.slack.<alias>.strict_mention_in_thread field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.strict_mention_in_thread <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__strict_mention_in_thread=
thread_replies bool? · default null

When true (default), replies stay in the originating Slack thread. When false, replies go to the channel root instead.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.thread_replies field.

zerocode

In the Config pane, set the channels.slack.<alias>.thread_replies field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.thread_replies <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__thread_replies=
use_markdown_blocks bool · default false

Use the newer Slack markdown block type (12 000 char limit, richer formatting). Defaults to false (uses universally supported section blocks with mrkdwn). Enable this only if your Slack workspace supports the markdown block type.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.use_markdown_blocks field.

zerocode

In the Config pane, set the channels.slack.<alias>.use_markdown_blocks field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.use_markdown_blocks <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__slack__<alias>__use_markdown_blocks=

Socket Mode vs HTTP

When app_token is set, the bot uses Socket Mode: it dials out to Slack, so no public URL is required. This is the recommended setup and what the quickstart above uses. Without an app_token, Slack must reach your bot over HTTP, which means hosting a public events endpoint, more setup and more to secure.

Threads and context

When a Slack conversation happens in a thread, that thread is its own conversation. ZeroClaw derives a distinct session key per thread, so every thread carries an independent context window and history: messages in one thread never bleed into another, and the agent does not see a sibling thread’s earlier turns. For Slack this is controlled by thread_replies: when it is on, top-level messages open a thread and each thread is a separate conversation; when off, replies post at the channel root and history is keyed by sender and target instead of by thread.

  • Isolation is the point. Each thread’s context is self-contained: it does not leak outside the thread, and nothing from outside the thread leaks in. Parallel threads hold separate conversational state, so unrelated tasks never contaminate each other.
  • Long threads grow context. A thread accumulates history while it stays active, so a very long thread eventually fills the model’s context window like any other long conversation. Start a new thread to reset.
  • In-flight work is scoped per thread. A new message in one thread does not cancel an in-flight response in another; each thread’s task stands alone.

Set the thread behavior on any surface:

Gateway dashboard

Open /config/channels/slack and toggle the channels.slack.<alias>.thread_replies field.

zerocode

In the Config pane, set the channels.slack.<alias>.thread_replies field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.thread_replies true     # thread replies on
zeroclaw config set channels.slack.<alias>.thread_replies false    # replies at the channel root

strict_mention_in_thread tightens this further: when true, the bot only answers inside a thread if a message there @-mentions it, instead of replying to every message in a thread it’s part of.

Mentions and formatting

  • mention_only: when true, the bot only answers messages that @-mention it, keeping it quiet in busy channels.
  • use_markdown_blocks: render replies with Slack Block Kit formatting for richer layout. Turn off for plain text.

Streaming

Slack streams replies via the stream_drafts boolean:

  • false (default): the whole reply posts as one message once the agent finishes.
  • true: the bot posts a placeholder immediately and edits it in place as the answer streams in. draft_update_interval_ms paces the edits; raise it if Slack rate-limits them.

Set it on any surface:

Gateway dashboard

Open /config/channels/slack and set the channels.slack.<alias>.stream_drafts field.

zerocode

In the Config pane, set the channels.slack.<alias>.stream_drafts field.

zeroclaw config

zeroclaw config set channels.slack.<alias>.stream_drafts <value>

draft_update_interval_ms controls how often the streaming draft is edited (raise it if Slack rate-limits the edits), and cancel_reaction sets an emoji users can react with to cancel an in-flight reply.

Troubleshooting

SymptomLikely causeFix
Bot connects but never repliesBot not invited to the channel/invite @YourBot in the channel
“not_authed” / “invalid_auth” at startupWrong or missing bot_tokenRecopy the xoxb- token (step 4)
Bot never connectsMissing app_token or Socket Mode offTurn on Socket Mode and set the xapp- token (step 3)
Bot ignores most messagesmention_only = true@-mention the bot, or set it to false
Replies have no formattinguse_markdown_blocks = falseSet it to true

See also