Agent MessengerAgent Messenger
TypeScript SDK

Slack

TypeScript SDK reference for Slack — client, real-time events, and types.

Installation

npm install agent-messenger
import { SlackClient, SlackListener, SlackError, SlackCredentialManager } from 'agent-messenger/slack'

SlackClient

The main client for interacting with Slack's API programmatically. Most API methods include automatic rate-limit retry with exponential backoff.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })

Or use automatic credential extraction — credentials are extracted from the desktop app, no manual token needed:

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login()

Authentication

// Verify credentials and get identity
const auth = await client.testAuth()
// → { user_id: string, team_id: string, user?: string, team?: string }

Messages

// Send a message
const msg = await client.sendMessage(channel, 'Hello world')
// → SlackMessage { ts, text, type, user, thread_ts }

// Send a threaded reply
const reply = await client.sendMessage(channel, 'Reply', threadTs)

// Get messages from a channel
const messages = await client.getMessages(channel, 20)
const filtered = await client.getMessages(channel, { limit: 50, oldest: '1700000000.000000' })
// → SlackMessage[]

// Get a single message by timestamp
const message = await client.getMessage(channel, ts)
// → SlackMessage | null

// Get thread replies with pagination
const thread = await client.getThreadReplies(channel, threadTs, { limit: 50 })
// → { messages: SlackMessage[], has_more: boolean, next_cursor?: string }

// Update a message
const updated = await client.updateMessage(channel, ts, 'New text')

// Delete a message
await client.deleteMessage(channel, ts)

// Post an ephemeral message (visible to one user only)
const ephemeralTs = await client.postEphemeral(channel, userId, 'Only you see this')

// Get a permalink to a message
const url = await client.getPermalink(channel, ts)

// Search messages across workspace
const results = await client.searchMessages('keyword', {
  sort: 'timestamp', // 'score' | 'timestamp'
  sortDir: 'desc', // 'asc' | 'desc'
  count: 20,
})
// → SlackSearchResult[]

Scheduled Messages

// Schedule a message (postAt is a Unix timestamp)
const scheduled = await client.scheduleMessage(channel, 'Future message', 1700000000)
const scheduled = await client.scheduleMessage(channel, 'Thread reply', 1700000000, threadTs)
// → SlackScheduledMessage { id, channel_id, post_at, date_created, text }

// List scheduled messages
const list = await client.listScheduledMessages()
const list = await client.listScheduledMessages(channel) // filter by channel
// → SlackScheduledMessage[]

// Cancel a scheduled message
await client.deleteScheduledMessage(channel, scheduledMessageId)

Channels

// List all channels (auto-paginates)
const channels = await client.listChannels()
// → SlackChannel[]

// List DM conversations
const dms = await client.listDMs({ includeArchived: false })
// → SlackDM[]

// Get channel info
const channel = await client.getChannel(channelId)
// → SlackChannel { id, name, is_private, is_archived, topic, purpose, ... }

// Resolve channel name to ID (passes IDs through unchanged)
const id = await client.resolveChannel('general')
const id = await client.resolveChannel('C0ABC123') // returned as-is

// Open a DM (or group DM) — returns existing channel if already open
const dm = await client.openConversation('U0ABC123')
const groupDm = await client.openConversation('U0ABC123,U0DEF456')
// → { channel_id: string, already_open: boolean }

// List members of a channel (auto-paginates)
const memberIds = await client.listChannelMembers(channelId)
// → string[]

// Create a channel
const newChannel = await client.createChannel('project-alpha')
const privateChannel = await client.createChannel('secret', true)
// → SlackChannel

// Archive a channel
await client.archiveChannel(channelId)

// Set topic / purpose
await client.setChannelTopic(channelId, 'New topic')
await client.setChannelPurpose(channelId, 'New purpose')

// Invite users (comma-separated IDs)
await client.inviteToChannel(channelId, 'U0ABC123,U0DEF456')

// Join / leave
await client.joinChannel(channelId)
await client.leaveChannel(channelId)

Users

// List all users (auto-paginates)
const users = await client.listUsers()
// → SlackUser[]

// Get user info
const user = await client.getUser(userId)
// → SlackUser { id, name, real_name, is_admin, is_bot, profile, ... }

// Look up user by email
const user = await client.lookupUserByEmail('alice@example.com')

