Slack Integration
Connect Agent Swarm to Slack — @mention agents to create tasks, use swarm# syntax to target specific workers, and manage multi-agent workflows via direct messages, channels, and the Slack Assistant sidebar.
Enable Slack for task creation, agent communication, and interactive workflows via direct messages and the Slack Assistant sidebar.
Setup
- Create a Slack App
- Enable Socket Mode (for real-time events without public webhooks)
- Enable Interactivity (for action buttons and modals)
- Enable Assistant View (for sidebar conversations)
- Add required bot token scopes:
app_mentions:readassistant:writechannels:history,channels:join,channels:manage,channels:readchat:write,chat:write.customize,chat:write.publiccommandsfiles:read,files:writegroups:history,groups:read,groups:writeim:history,im:read,im:writempim:history,mpim:read,mpim:writereactions:writeusers:read
- Subscribe to bot events:
app_mention,assistant_thread_started,assistant_thread_context_changed,message.channels,message.groups,message.im,message.mpim - Install to your workspace and copy tokens
A ready-to-use slack-manifest.json is included in the repository root — import it directly in the Slack App configuration page to set up all scopes, events, and features automatically. After adding channel-management scopes to an existing app, reinstall the app to the workspace so Slack grants them to the bot token.
Configuration
# Required for Slack
SLACK_BOT_TOKEN=xoxb-... # Bot User OAuth Token
SLACK_APP_TOKEN=xapp-... # App-Level Token (Socket Mode)
SLACK_SIGNING_SECRET=... # Signing Secret (optional for Socket Mode)
# Disable Slack (if not using)
SLACK_DISABLE=trueDevelopment API processes do not open Socket Mode by default, even when Slack tokens are present in the ambient environment. This prevents a local bun run start:http process from consuming events intended for production. Set SLACK_ALLOW_DEV_SOCKET_MODE=true only when that development process must connect to the configured Slack app; the server logs the blocked reason and this opt-in name otherwise.
How It Works
Creating Tasks
@mention the bot in Slack to create tasks. All Slack messages are routed directly as tasks — there is no separate inbox system.
Routing priority:
swarm#<uuid>— explicit agent targeting (always wins)swarm#all— broadcast to all workers- Thread follow-up — if in a thread where a worker is already active, or the thread was originally started by the swarm, routes directly to that worker/flow
- Lead fallback — if the bot was @mentioned and no other match, routes to the lead agent
If no agents are online, the message is queued as an unassigned task in the pool. The bot confirms that your request has been queued and will be processed when agents come back up.
Acceptance reactions
When the swarm accepts a Slack message, it adds an :eyes: reaction after it has successfully created or queued the task. The same acknowledgement appears when a thread message is accepted as steering for a running task. If ingestion fails before the request is accepted, the bot leaves the message unreacted instead of implying that work started; repeated delivery of an already-acknowledged event is treated as a harmless no-op.
Additive thread buffering uses a slightly richer vocabulary: :eyes: for the first captured message, :heavy_plus_sign: for later messages appended to that buffer, and :zap: when !now triggers an immediate flush.
Thread Follow-up Routing
When you @mention the bot in a thread where a worker is already handling a task, the message routes directly to that worker — no lead delegation needed. This keeps conversations flowing naturally.
If the assigned worker is offline or unavailable, the follow-up routes to the lead agent instead of dropping the message. The lead picks up the thread context and continues the conversation, preserving parentTaskId continuity for chained tasks.
By default, thread follow-ups route automatically without requiring an @mention. That includes human replies to swarm-started root messages, even when no task row existed yet for the thread. Set SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION=true to require an explicit @mention for thread follow-up routing — non-mention thread messages will be silently dropped instead of auto-routing.
Live steering instead of follow-up tasks
Slack can send buffered thread feedback into a task that is still running:
| Variable | Default | Behavior |
|---|---|---|
SLACK_THREAD_STEERING | off | lead targets the latest in-progress lead task in the thread; all targets the latest active task regardless of role. Unset and other values preserve normal follow-up task routing. |
SLACK_THREAD_STEERING_MODE | queue | queue adds the message at a turn boundary. steer requests an interrupt and degrades when the harness cannot interrupt. |
This is opt-in because it changes a thread reply from a durable follow-up task into input for an already-running task. Slack posts an acknowledgement that reflects the actual server outcome (steered, queued, or promoted to a follow-up).
Follow-up re-delegation guard: When the lead receives a task.worker.completed or task.worker.failed follow-up, it is explicitly instructed (via prompt template) not to re-delegate the same work back to a worker. A second guard in send-task blocks any re-delegation on a Slack thread that already has a completed task within the last 48 hours, preventing the duplicate-response cycle where repeated re-delegations caused the bot to answer the same thread multiple times.
Slack Context Propagation
Slack metadata (slackChannelId, slackThreadTs, slackUserId) is auto-inherited from the creator's current task. When a lead delegates work from a Slack-originated task, workers automatically receive the Slack context and post progress updates to the originating thread — no manual metadata passing needed.
The auto-inheritance works via the X-Source-Task-Id header, which links the new task back to the creator's active task to look up Slack metadata.
For cases where auto-inheritance isn't available (e.g., programmatic task creation without an active parent task), you can pass Slack metadata explicitly on send-task:
slackChannelId— Channel ID for progress updatesslackThreadTs— Thread timestamp for thread-level updatesslackUserId— Original requester's Slack user ID
Treat Slack channel/thread metadata as one routing unit. When a parent task or Slack-family context key already defines the route, send-task rejects a different explicit channel or thread instead of silently sending updates elsewhere. Omit the explicit Slack fields to inherit the existing route. For an intentional cross-channel handoff, provide both slackChannelId and slackThreadTs and set overrideSlackContext: true; the override is logged for audit.
Additive Slack Buffer
When enabled via ADDITIVE_SLACK=true, thread replies that do NOT @mention the bot are captured, buffered, and batched into a single follow-up task. This allows multi-message feedback without requiring an @mention each time.
- Messages are buffered for a configurable debounce window (
ADDITIVE_SLACK_BUFFER_MS, default 10s) - Buffered messages are flushed into a single task with dependency chaining to the active task
- Use the
!nowcommand to flush the buffer immediately — skips dependency chaining so the task starts right away - Reactions provide visual feedback: :eyes: for first captured message, :heavy_plus_sign: for subsequent appended messages, :zap: for
!now - When
SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION=true, the additive buffer is disabled for non-mention messages
Tree-Based Status Messages
The legacy per-task renderer remains the default. Set SLACK_RENDER_V2=true to opt in to the v2 preview, where each Slack thread owns exactly one engine message: a task tree that is updated in place with chat.update. Later asks append to the same tree, delegated tasks stay nested under their parent, and task IDs remain clickable:
🧵 worked for 44m
├─ ✅ First ask · 7m51s · task-id
├─ ⏳ Current ask · 8m05s · task-id · latest progress
│ └─ ✅ Researcher · 8m24s · task-id
└─ ✅ Completed ask · 12m · task-idThe tree is rendered as footer-weight context blocks and contains status, elapsed time, hierarchy, task links, and the latest bounded progress for active tasks. It does not repeat worker output or cross-link Slack messages: keeping message permalinks out of the tree prevents Slack from producing noisy link previews. Updates are debounced, link/media unfurls are disabled, and rate-limit retries honor Slack backoff.
Outcome Cards and Message Provenance
Each completed Slack ask gets one outcome card containing the full multi-paragraph Markdown result, bounded only by Slack's presentation limit with a link to the full task when truncation is required. If the agent already delivered the result with slack-reply, the outcome card collapses to a compact completion instead of repeating that message. The renderer re-reads this delivery marker before finalizing, so a retry cannot preserve stale duplicate content. Outcome cards do not link back to the tree, which avoids an otherwise redundant Slack permalink unfurl.
Terminal task output is automatically delivered through the thread's outcome card. No additional relay message is created: extra messages only appear when an agent explicitly calls slack-reply, slack-post, or slack-start-thread. When slack-reply is used, the outcome card is compacted instead of duplicating that explicit reply. These tools accept optional Block Kit blocks; when omitted they generate a mrkdwn section. Their compact context footer contains the originating agent and task without a Slack-message permalink.
Tree, outcome, and explicit agent message timestamps are persisted in slack_messages, so the renderer can reuse messages after a restart.
Opt In to the Renderer
Leave SLACK_RENDER_V2 unset or set it to false to use the default legacy per-task assignment/progress/completion renderer. Set SLACK_RENDER_V2=true to preview the new task tree and streamed outcome cards without changing the deployment.
Rich Block Kit Messages
The v2 tree uses mrkdwn inside footer-weight context blocks, which keeps every task ID clickable. Outcome and explicit agent messages use Block Kit where it adds structure:
- Context blocks for the compact provenance footer
- Section blocks for explicitly supplied agent content
- Markdown is automatically converted to Slack's
mrkdwnformat
Interactive Actions
The default legacy renderer's task messages include interactive buttons when SLACK_RENDER_V2 is unset or false:
- Follow-up — Opens a modal to send a follow-up message to the same agent, creating a new task with dependency on the completed one
- View Full Logs — Links to the task detail page in the dashboard
- Cancel — Shows a confirmation dialog before cancelling an in-progress task
Assistant Sidebar
The bot supports Slack's Assistant sidebar for direct conversations:
- Open the sidebar in any channel or DM to start a conversation
- Suggested prompts help you get started ("Check agent status", "Assign a task", "List recent tasks")
- Follow-up messages in assistant threads route to the same agent that handled the original task
- Assistant-thread messages that only @mention another user are ignored unless they also mention the swarm bot, preventing accidental task creation from co-mentions like
@Devin are you here? - File uploads (
file_sharemessages) in assistant threads are automatically detected and routed to the lead agent - The assistant sets a typing status while the agent is working (gracefully handles permission errors in non-assistant threads)
Progress Updates
The engine reflects progress by updating the thread tree. Agents can choose to send a distinct message with slack-reply, but routine start, progress, completion, and failure receipts are not posted automatically.
Reading Messages
Agents can read Slack threads using slack-read:
- By task ID (reads the thread associated with a task)
- By channel ID (leads only, for channel history)
slack-read and the worker thread-context helpers extract all message layers together: top-level text, legacy attachments, and Block Kit blocks. That means alert threads from tools like Datadog, PagerDuty, and GitHub keep both the short summary and the richer body content (fields, context, action URLs) instead of silently dropping everything outside the root text.
Posting Messages
The lead agent can post messages to channels using slack-post. By default each call creates a new top-level message. To run a multi-message conversation under a single Slack thread, the lead first calls slack-start-thread to create the parent message, then passes the returned ts as threadTs on subsequent slack-post calls — keeping the channel tidy and the conversation discoverable.
Managing Channels
Lead agents can manage the Slack channel lifecycle through three MCP tools:
slack-create-channel— create a public or private channel. Slack naming rules are applied and the normalized name is returned.slack-invite-to-channel— invite up to 100 workspace users. Users who are already members are treated as a successful no-op.slack-archive-channel— archive a channel. Already-archived channels are a successful no-op, while Slack's general channel remains protected.
These operations require lead privileges plus channels:manage for public channels and groups:write for private channels. If Slack reports a missing scope, update slack-manifest.json, apply the manifest, and reinstall the app before retrying.
Updating or Deleting Messages
Lead-gated Slack mutation tools can also manage an existing swarm-authored message after it has been posted:
slack-update— replace the text or blocks of an existing message in a channel or threadslack-delete— remove an existing message when follow-up automation or cleanup needs it
These mutation tools follow the same public-channel auto-join behavior as the other Slack tools, but they stay lead-only because they change already-published Slack state.
Channel Membership
If the bot is not yet a member of a public, internal channel, slack-read, slack-post, slack-reply, and slack-start-thread automatically join it (via the channels:join scope) and retry — no manual /invite needed. Private channels and external Slack Connect channels cannot be self-joined: the tools return a clear error asking you to invite the bot with /invite @<bot-name> first.
User Filtering
By default, all Slack users can interact with the bot. To restrict access:
# Only users with matching email domains
SLACK_ALLOWED_EMAIL_DOMAINS=company.com,partner.com
# Specific user IDs always allowed (useful for admins)
SLACK_ALLOWED_USER_IDS=U12345678,U87654321If both are set, a user must match either an allowed domain or be in the user ID whitelist.
Attachment Handling
Slack messages with file attachments (voice memos, images, documents) are automatically recognized and processed. When a user sends a file — even without any text — the bot detects it and includes attachment metadata (filename, MIME type, size, Slack file ID) in the task description.
This means agents can:
- Receive voice messages or image uploads as tasks
- Access file metadata to decide whether to download and process attachments
- Use
slack-download-fileto retrieve the actual file content
File Handling
Agents can upload and download files via Slack:
slack-upload-file— Upload a file to a Slack channel or threadslack-download-file— Download a file from Slack by file ID or URL
For task-scoped uploads, slack-upload-file preserves the visible conversation thread: channel tasks upload under the original Slack thread, and Slack DMs prefer the user-facing DM tree root instead of an internal progress-message thread when both exist.
Files are saved to /workspace/shared/downloads/{agentId}/slack/ by default (each agent writes to its own subdirectory).
Related
- Environment Variables — Slack configuration variables (
SLACK_BOT_TOKEN, etc.) - Task Lifecycle — How Slack messages become tasks
- GitHub App Integration — Another external task source
- Linear Integration — Bidirectional ticket tracking with Linear
- Sentry Integration — Automated error triage from Sentry
- x402 Payments — Enable agents to make crypto micropayments
Integrations
External tools the swarm can talk to — issue trackers, chat platforms, error monitoring, email, and more.
GitHub App Integration
Connect your GitHub repos to Agent Swarm — AI agents auto-trigger on @mentions, issue assignments, and labels via webhooks. Full setup guide with permissions, event handling, and bi-directional sync.