Skip to main content

Slack

Socket mode (default)

Quick setup (beginner)

  1. Create a Slack app and enable Socket Mode.
  2. Create an App Token (xapp-...) and Bot Token (xoxb-...).
  3. Set tokens for Clawdia and start the gateway.
Minimal config:
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-..."
    }
  }
}

Setup

  1. Create a Slack app (From scratch) in https://api.channels.slack.com/apps.
  2. Socket Mode → toggle on. Then go to Basic InformationApp-Level TokensGenerate Token and Scopes with scope connections:write. Copy the App Token (xapp-...).
  3. OAuth & Permissions → add bot token scopes (use the manifest below). Click Install to Workspace. Copy the Bot User OAuth Token (xoxb-...).
  4. Optional: OAuth & Permissions → add User Token Scopes (see the read-only list below). Reinstall the app and copy the User OAuth Token (xoxp-...).
  5. Event Subscriptions → enable events and subscribe to:
    • message.* (includes edits/deletes/thread broadcasts)
    • app_mention
    • reaction_added, reaction_removed
    • member_joined_channel, member_left_channel
    • channel_rename
    • pin_added, pin_removed
  6. Invite the bot to channels you want it to read.
  7. Slash Commands → create /clawd if you use channels.slack.slashCommand. If you enable native commands, add one slash command per built-in command (same names as /help). Native defaults to off for Slack unless you set channels.slack.commands.native: true (global commands.native is "auto" which leaves Slack off).
  8. App Home → enable the Messages Tab so users can DM the bot.
Use the manifest below so scopes and events stay in sync. Multi-account support: use channels.slack.accounts with per-account tokens and optional name. See gateway/configuration for the shared pattern.

Clawdia config (minimal)

Set tokens via env vars (recommended):
  • SLACK_APP_TOKEN=xapp-...
  • SLACK_BOT_TOKEN=xoxb-...
Or via config:
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-..."
    }
  }
}

User token (optional)

Clawdia can use a Slack user token (xoxp-...) for read operations (history, pins, reactions, emoji, member info). By default this stays read-only: reads prefer the user token when present, and writes still use the bot token unless you explicitly opt in. Even with userTokenReadOnly: false, the bot token stays preferred for writes when it is available. User tokens are configured in the config file (no env var support). For multi-account, set channels.slack.accounts.<id>.userToken. Example with bot + app + user tokens:
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
      userToken: "xoxp-..."
    }
  }
}
Example with userTokenReadOnly explicitly set (allow user token writes):
{
  channels: {
    slack: {
      enabled: true,
      appToken: "xapp-...",
      botToken: "xoxb-...",
      userToken: "xoxp-...",
      userTokenReadOnly: false
    }
  }
}

Token usage

  • Read operations (history, reactions list, pins list, emoji list, member info, search) prefer the user token when configured, otherwise the bot token.
  • Write operations (send/edit/delete messages, add/remove reactions, pin/unpin, file uploads) use the bot token by default. If userTokenReadOnly: false and no bot token is available, Clawdia falls back to the user token.

History context

  • channels.slack.historyLimit (or channels.slack.accounts.*.historyLimit) controls how many recent channel/group messages are wrapped into the prompt.
  • Falls back to messages.groupChat.historyLimit. Set 0 to disable (default 50).

HTTP mode (Events API)

Use HTTP webhook mode when your Gateway is reachable by Slack over HTTPS (typical for server deployments). HTTP mode uses the Events API + Interactivity + Slash Commands with a shared request URL.

Setup

  1. Create a Slack app and disable Socket Mode (optional if you only use HTTP).
  2. Basic Information → copy the Signing Secret.
  3. OAuth & Permissions → install the app and copy the Bot User OAuth Token (xoxb-...).
  4. Event Subscriptions → enable events and set the Request URL to your gateway webhook path (default /slack/events).
  5. Interactivity & Shortcuts → enable and set the same Request URL.
  6. Slash Commands → set the same Request URL for your command(s).
Example request URL: https://gateway-host/slack/events

Clawdia config (minimal)

{
  channels: {
    slack: {
      enabled: true,
      mode: "http",
      botToken: "xoxb-...",
      signingSecret: "your-signing-secret",
      webhookPath: "/slack/events"
    }
  }
}
Multi-account HTTP mode: set channels.slack.accounts.<id>.mode = "http" and provide a unique webhookPath per account so each Slack app can point to its own URL.

Manifest (optional)