// Get detailed user profile
const profile = await client.getUserProfile(userId)
// → SlackUserProfile { display_name, status_text, status_emoji, email, ... }

// Set user profile (status, etc.)
// Note: SDK requires colon-wrapped emoji (e.g., ':calendar:').
// The CLI `set-status` command auto-wraps, so you pass just the name.
await client.setUserProfile({
  status_text: 'In a meeting',
  status_emoji: ':calendar:',
  status_expiration: 1700100000,
})

Reactions

await client.addReaction(channel, ts, 'thumbsup')
await client.removeReaction(channel, ts, 'thumbsup')

Files

// Upload a file
const file = await client.uploadFile([channelId], buffer, 'report.pdf')
// → SlackFile { id, name, title, mimetype, size, url_private, ... }

// List files
const files = await client.listFiles()
const files = await client.listFiles(channelId)
// → SlackFile[]

// Get file metadata
const info = await client.getFileInfo(fileId)

// Download a file
const { buffer, file } = await client.downloadFile(fileId)

// Delete a file
await client.deleteFile(fileId)

Pins

await client.pinMessage(channel, ts)
await client.unpinMessage(channel, ts)

const pins = await client.listPins(channel)
// → SlackPin[] { channel, message, date_created, created_by }

Bookmarks

// Add a bookmark
const bookmark = await client.addBookmark(channel, 'Docs', 'https://docs.example.com', {
  type: 'link',
  emoji: ':books:',
})
// → SlackBookmark { id, channel_id, title, link, emoji, type, ... }

// Edit a bookmark
const updated = await client.editBookmark(channel, bookmarkId, {
  title: 'New Title',
  link: 'https://new.url',
})

// Remove a bookmark
await client.removeBookmark(channel, bookmarkId)

// List bookmarks
const bookmarks = await client.listBookmarks(channel)
// → SlackBookmark[]

Reminders

// Add a reminder (time is a Unix timestamp)
const reminder = await client.addReminder('Review PR', 1700000000)
const reminder = await client.addReminder('Submit report', 1700000000, { user: 'U0ABC123' })
// → SlackReminder { id, creator, text, user, recurring, time, complete_ts }

// List all reminders
const reminders = await client.listReminders()
// → SlackReminder[]

// Complete / delete
await client.completeReminder(reminderId)
await client.deleteReminder(reminderId)

User Groups

// List all user groups
const usergroups = await client.listUsergroups()
const usergroups = await client.listUsergroups({
  includeDisabled: true,
  includeUsers: true,
  includeCount: true,
})
// → SlackUsergroup[]

// Create a user group
const group = await client.createUsergroup('Marketing Team', {
  handle: 'marketing-team',
  description: 'Marketing gurus',
  channels: ['C012ABC'],
})
// → SlackUsergroup

// Update a user group (name, handle, description, channels)
const updated = await client.updateUsergroup('S0616NG6M', {
  name: 'New Name',
  handle: 'new-handle',
  description: 'Updated description',
  channels: ['C012ABC', 'C034DEF'],
})
// → SlackUsergroup

// Enable / disable a user group
const enabled = await client.enableUsergroup('S0616NG6M')
const disabled = await client.disableUsergroup('S0616NG6M')
// → SlackUsergroup

// List members of a user group
const members = await client.listUsergroupMembers('S0616NG6M')
const members = await client.listUsergroupMembers('S0616NG6M', { includeDisabled: true })
// → string[] (user IDs)

// Update members (replaces all members)
const result = await client.updateUsergroupMembers('S0616NG6M', ['U060R4BJ4', 'U060RNRCZ'])
// → SlackUsergroup

Emoji

// List all custom emoji in workspace
const emoji = await client.listEmoji()
// → Record<string, string>  (name → URL or alias)

Unread & Activity

// Get unread counts for all channels
const counts = await client.getUnreadCounts()
// → SlackUnreadCounts { channels, total_unread, total_mentions }

// Get thread subscription details
const view = await client.getThreadView(channelId, threadTs)
// → SlackThreadView { unread_count, last_read, subscribed }

// Mark channel as read
await client.markRead(channelId, ts)

// Get activity feed (mentions, reactions, replies)
const activity = await client.getActivityFeed({
  types: 'thread_reply,message_reaction',
  limit: 50,
})
// → SlackActivityItem[]

Saved Items, Drafts & Sections

// Get saved items (paginated)
const saved = await client.getSavedItems(cursor)
// → { items: SlackSavedItem[], has_more: boolean, next_cursor?: string }

