Skip to content

Slack Integration

Connect your herdctl agents to Slack, allowing users to interact with AI agents through channel messages, @mentions, and threaded conversations. herdctl uses Slack’s Socket Mode so no public URL or reverse proxy is needed.

herdctl uses a per-agent connector model for Slack, matching the Discord integration architecture. Each agent gets its own SlackConnector instance, but all agents share the same Slack App identity (one bot token for the whole workspace). Different channels are assigned to different agents.

Single Bot

One Slack App handles all agents — no need to create multiple bots

Channel Routing

Each channel maps to a specific agent, so users talk to the right agent automatically

Socket Mode

Connects via WebSocket — no public URL, domain, or reverse proxy required

Threaded Replies

Responses appear in threads, keeping channels organized

Slack Workspace (single bot)
├── #support
│ └── @HerdBot → routed to support-agent
├── #dev-help
│ └── @HerdBot → routed to coder-agent
└── #general
└── @HerdBot → routed to general-agent

All channels use the same bot identity. When a user messages or @mentions the bot in a channel, herdctl routes the message to the agent assigned to that channel and triggers a Claude session to respond.

AspectSlackDiscord
Bots per workspace1 shared bot identity, 1 connector per agent1 bot per agent
Token modelBot token + App token (shared across agents)Separate bot token per agent
ConnectionSocket Mode (WebSocket)Gateway (WebSocket)
Channel structureFlat channelsGuild → Channels
CommandsPrefix commands (!help)Slash commands (/help)
DM supportSupportedSupported
Public URL neededNoNo
ModeBehaviorBest For
mentionResponds only when @mentionedShared channels where you want explicit interaction
autoResponds to all messagesDedicated channels for a specific agent

herdctl supports two different Slack integrations:

IntegrationTypePurposeConfiguration
ChatTwo-wayInteractive conversationschat.slack
Notification HooksOne-wayJob completion alertshooks.after_run

The chat integration documented on this page enables interactive, two-way conversations:

  • Bot responds to user messages and mentions
  • Maintains conversation context in threads
  • Users can ask questions and get responses
  • Configured in the chat.slack section of agent config

Notification hooks send one-way alerts when jobs complete:

  • Post job results to a channel
  • Conditional notifications (e.g., only when a price drops)
  • No user interaction — just announcements
  • Configured in the hooks.after_run section

Example notification hook:

hooks:
after_run:
- type: slack
channel_id: "${SLACK_CHANNEL_ID}"
bot_token_env: SLACK_BOT_TOKEN
when: "metadata.shouldNotify"

Before setting up Slack integration, ensure you have:

  • A Slack account
  • Admin permissions on the target Slack workspace (or permission to install apps)
  • Access to the Slack API portal
  • herdctl installed and configured
  • Your agent configuration file ready
  1. Open the Slack API Portal

    Navigate to https://api.slack.com/apps and sign in with your Slack account.

  2. Create a New App

    Click Create New App and choose From scratch.

    Enter a name for your app (e.g., “HerdBot” or “My Fleet Bot”) and select the workspace where you want to install it.

  3. Enable Socket Mode

    In the left sidebar, go to Settings > Socket Mode.

    Toggle Enable Socket Mode on.

    You’ll be prompted to generate an App-Level Token:

    • Give it a name (e.g., “socket-mode-token”)
    • Add the connections:write scope
    • Click Generate
    • Copy the token (starts with xapp-)
  4. Add Bot Token Scopes

    In the left sidebar, go to Features > OAuth & Permissions.

    Scroll to Bot Token Scopes and add:

    ScopePurpose
    app_mentions:readDetect when users @mention the bot
    chat:writeSend messages and replies
    channels:historyRead channel messages for context
    files:writeUpload files (for long responses)
    im:historyRead DM messages for direct message support
  5. Subscribe to Events

    In the left sidebar, go to Features > Event Subscriptions.

    Toggle Enable Events on.

    Under Subscribe to bot events, add:

    EventPurpose
    app_mentionTriggers when the bot is @mentioned
    message.channelsTriggers on all channel messages (for auto mode)
    message.imTriggers on direct messages to the bot

    Click Save Changes.

  6. Install the App to Your Workspace

    In the left sidebar, go to Settings > Install App.

    Click Install to Workspace and authorize the app.

    Copy the Bot User OAuth Token (starts with xoxb-).

  7. Add the Bot to Channels

    In Slack, go to each channel where you want the bot to operate:

    • Type @YourBotName and send the message, then click Invite to Channel when prompted
    • Or open channel settings > Integrations > Add an App

    The bot must be a member of a channel to receive messages from it.