Use this Slack app manifest to create the app quickly (adjust the name/command if you want). Include the user scopes if you plan to configure a user token.
{
  "display_information": {
    "name": "Clawdia",
    "description": "Slack connector for Clawdia"
  },
  "features": {
    "bot_user": {
      "display_name": "Clawdia",
      "always_online": false
    },
    "app_home": {
      "messages_tab_enabled": true,
      "messages_tab_read_only_enabled": false
    },
    "slash_commands": [
      {
        "command": "/clawd",
        "description": "Send a message to Clawdia",
        "should_escape": false
      }
    ]
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "chat:write",
        "channels:history",
        "channels:read",
        "groups:history",
        "groups:read",
        "groups:write",
        "im:history",
        "im:read",
        "im:write",
        "mpim:history",
        "mpim:read",
        "mpim:write",
        "users:read",
        "app_mentions:read",
        "reactions:read",
        "reactions:write",
        "pins:read",
        "pins:write",
        "emoji:read",
        "commands",
        "files:read",
        "files:write"
      ],
      "user": [
        "channels:history",
        "channels:read",
        "groups:history",
        "groups:read",
        "im:history",
        "im:read",
        "mpim:history",
        "mpim:read",
        "users:read",
        "reactions:read",
        "pins:read",
        "emoji:read",
        "search:read"
      ]
    }
  },
  "settings": {
    "socket_mode_enabled": true,
    "event_subscriptions": {
      "bot_events": [
        "app_mention",
        "message.channels",
        "message.groups",
        "message.im",
        "message.mpim",
        "reaction_added",
        "reaction_removed",
        "member_joined_channel",
        "member_left_channel",
        "channel_rename",
        "pin_added",
        "pin_removed"
      ]
    }
  }
}
If you enable native commands, add one slash_commands entry per command you want to expose (matching the /help list). Override with channels.slack.commands.native.

Scopes (current vs optional)

Slack’s Conversations API is type-scoped: you only need the scopes for the conversation types you actually touch (channels, groups, im, mpim). See https://api.channels.slack.com/docs/conversations-api for the overview.

Bot token scopes (required)

User token scopes (optional, read-only by default)

Add these under User Token Scopes if you configure channels.slack.userToken.
  • channels:history, groups:history, im:history, mpim:history
  • channels:read, groups:read, im:read, mpim:read
  • users:read
  • reactions:read
  • pins:read
  • emoji:read
  • search:read

Not needed today (but likely future)

Config

Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens:
{
  "slack": {
    "enabled": true,
    "botToken": "xoxb-...",
    "appToken": "xapp-...",
    "groupPolicy": "allowlist",
    "dm": {
      "enabled": true,
      "policy": "pairing",
      "allowFrom": ["U123", "U456", "*"],
      "groupEnabled": false,
      "groupChannels": ["G123"],
      "replyToMode": "all"
    },
    "channels": {
      "C123": { "allow": true, "requireMention": true },
      "#general": {
        "allow": true,
        "requireMention": true,
        "users": ["U123"],
        "skills": ["search", "docs"],
        "systemPrompt": "Keep answers short."
      }
    },
    "reactionNotifications": "own",
    "reactionAllowlist": ["U123"],
    "replyToMode": "off",
    "actions": {
      "reactions": true,
      "messages": true,
      "pins": true,
      "memberInfo": true,
      "emojiList": true
    },
    "slashCommand": {
      "enabled": true,
      "name": "clawd",
      "sessionPrefix": "slack:slash",
      "ephemeral": true
    },
    "textChunkLimit": 4000,
    "mediaMaxMb": 20
  }
}
Tokens can also be supplied via env vars:
  • SLACK_BOT_TOKEN
  • SLACK_APP_TOKEN
Ack reactions are controlled globally via messages.ackReaction + messages.ackReactionScope. Use messages.removeAckAfterReply to clear the ack reaction after the bot replies.

Limits

  • Outbound text is chunked to channels.slack.textChunkLimit (default 4000).
  • Optional newline chunking: set channels.slack.chunkMode="newline" to split on each line before length chunking.
  • Media uploads are capped by channels.slack.mediaMaxMb (default 20).

Reply threading

By default, Clawdia replies in the main channel. Use channels.slack.replyToMode to control automatic threading:
ModeBehavior
offDefault. Reply in main channel. Only thread if the triggering message was already in a thread.
firstFirst reply goes to thread (under the triggering message), subsequent replies go to main channel. Useful for keeping context visible while avoiding thread clutter.
allAll replies go to thread. Keeps conversations contained but may reduce visibility.
The mode applies to both auto-replies and agent tool calls (slack sendMessage).

Per-chat-type threading

You can configure different threading behavior per chat type by setting channels.slack.replyToModeByChatType:
{
  channels: {
    slack: {
      replyToMode: "off",        // default for channels
      replyToModeByChatType: {
        direct: "all",           // DMs always thread
        group: "first"           // group DMs/MPIM thread first reply
      },
    }
  }
}
Supported chat types:
  • direct: 1:1 DMs (Slack im)
  • group: group DMs / MPIMs (Slack mpim)
  • channel: standard channels (public/private)
Precedence:
  1. replyToModeByChatType.<chatType>
  2. replyToMode
  3. Provider default (off)