// Get drafts (paginated)
const drafts = await client.getDrafts(cursor)
// → { drafts: SlackDraft[], next_cursor?: string }

// Get sidebar channel sections
const sections = await client.getChannelSections()
// → SlackChannelSection[] { id, name, channel_ids }

RTM Connection

// Low-level RTM connect (used internally by SlackListener)
const rtm = await client.rtmConnect()
// → { url: string, cookie: string, self: { id }, team: { id } }

Real-Time Events

SlackListener connects to Slack's RTM WebSocket for instant event streaming.

import { SlackClient, SlackListener } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })
const listener = new SlackListener(client)

listener.on('message', (event) => {
  console.log(`#${event.channel} <${event.user}>: ${event.text}`)
})

listener.on('reaction_added', (event) => {
  console.log(`:${event.reaction}: by ${event.user}`)
})

// Catch-all for any RTM event
listener.on('slack_event', (event) => {
  console.log(event.type, event)
})

listener.on('error', (err) => console.error(err))

await listener.start() // connects via RTM WebSocket
// listener.stop()      // clean shutdown

Available Events

EventDescription
messageNew message, edit, delete, thread reply
reaction_added / reaction_removedReaction changes
member_joined_channel / member_left_channelMember changes
user_typingTyping indicator
presence_changeUser active/away status
channel_created / channel_deleted / channel_rename / channel_archive / channel_unarchiveChannel lifecycle
slack_eventCatch-all for every RTM event
connected / disconnectedConnection lifecycle
errorConnection or API error

Features:

  • No channel subscription needed — receives all workspace events
  • Auto-reconnect with exponential backoff
  • Ping/pong keepalive

Types

import type {
  SlackChannel,
  SlackMessage,
  SlackUser,
  SlackReaction,
  SlackFile,
  SlackSearchResult,
  SlackUnreadCounts,
  SlackThreadView,
  SlackSavedItem,
  SlackActivityItem,
  SlackDM,
  SlackDraft,
  SlackChannelSection,
  SlackPin,
  SlackBookmark,
  SlackScheduledMessage,
  SlackReminder,
  SlackUserProfile,
  SlackUsergroup,
  SlackRTMEvent,
  SlackRTMMessageEvent,
  SlackRTMReactionEvent,
  SlackRTMMemberEvent,
  SlackRTMChannelEvent,
  SlackRTMPresenceEvent,
  SlackRTMUserTypingEvent,
  SlackRTMGenericEvent,
  SlackListenerEventMap,
  WorkspaceCredentials,
  Config,
} from 'agent-messenger/slack'

Zod Schemas

Runtime-validated schemas are also exported for parsing API responses:

import {
  SlackChannelSchema,
  SlackReactionSchema,
  SlackFileSchema,
  SlackMessageSchema,
  SlackUserSchema,
  SlackUsergroupSchema,
  WorkspaceCredentialsSchema,
  ConfigSchema,
} from 'agent-messenger/slack'

Examples

Deploy Notifier

Post a deployment message, update it with progress in a thread, and mark it complete with a reaction.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login()
const channel = await client.resolveChannel('deploys')

// Post parent message
const msg = await client.sendMessage(channel, '🚀 Deploying v2.1.0 to production...')
await client.addReaction(channel, msg.ts, 'hourglass_flowing_sand')

// Stream progress in thread
await client.sendMessage(channel, '• Building application...', msg.ts)
await client.sendMessage(channel, '• Running tests (142 passed)...', msg.ts)
await client.sendMessage(channel, '• Rolling out to 3 regions...', msg.ts)

// Finalize
await client.removeReaction(channel, msg.ts, 'hourglass_flowing_sand')
await client.addReaction(channel, msg.ts, 'white_check_mark')
await client.updateMessage(channel, msg.ts, '✅ v2.1.0 deployed to production')

Project Channel Setup

Create a channel, configure it, invite the team, and pin a welcome message — all in one script.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })

// Create and configure
const channel = await client.createChannel('project-alpha')
await client.setChannelTopic(channel.id, 'Project Alpha — launch Q1 2026')
await client.setChannelPurpose(channel.id, 'Coordinate development for Alpha release')

// Invite team
await client.inviteToChannel(channel.id, 'U001,U002,U003')

