Slack Setup Guide

August 23, 2026 ยท View on GitHub

How to create a Slack app for Kiro Crew and connect it.

Slack is optional: If you don't need Slack, skip this entirely. The default kirocrew setup configures no messaging channels, and the gateway runs the web dashboard without any. When you are ready to connect Slack, run kirocrew setup --slack to enter the tokens created below.

Kiro Crew connects to Slack using Socket Mode, so it runs entirely from your own machine over an outbound WebSocket: no public URL, no inbound webhooks, and no hosting required. You just need a Slack workspace where you can install an app.


Choose Your Path

There are two independent paths to create a Slack app. Pick one, and do not mix them.

PathStepsBest for
Path A: Manifest (recommended)6 stepsMost users; auto-configures all scopes, events, and permissions in one shot
Path B: Manual11 stepsIf you need to customize scopes or understand each setting individually

Both paths require a Slack workspace where you can install apps (see Prerequisites).


Prerequisites

  • A Slack workspace where you have permission to install apps. If you don't have one, create a free workspace at https://slack.com/get-started. You can install your own apps in a workspace you own.
  • Python and Kiro Crew installed (pip install kirocrew), so you can run the kirocrew CLI.

Tip: Use a personal or test workspace for your first run. You can always export the app manifest and recreate the app in another workspace later (see Reusing the App in Another Workspace).


Step 1. Generate the Manifest

kirocrew manifest --url

This prints a one-click URL that opens Slack's "Create New App" page with all scopes, events, and permissions pre-filled:

๐Ÿ”— Click to create your Slack app:
https://api.slack.com/apps?new_app=1&manifest_yaml=...
  1. Click (or Cmd-click) the URL printed in Step 1
  2. Select your workspace from the workspace dropdown
  3. Click Create
Alternative: paste manifest manually

If the URL doesn't work, generate the raw YAML instead:

kirocrew manifest -o ~/.kiro/crew/slack-manifest.yaml

Then:

  1. Go to https://api.slack.com/apps โ†’ Create New App โ†’ From a manifest
  2. Select your workspace
  3. Paste the contents of ~/.kiro/crew/slack-manifest.yaml
  4. Click Create

Step 3. Generate App Token

  1. Settings โ†’ Socket Mode โ†’ Toggle OFF then back ON, which triggers the token generation dialog
  2. Add scope connections:write โ†’ Generate
  3. Copy the xapp-... token. This is your App Token

Why toggle off/on? Slack manifests can declare socket_mode_enabled: true, but the App Token (xapp-...) must still be generated manually through the UI. There is no API or manifest field for token generation. Toggling forces the generation dialog to appear.

Step 4. Install to Workspace

  1. Go to Features โ†’ OAuth & Permissions โ†’ Install to Workspace
  2. Approve the installation
  3. Copy the xoxb-... token. This is your Bot Token

"Install App" button greyed out? The Settings โ†’ Install App page sometimes has the button disabled due to a Slack UI bug. Use Features โ†’ OAuth & Permissions โ†’ Install to Workspace instead, which does the same thing.

Workspace admin approval? Some workspaces restrict who can install apps. If your install needs approval, an admin of that workspace must approve it. In a workspace you own, you can self-approve.

Step 5. Configure Kiro Crew

Run the interactive setup, which prompts for both tokens:

kirocrew setup

Paste your App Token (xapp-...), Bot Token (xoxb-...), and your Slack Member ID when prompted.

To find your Slack Member ID: open your workspace in Slack โ†’ click your profile picture โ†’ Profile โ†’ โ‹ฎ โ†’ Copy member ID.

โš ๏ธ Your Member ID is per-workspace. If you install the app in a different workspace, your Member ID changes, so use the ID from the workspace where Kiro Crew is installed.

Step 6. Verify & Run

kirocrew doctor    # verify tokens and config
kirocrew gateway   # start KiroCrew

Open your workspace in Slack, find your app in the Apps section, and send it a DM. The app only lives in the workspace where you installed it.

You're done! ๐ŸŽ‰ Skip ahead to After Setup for next steps.


Path B: Manual Setup

Use this path if you want to configure each scope and event individually, or if you need to customize the app beyond what the manifest provides.

Step 1. Create the App

  1. Go to https://api.slack.com/apps โ†’ Create New App โ†’ From scratch
  2. Name: something unique (e.g. kirocrew). Generic names may conflict with existing apps in the same workspace
  3. Workspace: select your workspace

Step 2. Enable Socket Mode & Get App Token

  1. Settings โ†’ Socket Mode โ†’ Toggle ON
  2. Click Generate Token โ†’ add scope connections:write โ†’ Generate
  3. Copy the xapp-... token. This is your App Token

Step 3. Add Bot Scopes

Go to Features โ†’ OAuth & Permissions โ†’ Bot Token Scopes and add:

