conductorv2

Integrations

Slack

Connect Conductor to Slack to send messages, manage channels, list users, upload files, and react to messages. Covers bot tokens, user tokens, OAuth scopes, Socket Mode for local dev, and every common error you'll hit.

Authentication

Slack supports two token types. For Conductor, bot tokens are almost always the right choice. User tokens act on behalf of a specific person and are better suited to personal automation.

Bot Token (xoxb-)

Issued to the Slack App itself. Acts as the bot user. Scoped to what the app is allowed to do. Recommended for shared workflows, automations, and production integrations. Stays valid until revoked.

User Token (xoxp-)

Acts as a specific Slack user. Can read DMs and private channels that user has access to. Useful for personal scripts. Gets revoked when the user logs out or changes password. Less preferred for production.

Creating a Slack App

  1. 01Go to api.slack.com/apps and click Create New App.
  2. 02Choose From scratch. Give it a name and pick your workspace.
  3. 03Under OAuth & Permissions, scroll to Bot Token Scopes and add the scopes you need (see table below).
  4. 04Click Install to Workspace. Authorize on the OAuth screen.
  5. 05Copy the Bot User OAuth Token (starts with xoxb-). This goes in your Conductor config.
  6. 06Under Basic Information → App Credentials, copy your Signing Secret.

You can customize the bot's display name and icon under App Home → Your App's Presence in Slack. The display name is what appears in channels when the bot posts messages.

OAuth Scopes

Add these under OAuth & Permissions → Bot Token Scopes. Start with the minimum you need — Slack lets you add scopes later (but requires reinstalling the app after changes).

ScopeWhat it allows
channels:readList public channels and see channel info
channels:historyRead messages in public channels the bot has joined
channels:joinAllow bot to join public channels
chat:writeSend messages as the bot
chat:write.publicSend messages to channels the bot hasn't joined
users:readView basic user info (name, email with users:read.email)
users:read.emailView email addresses of users
files:readRead files shared in channels the bot can access
files:writeUpload, edit, and delete files
reactions:readView emoji reactions and their authors
reactions:writeAdd and remove emoji reactions
im:readView direct messages that the bot is in
im:writeStart direct messages with people
im:historyRead messages in direct message conversations
groups:readView basic info about private channels the bot is in
groups:historyRead messages in private channels the bot has joined
mpim:readView basic info about group direct messages the bot is in
mpim:historyRead messages in group direct messages

After adding scopes, always click Reinstall to Workspace. Scopes only take effect after reinstall.

Socket Mode (local dev)

Socket Mode lets your app receive events over a persistent WebSocket connection instead of requiring a public HTTPS URL. This is ideal for local development or environments behind a firewall. To enable it, go to Socket Mode in your app settings, toggle it on, and generate an App-Level Token with the connections:write scope. App-level tokens start with xapp-.

conductor.json — socket mode
{
  "plugins": {
    "slack": {
      "token": "xoxb-your-bot-token-here",
      "app_token": "xapp-1-your-app-level-token",
      "socket_mode": true
    }
  }
}

Event subscriptions still need to be configured in your app settings under Event Subscriptions, even when using Socket Mode. Subscribe to events like message.channels, message.im, and app_mention.

Conductor config

conductor.json
{
  "plugins": {
    "slack": {
      "token": "xoxb-your-bot-token-here",
      "signing_secret": "your-signing-secret"
    }
  }
}

Store your bot token in an environment variable and reference it via ${SLACK_BOT_TOKEN} rather than hardcoding it. Never commit tokens to source control.

Available tools

ToolDescription
slack.message.sendPost a message to a channel or DM. Supports plain text and Block Kit blocks.
slack.message.listFetch recent messages from a channel. Supports pagination and time-range filtering.
slack.channel.listList all public (and optionally private) channels in the workspace.
slack.channel.createCreate a new channel. Bot must have channels:manage scope.
slack.channel.archiveArchive an existing channel. Requires channels:manage scope.
slack.user.listList all users in the workspace including bots and deactivated accounts.
slack.user.infoGet detailed profile info for a specific user by their user ID.
slack.file.uploadUpload a file and optionally share it to one or more channels.
slack.reaction.addAdd an emoji reaction to a specific message by channel and timestamp.
slack.thread.replyReply to a message in its thread using the parent message's timestamp.

