slackcli conversations covers channels, DMs, and group DMs: listing them,
reading history and threads, fetching one message, and seeing what is unread.
Every subcommand accepts --workspace <id|name>. list, read, get and
unread take --fields with --json
to print only the fields you name, e.g. --json --fields ts,user,text.
conversations list
Section titled “conversations list”slackcli conversations list # every conversation you can seeslackcli conversations list --types=public_channelslackcli conversations list --types=im # DMs onlyslackcli conversations list --limit=200 --exclude-archived
# Machine-readableslackcli conversations list --json| Option | Default | Purpose |
|---|---|---|
--types <types> |
public_channel,private_channel,mpim,im |
Comma-separated conversation types |
--limit <number> |
100 |
How many to return |
--exclude-archived |
off | Skip archived conversations |
--cursor <cursor> |
— | Fetch the next page |
--json |
off | JSON output with a resolved users array |
--fields <list> |
all | With --json, only these comma-separated fields (dot paths allowed) |
DM entries are resolved to the other person’s name. When more results exist, the
exact --cursor command for the next page is printed (human output) or set on
next_cursor (--json; null on the last page).
conversations read
Section titled “conversations read”Read channel history, or one thread.
# Recent messages in a channel (oldest first)slackcli conversations read C1234567890
# One threadslackcli conversations read C1234567890 --thread-ts=1234567890.123456
# The thread a message link points atslackcli conversations read --permalink="https://myteam.slack.com/archives/C1234567890/p1234567890123456"
# Top-level messages onlyslackcli conversations read C1234567890 --exclude-replies
# A time windowslackcli conversations read C1234567890 --oldest=1735689600 --latest=1738368000
# Machine-readableslackcli conversations read C1234567890 --json
# Only what is new since the last poll, minus your own messagesslackcli conversations read C1234567890 --oldest="$CURSOR" --exclude-self --json| Option | Default | Purpose |
|---|---|---|
--thread-ts <ts> |
— | Read a specific thread instead of the channel |
--permalink <url> |
— | Replaces the channel argument and --thread-ts |
--exclude-replies |
off | Drop threaded replies from channel history |
--exclude-self |
off | Drop messages sent by the authenticated user (or, for a bot token, its bot) |
--limit <number> |
100 |
How many messages |
--oldest / --latest |
— | Time range bounds (1234567890.123456, p1234567890123456, or epoch seconds); only messages strictly newer than --oldest are shown |
--json |
off | JSON output, including ts and thread_ts |
--fields <list> |
all | With --json, only these comma-separated fields (dot paths allowed) |
Channel history comes back newest-first from Slack and is reversed so you read
top to bottom. Thread replies are already chronological. --json also includes
reactions, blocks, attachments, file metadata, and a resolved users array.
--json also carries a polling cursor:
next_oldest— the newesttsof the page Slack returned, taken before--exclude-self/--exclude-repliesfilter it. Pass it back as--oldeston the next call. On an empty page it is the--oldestyou passed in (ornullwithout one), and it never moves backwards.has_more— Slack has more messages in the range than--limitreturned.
Slack’s thread API always returns the thread’s parent message, whatever
--oldest says, so read drops every message with ts <= --oldest itself: a
thread poll does not see the parent again. --exclude-self uses the user ID
saved at login; a workspace saved before that, or a bot token (whose bot_id
is not stored), costs one auth.test call. See
Poll a channel or thread safely.
conversations get
Section titled “conversations get”Fetch one message by channel and timestamp.
slackcli conversations get C1234567890 1234567890.123456slackcli conversations get --permalink="https://myteam.slack.com/archives/C1234567890/p1234567890123456"slackcli conversations get C1234567890 p1234567890123456 --jsonAuth-type caveat. With browser auth this resolves both top-level messages and
thread replies. With a standard token it can only resolve top-level messages
— looking up an arbitrary reply needs its parent’s thread_ts, and no public
Slack API returns that from a reply timestamp alone. Read the thread instead:
conversations read <channel> --thread-ts=<parent>.
conversations unread
Section titled “conversations unread”slackcli conversations unreadslackcli conversations unread --types=dms # channels, dms, groupsslackcli conversations unread --jsonConversations with mentions sort first, then alphabetically. --json gives
{ unread_channels: [...] }. When no conversation and no thread is unread the
command prints “All caught up!” on stderr and writes nothing to stdout, even
with --json.
Unread threads. With browser auth the command also says whether the threads you follow have unread replies or mentions, which Slack counts separately from their channel:
🧵 Threads: unread replies, 1 mention{ "unread_channels": [], "threads": { "has_unreads": true, "mention_count": 1 } }threads is one summary for the whole workspace, not a list: it does not say
which threads. It comes from the same client.counts response, so it costs no
extra call, and --types does not filter it. Unread threads alone are enough to
print a result, so a script should test both keys before reporting “nothing
unread”. A thread mention counts as unread even when has_unreads is false.
With a standard token the threads key is left out, because
conversations.list has no equivalent.
Auth-type caveat. With browser auth this reads Slack’s own unread state
(client.counts), then makes one or two API calls per unread conversation to
resolve its name (a DM also looks up the user), so a workspace with many unread
channels may hit Slack rate limits. With a standard token it reads the first 1000 conversations from conversations.list
and keeps those you are a member of that carry an unread count; Slack often
omits those counts for app tokens, so the result can be incomplete.
conversations members list
Section titled “conversations members list”slackcli conversations members list C1234567890slackcli conversations members list C1234567890 --limit=50slackcli conversations members list C1234567890 --cursor="$NEXT_CURSOR" # next pageslackcli conversations members list C1234567890 --jsonLists the member IDs of a channel or conversation. --limit reflects members
returned (it pages until it has that many or the conversation is exhausted),
and when more remain the output prints the --cursor value to fetch the next
page. --json gives { channel_id, member_count, members: [...], next_cursor? }.
Enterprise-grid caveat. On an Enterprise Grid org this endpoint can be
blocked by org policy — Slack returns enterprise_is_restricted, the command
reports it clearly and exits non-zero (scoping to a team does not lift it).
Member management (add/remove) and self ops (join/leave) are
documented below; this command is read-only.
Previewing a change (--dry-run)
Section titled “Previewing a change (--dry-run)”members add, members remove, join and leave take --dry-run: the
channel and user IDs are resolved and checked, and the command prints what
it would do without doing it and without prompting. The preview lists the
users that would be added or removed.
slackcli conversations members add C1234567890 U1 U2 --dry-runslackcli conversations leave C1234567890 --dry-run --jsonSee --dry-run for the preview format.
conversations members add
Section titled “conversations members add”slackcli conversations members add C1234567890 U1 U2 U3slackcli conversations members add C1234567890 U1,U2 --yes # non-interactiveslackcli conversations members add C1234567890 U1 --jsonAdds one or more users (or agents/apps) to a channel. IDs are comma- or
space-separated. This is a batch, all-or-nothing operation: Slack’s
conversations.invite adds the whole set or, if any ID cannot be invited,
adds none and reports the error — a success message never over-reports.
Mutating commands confirm before acting: in a terminal you get a [y/N]
prompt; with no terminal the command refuses unless you pass --yes, so a
script cannot change membership by accident. --json gives
{ channel_id, added: [...] }.
conversations members remove
Section titled “conversations members remove”slackcli conversations members remove C1234567890 U1 U2slackcli conversations members remove C1234567890 U1 --yes --jsonRemoves one or more users from a channel. Slack’s conversations.kick removes
one user per call, so this is best-effort per ID: it attempts every ID and
reports which came out and which failed, rather than stopping at the first
error. It exits non-zero if any removal failed, and --json gives
{ channel_id, removed: [...], failed: [{ user, error }] }. Confirmation
works exactly as for add (prompt, or --yes when non-interactive).
conversations join
Section titled “conversations join”slackcli conversations join C1234567890slackcli conversations join C1234567890 --jsonJoins a public channel as yourself. This is a self-op with no confirmation
prompt — it is idempotent (joining a channel you are already in is a no-op) and
only changes your own membership. --json gives { channel_id, channel }.
conversations leave
Section titled “conversations leave”slackcli conversations leave C1234567890slackcli conversations leave C1234567890 --yes --jsonLeaves a channel or conversation as yourself. Like the other mutating commands
it confirms first (or takes --yes when non-interactive). Leaving a channel you
are already out of is reported as a no-op, not an error. --json gives
{ channel_id, left, not_in_channel }.