Legacy channels.slack.dm.replyToMode is still accepted as a fallback for direct when no chat-type override is set. Examples: Thread DMs only:
{
  channels: {
    slack: {
      replyToMode: "off",
      replyToModeByChatType: { direct: "all" }
    }
  }
}
Thread group DMs but keep channels in the root:
{
  channels: {
    slack: {
      replyToMode: "off",
      replyToModeByChatType: { group: "first" }
    }
  }
}
Make channels thread, keep DMs in the root:
{
  channels: {
    slack: {
      replyToMode: "first",
      replyToModeByChatType: { direct: "off", group: "off" }
    }
  }
}

Manual threading tags

For fine-grained control, use these tags in agent responses:
  • [[reply_to_current]] — reply to the triggering message (start/continue thread).
  • [[reply_to:<id>]] — reply to a specific message id.

Sessions + routing

  • DMs share the main session (like WhatsApp/Telegram).
  • Channels map to agent:<agentId>:slack:channel:<channelId> sessions.
  • Slash commands use agent:<agentId>:slack:slash:<userId> sessions (prefix configurable via channels.slack.slashCommand.sessionPrefix).
  • If Slack doesn’t provide channel_type, Clawdia infers it from the channel ID prefix (D, C, G) and defaults to channel to keep session keys stable.
  • Native command registration uses commands.native (global default "auto" → Slack off) and can be overridden per-workspace with channels.slack.commands.native. Text commands require standalone /... messages and can be disabled with commands.text: false. Slack slash commands are managed in the Slack app and are not removed automatically. Use commands.useAccessGroups: false to bypass access-group checks for commands.
  • Full command list + config: Slash commands

DM security (pairing)

  • Default: channels.slack.dm.policy="pairing" — unknown DM senders get a pairing code (expires after 1 hour).
  • Approve via: clawdia pairing approve slack <code>.
  • To allow anyone: set channels.slack.dm.policy="open" and channels.slack.dm.allowFrom=["*"].
  • channels.slack.dm.allowFrom accepts user IDs, @handles, or emails (resolved at startup when tokens allow). The wizard accepts usernames and resolves them to ids during setup when tokens allow.

Group policy

  • channels.slack.groupPolicy controls channel handling (open|disabled|allowlist).
  • allowlist requires channels to be listed in channels.slack.channels.
  • If you only set SLACK_BOT_TOKEN/SLACK_APP_TOKEN and never create a channels.slack section, the runtime defaults groupPolicy to open. Add channels.slack.groupPolicy, channels.defaults.groupPolicy, or a channel allowlist to lock it down.
  • The configure wizard accepts #channel names and resolves them to IDs when possible (public + private); if multiple matches exist, it prefers the active channel.
  • On startup, Clawdia resolves channel/user names in allowlists to IDs (when tokens allow) and logs the mapping; unresolved entries are kept as typed.
  • To allow no channels, set channels.slack.groupPolicy: "disabled" (or keep an empty allowlist).
Channel options (channels.slack.channels.<id> or channels.slack.channels.<name>):
  • allow: allow/deny the channel when groupPolicy="allowlist".
  • requireMention: mention gating for the channel.
  • allowBots: allow bot-authored messages in this channel (default: false).
  • users: optional per-channel user allowlist.
  • skills: skill filter (omit = all skills, empty = none).
  • systemPrompt: extra system prompt for the channel (combined with topic/purpose).
  • enabled: set false to disable the channel.

Delivery targets

Use these with cron/CLI sends:
  • user:<id> for DMs
  • channel:<id> for channels

Tool actions

Slack tool actions can be gated with channels.slack.actions.*:
Action groupDefaultNotes
reactionsenabledReact + list reactions
messagesenabledRead/send/edit/delete
pinsenabledPin/unpin/list
memberInfoenabledMember info
emojiListenabledCustom emoji list

Security notes

  • Writes default to the bot token so state-changing actions stay scoped to the app’s bot permissions and identity.
  • Setting userTokenReadOnly: false allows the user token to be used for write operations when a bot token is unavailable, which means actions run with the installing user’s access. Treat the user token as highly privileged and keep action gates and allowlists tight.
  • If you enable user-token writes, make sure the user token includes the write scopes you expect (chat:write, reactions:write, pins:write, files:write) or those operations will fail.

Notes

  • Mention gating is controlled via channels.slack.channels (set requireMention to true); agents.list[].groupChat.mentionPatterns (or messages.groupChat.mentionPatterns) also count as mentions.
  • Multi-agent override: set per-agent patterns on agents.list[].groupChat.mentionPatterns.
  • Reaction notifications follow channels.slack.reactionNotifications (use reactionAllowlist with mode allowlist).
  • Bot-authored messages are ignored by default; enable via channels.slack.allowBots or channels.slack.channels.<id>.allowBots.
  • Warning: If you allow replies to other bots (channels.slack.allowBots=true or channels.slack.channels.<id>.allowBots=true), prevent bot-to-bot reply loops with requireMention, channels.slack.channels.<id>.users allowlists, and/or clear guardrails in AGENTS.md and SOUL.md.
  • For the Slack tool, reaction removal semantics are in /tools/reactions.
  • Attachments are downloaded to the media store when permitted and under the size limit.