ScopePurpose
app_mentions:readRespond when @mentioned
chat:writeSend, update, and delete messages
channels:historyRead channel messages (for @mentions)
channels:readList public channels (the channel picker) and read channel metadata
groups:historyRead messages and thread replies in private channels the bot is in
groups:readList private channels the bot is in and read their metadata
im:historyRead DM history
im:readView DM metadata
im:writeOpen DMs
reactions:writeAdd and remove emoji reactions
files:readRead uploaded files
files:writeUpload screenshots
users:readProfile lookups (users.info) resolve a sender's real name. Without it the lookup fails and is caught: the display name falls back to the matching slack.allowed_users entry, then to the raw Slack member ID
commandsSlash commands

The emoji:read bot scope is deliberately not in the shipped manifest. Add it only if you want custom workspace emojis to appear in the emoji picker.

Upgrading an existing app? Adding a scope to the manifest does not retroactively grant it: Slack only grants new scopes when the app is reinstalled to the workspace. After adding scopes (or importing an updated manifest), go to Settings โ†’ Install App โ†’ Reinstall to Workspace and copy the new Bot Token.

Step 4. Add User Scopes

Under User Token Scopes, add:

channels:history, channels:read, groups:history, groups:read, im:history, im:read, mpim:history, mpim:read, search:read, and users:read.

These scopes belong to the installing user's xoxp-... token. The Kiro Crew gateway itself uses the bot token; the user token is for a separately configured Slack MCP/search integration that lets an agent search Slack as that user. Store and configure that token only in the integration that consumes it.

Step 5. Subscribe to Events

  1. Features โ†’ Event Subscriptions โ†’ Toggle ON
  2. Under Subscribe to bot events, add all of these:
    • message.im
    • message.channels
    • message.groups
    • app_mention
    • app_home_opened
    • file_change
    • member_joined_channel
  3. Click Save Changes

Step 6. Add Slash Commands

Features โ†’ Slash Commands โ†’ Create New Command:

FieldValue
Command/kirocrew
Short DescriptionDashboard access, allowlist, and channel tracking
Usage Hintdashboard [duration] | @user | #channel

The command name you choose here must match the slack.command value in ~/.kiro/crew/config.json (default: kirocrew):

{
  "slack": {
    "command": "kirocrew"
  }
}

When creating the command, check Escape channels, users, and links sent to your app so mentions resolve to <@U1234|user> format.

Step 7. Enable Interactivity

Features โ†’ Interactivity & Shortcuts โ†’ Toggle ON

No Request URL needed: Socket Mode handles it. This is what makes the Block Kit buttons work, including tool approval (approve / trust / reject), the multiple-choice option buttons, the cron and subagent acknowledge buttons, and the session Resume / End buttons.

Step 8. Enable App Home

Features โ†’ App Home:

  • Enable Home Tab
  • Enable Chat Tab
  • Check "Allow users to send Slash commands and messages from the chat tab"

Step 9. Install to Workspace & Get Bot Token

  1. Features โ†’ OAuth & Permissions โ†’ Install to Workspace โ†’ Approve
  2. Copy the xoxb-... token. This is your Bot Token

"Install App" button greyed out? Use Features โ†’ OAuth & Permissions โ†’ Install to Workspace instead (known Slack UI bug).

Step 10. Configure Kiro Crew

Same as Path A, Step 5.

Step 11. Verify & Run

Same as Path A, Step 6.

If you add scopes to an existing app, reinstall it from OAuth & Permissions before testing. Slack does not grant newly declared scopes to tokens from an older installation.


After Setup

Manual Token Configuration

If you prefer to configure tokens manually instead of using kirocrew setup:

mkdir -p ~/.kiro/crew
cat > ~/.kiro/crew/.env << 'EOF'
SLACK_APP_TOKEN=xapp-your-app-token-here
SLACK_BOT_TOKEN=xoxb-your-bot-token-here
KIROCREW_OWNER_ID=your-slack-member-id
EOF
chmod 600 ~/.kiro/crew/.env

Owner-Only Access

Only the owner (KIROCREW_OWNER_ID) can interact with Kiro Crew via Slack. Multi-user access is disabled at the authorization predicate itself, not by configuration: is_allowed_user resolves to an owner check, is_open_channel always returns false, and the channel-join allowlist prompt is a no-op. A /<command> @user invocation replies that multi-user access is disabled.

Setting or changing KIROCREW_OWNER_ID invalidates existing dashboard sessions. The value is not just the Slack DM routing target โ€” it is also the dashboard's authorization principal and the subject baked into every dashboard token at mint time. A dashboard session signed in before the value was set (or before it changed) keeps its old subject for its whole life, so owner-only actions (the Trust/YOLO switch, tool approvals, worktree creation, cloud setup, and the other owner-gated surfaces) are denied for it afterwards. For a session from before an owner was FIRST configured, the dashboard detects the case and shows a sign-in banner; a session signed in under a PREVIOUS owner value is denied with the generic response and likewise needs a fresh sign-in. Either way, run kirocrew token and open the fresh link to re-authenticate under the new owner.