Usage examples

Sending messages

slack.message.send
// Send a message to a channel
slack.message.send({
  channel: "C0123456789",   // channel ID, not name
  text: "Hello from Conductor!"
})

// Send with blocks (Block Kit)
slack.message.send({
  channel: "C0123456789",
  text: "Fallback text",
  blocks: [
    {
      type: "section",
      text: { type: "mrkdwn", text: "*Hello!* This is a block message." }
    }
  ]
})

Replying to threads

slack.thread.reply
// Reply in a thread
slack.thread.reply({
  channel: "C0123456789",
  thread_ts: "1683000000.123456",  // timestamp of the parent message
  text: "This is a thread reply"
})

Uploading files

slack.file.upload
// Upload a file to a channel
slack.file.upload({
  channels: ["C0123456789"],
  content: "File contents here as a string",
  filename: "report.txt",
  title: "My Report"
})

Adding reactions

slack.reaction.add
// Add a reaction to a message
slack.reaction.add({
  channel: "C0123456789",
  timestamp: "1683000000.123456",
  name: "thumbsup"   // emoji name without colons
})

Finding channel and workspace IDs

Slack's API always uses IDs, never names. Channel names can change; IDs cannot.

finding IDs
// Channel IDs look like: C0123456789
// To find a channel ID in Slack:
// 1. Right-click the channel name → "Copy link"
//    The link ends in /C0123456789 — that's the ID
// 2. Or: channel details → scroll to bottom → "Channel ID"
//
// Workspace ID (team ID) looks like: T0123456789
// Settings & administration → Workspace settings
// The URL contains /T0123456789

Rate limits

Slack uses a tiered rate limit system. Each API method belongs to a tier. When you exceed a tier's limit, the API returns HTTP 429 with a Retry-After header specifying seconds to wait.

TierRate limitExample methods
Tier 1~1 req/minfiles.list, conversations.list, users.list
Tier 2~20 req/minconversations.history, conversations.replies
Tier 3~50 req/minchat.postMessage, reactions.add
Tier 4~100 req/minauth.test, users.info
Special1 msg/sec per channelchat.postMessage (per channel limit)

Common errors

channel_not_found

Channel ID is wrong or bot can't see the channel

Fix: Verify the channel ID. Make sure it's a channel ID (C...) not a name.

not_in_channel

Bot is not a member of the channel

Fix: Invite the bot: /invite @YourBotName in the channel, or add channels:join scope.

missing_scope

Token doesn't have the required OAuth scope

Fix: Add the scope in your Slack App settings → OAuth & Permissions, then reinstall.

token_revoked

The token has been revoked by an admin or user

Fix: Reinstall the app to the workspace to get a fresh token.

ratelimited

Too many API requests too quickly

Fix: Respect the Retry-After header. Implement exponential backoff.

invalid_auth

The token format is wrong or the token is invalid

Fix: Check the token starts with xoxb- (bot) or xoxp- (user). No extra spaces.

account_inactive

The Slack account associated with the token is deactivated

Fix: Reauthorize with an active account. For bot tokens, the app itself must be active.

Security best practices

  • Store tokens in environment variables or a secrets manager. Never hardcode them in config files committed to source control.
  • Request only the scopes your integration actually needs. Slack's principle of least privilege reduces blast radius if a token is leaked.
  • Rotate tokens periodically and immediately if you suspect compromise. Revoke old tokens under OAuth & Permissions → Revoke tokens.
  • For production, use a dedicated bot account rather than a personal user token. This way the integration doesn't break if someone leaves the team.
  • Verify the X-Slack-Signature header on incoming webhooks using your signing secret to prevent spoofed requests.