You’ll need channel IDs to configure which channels route to which agents.

  1. Open Slack (desktop or web app)

  2. Right-click on a channel name in the sidebar

  3. Select View channel details (or Open channel details)

  4. The channel ID is shown at the bottom of the details panel — click to copy

Alternatively, you can find the channel ID in the URL when viewing a channel in the Slack web app:

https://app.slack.com/client/T01234567/C0123456789
^^^^^^^^^^^
This is the channel ID
channels:
- id: "C0123456789" # Channel ID (starts with C)
name: "#support"

Configure Slack in your agent’s YAML file under the chat.slack section.

name: support-agent
description: "Customer support agent"
chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
channels:
- id: "C0123456789"
name: "#support"
mode: mention
chat:
slack:
# Environment variable containing the Bot User OAuth Token (required)
# Token starts with xoxb-
bot_token_env: SLACK_BOT_TOKEN
# Environment variable containing the App-Level Token for Socket Mode (required)
# Token starts with xapp-
app_token_env: SLACK_APP_TOKEN
# Session expiry in hours (default: 24)
session_expiry_hours: 24
# Log level: minimal, standard, verbose (default: standard)
log_level: standard
# Output configuration - control what SDK messages appear in Slack
output:
tool_results: true # Show tool results (default: true)
tool_result_max_length: 900 # Max chars in tool output (default: 900, max: 1000)
system_status: true # Show system status messages (default: true)
errors: true # Show error messages (default: true)
# Channel configurations
channels:
- id: "C0123456789" # Slack channel ID (required)
name: "#support" # Human-readable name (optional)
mode: mention # mention or auto (default: mention)
context_messages: 10 # Messages to include as context (default: 10)
- id: "C9876543210"
name: "#dev-help"
mode: auto
context_messages: 5
# Direct message settings (optional)
dm:
enabled: true # Accept DMs (default: true)
mode: auto # mention or auto (default: auto)
allowlist: # Only accept DMs from these user IDs (optional)
- "U0123456789"
blocklist: # Block DMs from these user IDs (optional)
- "U9876543210"
FieldTypeDefaultDescription
bot_token_envstringRequired. Environment variable name containing the Bot User OAuth Token (xoxb-)
app_token_envstringRequired. Environment variable name containing the App-Level Token (xapp-) for Socket Mode
session_expiry_hoursnumber24Hours before a conversation session expires
log_levelstringstandardLogging verbosity: minimal, standard, verbose
outputobjectControl what SDK messages appear in Slack (tool results, system status, errors)
channelsarrayList of Slack channels to operate in
FieldTypeDefaultDescription
idstringRequired. Slack channel ID (starts with C or G)
namestringHuman-readable name (for documentation only)
modestringmentionResponse mode: mention or auto
context_messagesnumber10Number of recent messages to include as context
FieldTypeDefaultDescription
dm.enabledbooleantrueWhether to accept direct messages
dm.modestringautoResponse mode for DMs: mention or auto
dm.allowliststring[]Only accept DMs from these Slack user IDs
dm.blockliststring[]Block DMs from these Slack user IDs

Control which SDK messages are surfaced in Slack. When your agent uses tools (Bash, Read, Write, etc.) or the SDK emits system/error messages, these settings determine what appears in the channel.

FieldTypeDefaultDescription
output.tool_resultsbooleantrueShow tool results when the agent uses tools
output.tool_result_max_lengthnumber900Maximum characters shown in tool output (max: 1000)
output.system_statusbooleantrueShow system status messages (e.g., “Compacting context…”)
output.errorsbooleantrueShow error messages when the SDK reports errors