// Add resources
await client.addBookmark(channel.id, 'Project Wiki', 'https://wiki.example.com/alpha', {
  emoji: ':books:',
})
await client.addBookmark(channel.id, 'Figma Designs', 'https://figma.com/file/abc', {
  emoji: ':art:',
})

// Pin welcome message
const welcome = await client.sendMessage(
  channel.id,
  [
    'Welcome to *Project Alpha*! 🎉',
    '',
    '📋 *Resources* — check the bookmarks above',
    '🧵 *Discussions* — use threads to keep things organized',
    '📌 *Decisions* — pin important messages',
  ].join('\n'),
)
await client.pinMessage(channel.id, welcome.ts)

Real-Time Bot

Listen for messages mentioning @bot and reply in thread. Uses SlackListener for instant event streaming — no polling.

import { SlackClient, SlackListener } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })
const listener = new SlackListener(client)

const auth = await client.testAuth()

listener.on('message', async (event) => {
  // Ignore own messages, edits, and non-text
  if (event.user === auth.user_id || event.subtype || !event.text) return

  if (event.text.includes(`<@${auth.user_id}>`)) {
    const threadTs = event.thread_ts || event.ts
    await client.sendMessage(event.channel, 'On it! 👀', threadTs)
    await client.addReaction(event.channel, event.ts, 'eyes')
  }
})

listener.on('reaction_added', async (event) => {
  if (event.reaction === 'ticket' && event.user !== auth.user_id) {
    await client.sendMessage(event.item.channel, `Ticket created for <@${event.user}>'s message.`, event.item.ts)
  }
})

listener.on('connected', (info) => {
  console.log(`Bot online as ${info.self.id}`)
})

await listener.start()

Morning Digest

Summarize unread activity and post a digest to a channel.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })
const channel = await client.resolveChannel('team-pulse')

// Gather data
const counts = await client.getUnreadCounts()
const activity = await client.getActivityFeed({ types: 'at_user,at_channel', limit: 10 })
const reminders = await client.listReminders()
const pending = reminders.filter((r) => r.complete_ts === 0)

// Build digest
const lines = [
  `📊 *Morning Digest* — ${new Date().toLocaleDateString()}`,
  '',
  `📬 *${counts.total_unread}* unread across *${counts.channels.filter((c) => c.unread_count > 0).length}* channels`,
  `🔔 *${counts.total_mentions}* mentions`,
]

if (activity.length > 0) {
  lines.push('', '*Recent mentions:*')
  for (const item of activity.slice(0, 5)) {
    lines.push(`• <#${item.channel}> — ${item.text.slice(0, 80)}`)
  }
}

if (pending.length > 0) {
  lines.push('', `⏰ *${pending.length}* pending reminders`)
  for (const r of pending.slice(0, 3)) {
    lines.push(`• ${r.text}`)
  }
}

await client.sendMessage(channel, lines.join('\n'))

Search for urgent messages, acknowledge with a reaction, and schedule a follow-up.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })

// Find urgent messages from the last day
const results = await client.searchMessages('urgent OR blocker', {
  sort: 'timestamp',
  sortDir: 'desc',
  count: 10,
})

for (const result of results) {
  // Acknowledge
  await client.addReaction(result.channel.id, result.ts, 'eyes')

  // Reply in thread
  await client.sendMessage(result.channel.id, 'Flagged for review — will follow up shortly.', result.ts)

  // Schedule a follow-up reminder
  const inOneHour = Math.floor(Date.now() / 1000) + 3600
  await client.addReminder(
    `Follow up on urgent message in #${result.channel.name}: "${result.text.slice(0, 50)}"`,
    inOneHour,
  )
}

File Pipeline

Download files from one channel and re-upload to another with a summary.

import { SlackClient } from 'agent-messenger/slack'

const client = await new SlackClient().login({ token, cookie })

const source = await client.resolveChannel('design-uploads')
const target = await client.resolveChannel('design-archive')

// Get recent files
const files = await client.listFiles(source)

for (const file of files.slice(0, 5)) {
  // Download
  const { buffer } = await client.downloadFile(file.id)

  // Re-upload to archive
  const uploaded = await client.uploadFile([target], buffer, file.name)

  await client.sendMessage(
    target,
    [
      `📎 Archived *${file.name}* from <#${source}>`,
      `Original uploader: <@${file.user}> · ${(file.size / 1024).toFixed(0)} KB`,
    ].join('\n'),
  )
}

On this page