Reaction Emojis

Kiro Crew adds phase-aware emoji reactions during message processing (queued โ†’ thinking โ†’ coding โ†’ done). Customize or disable:

{
  "slack": {
    "reactions": {
      "done": "sparkle",
      "thinking": "brain",
      "coding": "computer"
    },
    "reactions_enabled": true
  }
}

Set reactions_enabled to false to disable all phase reactions. Valid keys: queued, thinking, coding, browsing, tool, done, error.

To suppress a single phase (keep the others at their defaults), set it to null. For example, to keep the working/browsing/tool reactions but hide the terminal ๐Ÿฆž on every message:

{
  "slack": {
    "reactions": { "done": null }
  }
}

A suppressed phase removes any prior reaction but adds nothing new; stall reactions (๐Ÿฅฑ / ๐Ÿ˜จ) are unaffected.


Reusing the App in Another Workspace

To install your app in a different Slack workspace, export the manifest and recreate the app there:

  1. Export your app manifest: App Config โ†’ App Manifest โ†’ Copy to Clipboard (YAML)
  2. Go to https://api.slack.com/apps โ†’ Create New App โ†’ From a manifest
  3. Select the new workspace and paste the YAML
  4. Re-generate the App Token (Socket Mode) and Bot Token, then re-run kirocrew setup with the new tokens and your Member ID for that workspace

Dashboard Access

Dashboard access is token-authenticated. When Slack is connected you can mint a presigned link from Slack; the link is always sent as a DM, never posted into a channel, so the token cannot leak to a channel's members.

Any of these work:

!dashboard              # DM: 1-hour session (default)
!dashboard 2h           # DM: 2-hour session
/kirocrew dashboard     # Slash command: 1-hour session
/kirocrew dashboard 30m # Slash command: 30-minute session

Durations are <N>h or <N>m and are capped at 20 hours; anything longer is silently clamped to the cap, and an unparseable value gets a usage reply instead of a link.

How It Works

  1. Link must be clicked within 5 minutes (after that the URL expires)
  2. On first click: token is bound to your IP, and a session cookie is set (mc_token_<port>, HttpOnly, SameSite=Lax, Secure only over HTTPS). The cookie is keyed by the port your browser connects to, not the port the gateway listens on, because browsers do not isolate cookies by port and two tunnelled instances would otherwise overwrite each other's session
  3. Subsequent visits use the cookie, so there is no need to re-click the link
  4. Session cookie lasts for the requested duration (default 1h, cap 20h)
  5. Every request needs a valid token or cookie, including requests that arrive on loopback. Loopback is not an exemption: a local port forwarder (socat, ssh -R, a helper script) makes remote traffic appear to come from 127.0.0.1, so exempting it would be an auth bypass. The only loopback carve-out is a small set of internal API paths reserved for Kiro Crew's own processes (doctor, the MCP servers), and those additionally require a matching X-Internal-Secret read from ~/.kiro/crew/.local_secret

Dashboard URL Configuration

Set dashboard.url in ~/.kiro/crew/config.json to the host and port you reach the dashboard on:

{
  "dashboard": {
    "url": "http://my-host.example.com:8080"
  }
}

From this single URL, Kiro Crew derives:

  • Port to bind on (8080 in this example)
  • Allowed origins for the CSRF / WebSocket checks
  • Dashboard link hostname for !dashboard and /kirocrew dashboard

When omitted, it defaults to port 5476 and the localhost hostname.

The gateway itself always binds loopback in this build: publishing it is your reverse proxy's or tunnel's job, not the gateway's. KIROCREW_BIND widens the bind address only (the container image sets KIROCREW_BIND=0.0.0.0 so a published -p port is reachable from the host) and changes nothing about token auth, which is mounted unconditionally on both server paths.

Dashboard to Slack Sync

A dashboard chat session can be linked to a Slack thread for two-way sync.

Linking a Session

  1. Open the session menu (the chevron next to the chat title, or right-click the session row in the sidebar)
  2. Pick a target from the link list. The Slack DM entry is always offered; other entries come from your configured channel targets
  3. A new thread is posted in that conversation, titled from the session title (falling back to a snippet of the first prompt), followed by the last five messages as context. Titles and message text are redacted before posting
  4. The menu then shows a "Connected" row for the link, plus actions to post a reminder into the thread or to unlink

Linking a session that is already linked does not create a second thread: it posts a short note into the existing thread and returns the existing link. Unlinking leaves the session, its history, and the Slack thread intact, and posts a courtesy note into the thread so a Slack-side watcher knows why it went quiet.

