slice icon Context Slice

Slack

Overview

This skill provides instructions for correctly working with Slack operations. It covers searching for specific channels by name, paginating through all channels, fetching messages from channels with pagination, retrieving complete conversation threads including replies, and sending messages with configurable sender options.

Restrictions

  • Cannot reply to system messages (channel purpose and join notifications) as Slack doesn't allow threading on those message types
  • When sending messages, always default to as_user: true (send as the authenticated user) unless the user has specifically requested to send as a bot or has expressed a preference for bot sending
  • Never send raw markdown, use Slack-flavored markdown (mrkdwn) with the Slack Block Kit for beautifully formatted messages

Operations

Search Channels

Find a specific channel by name using conversations.list API endpoint:

  • Paginate through results until the target channel name is found
  • Use limit parameter to control page size (max 200)
  • Use types parameter to specify conversation types: public_channel, private_channel, mpim, im
  • Stop pagination when the channel is found (no need to fetch remaining pages)
  • Return the found channel or indicate if not found after all pages

Paginate Channels

Fetch all channels in a workspace using conversations.list API endpoint:

  • Use limit parameter to control page size (max 200)
  • Use types parameter to specify conversation types: public_channel, private_channel, mpim, im
  • By default, only public channels are returned
  • Paginate using cursor from response_metadata.next_cursor until all channels are retrieved

Fetch Channel Messages

Retrieve messages from a channel using conversations.history API endpoint:

  • Returns messages in reverse chronological order
  • Does NOT include thread replies - these must be fetched separately
  • Check reply_count field to identify messages with threads

Fetch Thread Replies

Retrieve all replies in a thread using conversations.replies API endpoint:

  • Requires channel ID and parent message timestamp (ts)
  • First message in response is the parent message itself (should be skipped)
  • Use when reply_count > 0 on a message

Send Messages

Send messages to channels or DMs using chat.postMessage API endpoint. Default to sending as the user (as_user: true) unless the user explicitly requests to send as a bot. Always use proper formatting - either Block Kit for structured messages or mrkdwn for simple messages. Never send raw markdown strings.

Example 1: Simple Message with mrkdwn

const messageResponse = await fetch('https://slack.com/api/chat.postMessage', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer SLACK_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    channel: channelId,
    text: 'Message text',
    as_user: true  // default to true; only set to false if user requests bot sending
  })
});

Example 2: Structured Message with Block Kit (RECOMMENDED for multi-section messages)

For structured messages with headers, sections, and dividers :

const messageResponse = await fetch('https://slack.com/api/chat.postMessage', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer SLACK_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    channel: channelId,
    text: 'Fallback text for notifications', // required; used as notification text
    blocks: [{
      type: 'header',
      text: {
        type: 'plain_text',
        text: '🚀 Deployment Status Update',
        emoji: true
      }
    },
    {
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: 'Production deployment completed successfully with *zero downtime*.'
      }
    },
    {
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: '*What We Accomplished*\n• Deployed v2.3.0 to production\n• Migrated database schema\n• Updated API endpoints'
      }
    },
    {
      type: 'divider'
    },
    {
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: '*Next Steps*\nMonitor metrics on the <https://dashboard.example.com|production dashboard>.'
      }
    }],
    username: 'Deploy Bot', // custom bot name
    icon_emoji: ':rocket:' // custom bot icon (make sure this emoji exists in their slack or is from the default set)
  })
});

Key parameters:

  • channel: Channel ID or user ID for DMs
  • text: Message content
  • as_user: Controls message sender
    • true (default choice) - Message appears from the authenticated user
    • false - Message appears from the bot/app (only use when explicitly requested by user)

Send Files

Upload files and images to channels using files.upload API endpoint.

When the user needs to send/upload any file type (images, documents, archives, videos, etc.) to a Slack channel, read sliceUpload Files to Slack to get complete information on how to send files.

Workflows

Searching for a Specific Channel

When searching for a specific channel by name, paginate through results until found:

async function findChannel(channelName) {
  let cursor = undefined;

  do {
    const url = cursor
      ? `https://slack.com/api/conversations.list?limit=200&cursor=${cursor}&types=public_channel,private_channel,mpim,im`
      : 'https://slack.com/api/conversations.list?limit=200&types=public_channel,private_channel,mpim,im';

    const response = await fetch(url, {
      headers: {
        'Authorization': 'Bearer SLACK_TOKEN',
        'Content-Type': 'application/json'
      }
    });

    const data = await response.json();

    // Search for the channel in current page
    const foundChannel = data.channels.find(ch => ch.name === channelName);
    if (foundChannel) {
      return foundChannel;
    }

    cursor = data.response_metadata?.next_cursor;
  } while (cursor);

  return null; // Channel not found
}

Critical steps:

  1. Start with initial request (no cursor)
  2. Search current page for matching channel name
  3. Return immediately if found (don't fetch remaining pages)
  4. Extract next_cursor from response_metadata
  5. Continue until channel found or cursor is empty/undefined
  6. Include types parameter to search all conversation types

Paginating Through All Channels

When fetching Slack channels, always paginate through all results to avoid missing channels:

let allChannels = [];
let cursor = undefined;

do {
  const url = cursor
    ? `https://slack.com/api/conversations.list?limit=200&cursor=${cursor}&types=public_channel,private_channel,mpim,im`
    : 'https://slack.com/api/conversations.list?limit=200&types=public_channel,private_channel,mpim,im';

  const response = await fetch(url, {
    headers: {
      'Authorization': 'Bearer SLACK_TOKEN',
      'Content-Type': 'application/json'
    }
  });

  const data = await response.json();
  allChannels = allChannels.concat(data.channels);
  cursor = data.response_metadata?.next_cursor;
} while (cursor);

Critical steps:

  1. Start with initial request (no cursor)
  2. Append results to collection
  3. Extract next_cursor from response_metadata
  4. Continue until cursor is empty/undefined
  5. Include types parameter to get all conversation types (public channels, private channels, multi-party DMs, and direct messages)

Fetching Complete Conversation History with Threads

To get the full conversation including all thread replies:

// Step 1: Fetch channel messages
const messagesResponse = await fetch(
  `https://slack.com/api/conversations.history?channel=${channelId}&limit=200`,
  {
    headers: {
      'Authorization': 'Bearer SLACK_TOKEN',
      'Content-Type': 'application/json'
    }
  }
);
const messagesData = await messagesResponse.json();

// Step 2: For each message with replies, fetch the thread
for (const msg of messagesData.messages) {
  if (msg.reply_count && msg.reply_count > 0) {
    const repliesResponse = await fetch(
      `https://slack.com/api/conversations.replies?channel=${channelId}&ts=${msg.ts}&limit=200`,
      {
        headers: {
          'Authorization': 'Bearer SLACK_TOKEN',
          'Content-Type': 'application/json'
        }
      }
    );

    const repliesData = await repliesResponse.json();
    // First message in replies is the parent, skip it
    const threadReplies = repliesData.messages.slice(1);

    // Store or process threadReplies
  }
}

Critical steps:

  1. Fetch messages using conversations.history
  2. Check each message for reply_count > 0
  3. For messages with replies, call conversations.replies with the message ts (timestamp)
  4. Skip the first message in replies array (it's the parent message)
  5. Process remaining messages as thread replies