All output types appear as formatted Slack messages with emoji indicators:

Message TypeEmojiExample Content
Tool result (success)Tool emoji🔧 Bash> git status with output preview
Tool result (error)Tool emoji🔧 Bash — command output with error
System status⚙️⚙️ System — “Compacting context…”
Error❌ Error — error description

Tool result messages include the tool name with an emoji, the input summary (command, file path, or search pattern), execution duration, output length, and a truncated preview of the output.

Minimal output (text responses only):

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
output:
tool_results: false
system_status: false
errors: false

Full visibility (all message types):

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
output:
tool_results: true
tool_result_max_length: 500
system_status: true
errors: true

herdctl uses environment variables for Slack tokens to keep secrets out of configuration files.

Slack requires two tokens (compared to Discord’s one):

Terminal window
# Bot User OAuth Token — authenticates as the bot user
# Found at: Slack App > OAuth & Permissions > Bot User OAuth Token
export SLACK_BOT_TOKEN="xoxb-your-bot-token-here"
# App-Level Token — enables Socket Mode connection
# Found at: Slack App > Settings > Basic Information > App-Level Tokens
export SLACK_APP_TOKEN="xapp-your-app-token-here"

If you have multiple workspaces or want descriptive names:

Terminal window
# Pattern: {PURPOSE}_SLACK_BOT_TOKEN / {PURPOSE}_SLACK_APP_TOKEN
export SUPPORT_SLACK_BOT_TOKEN="xoxb-..."
export SUPPORT_SLACK_APP_TOKEN="xapp-..."

Create a .env file in your project root (next to herdctl.yaml):

.env
SLACK_BOT_TOKEN=xoxb-your-bot-token-here
SLACK_APP_TOKEN=xapp-your-app-token-here
SLACK_CHANNEL_ID=C0123456789

herdctl automatically loads .env files when you run herdctl start.

In your agent YAML, reference the environment variable names (not the values):

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN # The variable NAME, not the token
app_token_env: SLACK_APP_TOKEN # The variable NAME, not the token

herdctl reads the tokens from process.env.SLACK_BOT_TOKEN and process.env.SLACK_APP_TOKEN at runtime. You can also use ${VAR_NAME} substitution in channel IDs for flexibility:

channels:
- id: "${SLACK_CHANNEL_ID}"

Slack’s Socket Mode is a key advantage of the herdctl Slack integration. Instead of requiring a public URL to receive webhook events, Socket Mode uses a WebSocket connection initiated by the bot.

Traditional Webhooks:
Slack ──HTTP POST──→ https://your-server.com/slack/events
(Requires public URL, domain, SSL, reverse proxy)
Socket Mode:
Your Bot ──WebSocket──→ Slack
(Works behind NAT, firewalls, on localhost)
  • No public URL needed — Run agents on your laptop, behind a firewall, or on a private server
  • No domain or SSL setup — No need to configure DNS, certificates, or reverse proxies
  • Instant setup — Just set tokens and go
  • Firewall friendly — Outbound WebSocket connections work through most firewalls

The App-Level Token (xapp-) is specifically for establishing this Socket Mode connection. The Bot User OAuth Token (xoxb-) is used for API calls (sending messages, reading history, etc.).

Terminal window
herdctl start

Look for connection messages in the logs:

[slack] Connecting to Slack via Socket Mode...
[slack] Connected to Slack: HerdBot
[slack] Listening in channels: #support (C0123456789), #dev-help (C9876543210)

The bot should appear as active in the channels you configured. You can check with:

!status

In a channel configured with mode: mention:

You: @HerdBot How do I reset my password?
HerdBot: To reset your password, follow these steps...

In a channel configured with mode: auto:

You: How do I reset my password?
HerdBot: To reset your password, follow these steps...

herdctl provides built-in prefix commands for every Slack-enabled agent:

CommandDescription
!helpShow available commands and usage
!statusShow bot connection status and session info
!resetClear conversation context and start fresh

Try them in any channel where the bot is active:

