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
limitparameter to control page size (max 200) - Use
typesparameter 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
limitparameter to control page size (max 200) - Use
typesparameter to specify conversation types:public_channel,private_channel,mpim,im - By default, only public channels are returned
- Paginate using cursor from
response_metadata.next_cursoruntil 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_countfield 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 > 0on 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 DMstext: Message contentas_user: Controls message sendertrue(default choice) - Message appears from the authenticated userfalse- 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 Upload 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:
- Start with initial request (no cursor)
- Search current page for matching channel name
- Return immediately if found (don't fetch remaining pages)
- Extract
next_cursorfromresponse_metadata - Continue until channel found or cursor is empty/undefined
- Include
typesparameter 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:
- Start with initial request (no cursor)
- Append results to collection
- Extract
next_cursorfromresponse_metadata - Continue until cursor is empty/undefined
- Include
typesparameter 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:
- Fetch messages using
conversations.history - Check each message for
reply_count > 0 - For messages with replies, call
conversations.replieswith the messagets(timestamp) - Skip the first message in replies array (it's the parent message)
- Process remaining messages as thread replies