Two-Way Sync

  • Slack to dashboard: a reply in the linked thread is routed into the linked dashboard session instead of spawning a fresh one
  • Dashboard to Slack: dashboard turns are mirrored into the linked thread

sessions Command

Type sessions in any Slack DM to list recent sessions. Each entry shows a status dot, the session title, the agent name, a bulleted preview of recent messages, and a Resume button. The same content backs the /<command> sessions slash command and the App Home tab.


Slack Commands Reference

Slash Commands

The slash command name is configurable via slack.command in config (default: kirocrew).

CommandPurpose
/<command> dashboardGet a presigned dashboard link (DM'd to you)
/<command> dashboard 2hDashboard link with custom duration (cap 20h)
/<command> yoloToggle auto-approve all tool calls
/<command> agentShow agent selector dropdown
/<command> agent <name>Switch to a named agent
/<command> voiceConfigure TTS voice settings
/<command> configManage users and channels (owner-only)
/<command> usersManage allowed users
/<command> channelsOpen channel management modal
/<command> sessionsList recent sessions with resume buttons
/<command> statusShow runtime stats
/<command> restartRestart the gateway (owner-only)
/<command> #channelTrack or untrack a channel

Any unrecognized sub-command prints the same list, generated from the live registry, so /<command> help is not a special case: anything that does not match falls through to it.

Owner-Only Bang Commands

These !-prefixed commands are restricted to KIROCREW_OWNER_ID.

CommandPurpose
!dashboard / !dashboard 2hGet a presigned dashboard link
!yolo on / !yolo offToggle auto-approve all tool calls
!agent <name> / !agent offSwitch the active agent
!ta <agent> / !ta offOverride agent for current thread only
!link-to-dashboardImport the current Slack thread into a dashboard session
!project <path> / !project offScope which project-local .kiro agents !ta can find (does not change the working directory)
!title <text>Set the thread title
!voiceConfigure TTS voice settings
!channelConfigure the current channel
!stopInterrupt the running turn
!restartRestart the gateway

Keyword Commands

Available in DMs or @mentions.

CommandPurpose
statusShow runtime stats summary
spawn <task>Run a subagent (blocking)
bg <task>Run a subagent (fire-and-forget)
spawn listList active subagents
cron listList cron jobs
cron remove <id>Remove a cron job
sessionsList recent sessions with resume buttons

Security: Protecting Your Dashboard Token

Dashboard tokens grant full session access, so treat them like passwords.

โœ… DoโŒ Don't
Keep the dashboard behind your own tunnel or reverse proxyShare dashboard URLs, which carry the token in ?token=
If a token is exposed, run kirocrew logout (ends all sessions, refresh chains included) and revoke at your tunnel or reverse-proxy auth layerPaste tokens in Slack channels, shared docs, or wikis
Avoid showing the browser URL bar during screen sharesLeave dashboard links in screen-share recordings
Leave the built-in kirocrew token deny rules enabledTrust an AI agent that asks to run kirocrew token

kirocrew logout ends every issued session, not just in-memory state: it bumps a persisted revocation generation that both access cookies and mc_refresh_<port> refresh tokens embed, so cookies handed out before the logout โ€” refresh chains included โ€” are rejected on their next request. See remote-and-mobile.md. Restarting the gateway ends nothing (the generation reloads unchanged). To cut off an exposed dashboard completely, also revoke at your tunnel or reverse-proxy auth layer; to end just one browser's session, sign out in that browser (POST /api/auth/logout), which revokes its chain alone.

โš ๏ธ Prompt injection risk: an attacker can hide instructions in a webpage or document that trick your agent into running kirocrew token and exfiltrating the output. Kiro Crew ships built-in denied-command rules covering that mint, including nested shell payloads and the kiro-crew spelling, enforced at the PreToolUse gate (hooks.py) rather than injected into any agent config file. They are on by default; leave them on. See ../architecture/security-deep-dive.md.


Troubleshooting

ProblemFix
Install App button greyed outUse Features โ†’ OAuth & Permissions โ†’ Install to Workspace instead (known Slack UI bug)
App created in wrong workspaceDelete the app on api.slack.com, then recreate it in the correct workspace
No events receivedVerify Socket Mode is ON, events are subscribed, App Home Chat Tab is enabled. Reinstall app after changes
Home tab is blankAdd app_home_opened event, enable Home Tab, reinstall app
missing_scope errorAdd the scope in OAuth & Permissions, reinstall app, re-run kirocrew setup
Bot doesn't respondCheck kirocrew doctor output. Ensure gateway is running (kirocrew gateway)
Install needs approvalYour workspace restricts app installs, so a workspace admin must approve, or use a workspace you own
Dashboard shows 403Token expired, IP changed, or the link was opened more than 5 minutes after it was issued. Run !dashboard for a new link

References