!status

Shows available commands and basic usage information:

!help

Example output:

Available commands:
!help - Show this help message
!status - Show bot status
!reset - Reset conversation context
Chat with me:
@HerdBot your question — in channels

Shows connection status and session information:

!status

Example output:

Bot Status
Connected: Yes
Uptime: 2h 34m
Session: Active (expires in 21h)
Channel: #support

Clears the conversation context for the current channel:

!reset

Example output:

Conversation reset! Starting fresh.

Use !reset when:

  • The bot seems confused or stuck on a previous topic
  • You want to start a completely new conversation
  • The session has accumulated too much irrelevant context

Like Discord, Slack creates a separate connector for each agent internally. However, all agents share the same Slack App identity (single bot token). Different agents are assigned to different channels.

Each channel maps to exactly one agent. Each agent’s connector manages its own channels directly, so messages are handled by the correct agent without centralized routing.

agents/support-agent.yaml
name: support-agent
chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
channels:
- id: "C111111111"
name: "#support"
mode: mention
# agents/coder-agent.yaml
name: coder-agent
chat:
slack:
bot_token_env: SLACK_BOT_TOKEN # Same tokens!
app_token_env: SLACK_APP_TOKEN # Same tokens!
channels:
- id: "C222222222"
name: "#dev-help"
mode: auto

Both agents share the same bot tokens because they use the same Slack App. The routing is determined by channel ID:

  • Messages in #support go to support-agent
  • Messages in #dev-help go to coder-agent
  1. Use distinct channels — Each agent should have its own dedicated channel(s). Since all agents share the same bot identity, channel separation is how users interact with different agents.

  2. Name channels clearly — Use descriptive channel names like #support-bot, #code-review, #dev-help so users know which agent they’re talking to.

  3. Set channel topics — Add the agent’s purpose to the channel topic (e.g., “Ask me about code reviews — powered by herdctl”).

  4. Use mention mode in shared channels — If a channel must be monitored by multiple agents (not recommended), use mention mode.

ScenarioRecommendation
Two agents need separate channelsAssign different channels to each agent
One agent needs multiple channelsList multiple channels in that agent’s config
Shared discussion channelDon’t assign it to an agent, or use mention mode

Slack chat integration maintains conversation context so your agent can have multi-turn conversations and “remember” what was discussed.

  • Scope: Sessions are per-channel
  • Identity: Session ID is based on channel ID
  • Expiry: Sessions expire after session_expiry_hours (default: 24 hours)
  • Persistence: Sessions are stored as YAML files in .herdctl/slack-sessions/ and survive bot restarts within the expiry window

When a user sends a message, the agent receives:

  1. The current message as the prompt
  2. Recent conversation history from the session
  3. The channel context

This allows natural conversations:

User: What's the current price of the Hyken chair?
Bot: The Hyken chair is currently $189 at Staples.
User: When did you last check?
Bot: I checked prices 2 hours ago at 10:30 AM.
User: Is that below my target?
Bot: Yes! Your target is $200, so $189 is $11 below target.

The bot remembers the chair and target price from earlier in the conversation.

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
session_expiry_hours: 24 # Default: 24 hours

Choose expiry based on your use case:

Use CaseRecommended Expiry
Support bot24-48 hours
Quick Q&A1-4 hours
Long-running projects72+ hours
Stateless responses1 hour

Users can clear their session context using !reset:

!reset

This is useful when:

  • The bot is confused about context from earlier
  • Starting a completely new topic
  • Testing fresh conversation flows

Control how many recent messages are included as context:

channels:
- id: "C0123456789"
mode: mention
context_messages: 10 # Include last 10 messages

More context = better continuity but higher token usage.

Slack agents can receive and respond to direct messages (DMs) from users.

DM support is enabled by default. To configure it:

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
channels:
- id: "C0123456789"
mode: mention
dm:
enabled: true
mode: auto
# Accept DMs from everyone (default)
dm:
enabled: true
mode: auto
# Only accept DMs from specific users
dm:
enabled: true
allowlist:
- "U0123456789"
- "U9876543210"
# Block specific users
dm:
enabled: true
blocklist:
- "UBADUSER001"
# Disable DMs entirely
dm:
enabled: false

Slack uses its own markup format called mrkdwn (not standard Markdown). herdctl automatically converts the agent’s Markdown output to Slack’s mrkdwn format, so you don’t need to worry about formatting differences.

MarkdownSlack mrkdwnRendered
**bold***bold*bold
*italic*_italic_italic
`code``code`code
~~strike~~~strike~strike
[link](url)<url|link>link

Code blocks, lists, and blockquotes are also converted automatically.

Slack has a message length limit of approximately 4,000 characters (compared to Discord’s 2,000). When a response exceeds this limit, herdctl automatically splits it into multiple messages.

ScenarioRecommended Mode
Dedicated agent channelauto
Shared team channelmention
High-traffic channelmention
Testing/developmentauto

For multiple agents in the same workspace:

  1. Dedicated channels: Give each agent its own channel with auto mode

    #support-bot → support-agent (auto)
    #dev-help → coder-agent (auto)
  2. Shared channels: Use mention mode so users explicitly address the bot

    #general → general-agent (mention)

Slack has rate limits on API calls. To avoid issues:

  1. Don’t use auto mode in high-traffic channels — Use mention mode instead
  2. Keep responses concise — Avoid very long multi-paragraph responses
  3. Reduce context_messages for busy channels — Lower token usage means faster responses

If you see rate limit warnings in logs, consider:

  • Switching busy channels to mention mode
  • Reducing session expiry to limit context size
  • Using shorter context_messages values

Cause: The environment variable specified in bot_token_env is not set or is empty.

Solution:

Terminal window
# Check if the variable is set
echo $SLACK_BOT_TOKEN
# Set it if missing
export SLACK_BOT_TOKEN="xoxb-your-token-here"

Cause: The environment variable specified in app_token_env is not set or is empty.

Solution:

Terminal window
# Check if the variable is set
echo $SLACK_APP_TOKEN
# Set it if missing — generate one at Slack App > Settings > Socket Mode
export SLACK_APP_TOKEN="xapp-your-token-here"

Cause: The bot token is incorrect, expired, or the app was uninstalled.

Solution:

  1. Go to api.slack.com/apps
  2. Select your app
  3. Go to OAuth & Permissions
  4. Check that the app is installed (or reinstall it)
  5. Copy the current Bot User OAuth Token
  6. Update your environment variable

Cause: Various configuration issues.

Checklist:

  • Is the bot added to the channel? (Invite it with @BotName or via channel settings)
  • Is the channel ID correct in the config?
  • Is the mode appropriate? (mention requires @mention)
  • Are event subscriptions configured? (app_mention and message.channels)
  • Is Socket Mode enabled?
  • Check logs for errors

Cause: The bot is not a member of the channel, or the channel ID is wrong.

Solution:

  1. Verify the channel ID is correct (right-click channel > View channel details)
  2. Invite the bot to the channel (@BotName in the channel)
  3. For private channels, the bot must be explicitly invited

Cause: App-Level Token is missing, invalid, or lacks the connections:write scope.

Solution:

  1. Go to api.slack.com/apps
  2. Select your app > Settings > Socket Mode
  3. Verify Socket Mode is enabled
  4. Check that your App-Level Token has the connections:write scope
  5. If needed, generate a new App-Level Token

Cause: The bot lacks required OAuth scopes.

Solution:

  1. Go to your Slack App > OAuth & Permissions > Bot Token Scopes
  2. Add the missing scope (see required scopes)
  3. Reinstall the app — scope changes require reinstallation

Enable verbose logging to troubleshoot issues:

chat:
slack:
bot_token_env: SLACK_BOT_TOKEN
app_token_env: SLACK_APP_TOKEN
log_level: verbose # Shows detailed debug information

Verbose logs include:

  • All received messages and whether they were processed
  • Socket Mode connection state changes
  • Channel-to-agent routing decisions
  • Rate limit events

Use the !status command in any channel where the bot is active to check connection status and session information.