Slack CLI
Interact with Slack workspaces from the command line using slack-cli and direct Slack API calls.
Assume
slack-cliis already installed and configured. If theslackcommand is not found or returnsnot_inited, refer to references/INSTALLATION.md for setup instructions.
When to Use
- User wants to send, update, or delete Slack messages
- User wants to read channel history or search messages
- User wants to list channels, users, or conversations
- User wants to upload files to Slack
- User wants to manage reminders, snooze, or presence
- User wants to automate Slack workflows
Setup
If slack is not available or not configured, see references/INSTALLATION.md for full setup instructions.
The token is stored at the path configured in the slack binary's etcdir variable (typically /opt/homebrew/etc/slack-cli/.slack on macOS). You can also use the SLACK_CLI_TOKEN environment variable.
Credential Management with psst
Store the Slack token in the global psst vault:
psst --global set SLACK_CLI_TOKEN --tag slack
For built-in commands, inject the token via psst:
psst --global SLACK_CLI_TOKEN -- slack chat send 'Hello!' '#channel'
For direct API calls, use inline psst get:
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" "https://slack.com/api/..."
Built-in Commands
The slack CLI supports these commands natively:
Chat
# Send a message
slack chat send 'Hello world!' '#channel'
# Send with rich formatting
slack chat send --text 'text' --channel '#channel' --color good --title 'Title' --pretext 'Pretext'
# Pipe content as a message
echo "some output" | slack chat send --channel '#channel'
# Update a message (requires timestamp and channel)
slack chat update 'Updated text' 1405894322.002768 '#channel'
# Delete a message
slack chat delete 1405894322.002768 '#channel'
# Chain: send, capture ts+channel, then update
slack chat send 'hello' '#general' --filter '.ts + "\n" + .channel' | \
xargs -n2 slack chat update 'goodbye'
Files
# Upload a file
slack file upload README.md '#channel'
# Upload with metadata
slack file upload README.md '#channel' --comment 'See attached' --title 'README'
# Create a Slack post from markdown
slack file upload --file post.md --filetype post --title 'Post Title' --channels '#channel'
# List files
slack file list
slack file list --filter '[.files[] | {id, name, size}]'
# File info / delete
slack file info F2147483862
slack file delete F2147483862
Reminders
# Add a reminder
slack reminder add 'lunch' $(date -v +30M "+%s")
# List / complete / delete
slack reminder list
slack reminder complete Rm7MGABKT6
slack reminder delete Rm7MGABKT6
Snooze (Do Not Disturb)
slack snooze start 60 # Start snooze for 60 minutes
slack snooze info # Check your snooze status
slack snooze end # End snooze
Presence
slack presence active
slack presence away
Global Options
All commands support:
--filter|-f <jq-filter>— Apply a jq filter to the JSON response--compact|-cp— Compact JSON output--monochrome|-m— No color in jq output--trace|-x— Enable bash trace for debugging
Known Limitations
- DMs via built-in CLI don't work reliably.
slack chat send 'msg' '@user'often fails withchannel_not_found. Use the direct API pattern below instead. - Messages sent via
chat:writeappear as the Slack App, not as your user. This is a Slack API limitation — even with a user token, the app identity is used.
Direct API Calls
The slack CLI doesn't cover all Slack API methods. For anything beyond the built-in commands, call the Slack API directly using curl. Read the token from the CLI's config file.
Sending Direct Messages
The built-in slack chat send doesn't handle DMs well. Use the API directly:
# Step 1: Open (or find) the DM channel with a user
DM_CHANNEL=$(curl -s -X POST \
-H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
-H "Content-Type: application/json" \
-d '{"users":"USER_ID"}' \
"https://slack.com/api/conversations.open" | jq -r '.channel.id')
# Step 2: Send the message
curl -s -X POST \
-H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
-H "Content-Type: application/json" \
-d "{\"channel\":\"${DM_CHANNEL}\",\"text\":\"Hello!\"}" \
"https://slack.com/api/chat.postMessage"
Requires
im:writescope. Without it,conversations.openwill fail. If you don't haveim:write, you can work around it by finding an existing DM channel:DM_CHANNEL=$(curl -s \ -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \ "https://slack.com/api/conversations.list?types=im&limit=200" | \ jq -r '.channels[] | select(.user == "USER_ID") | .id')
List Channels
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/conversations.list?types=public_channel,private_channel&limit=200" | \
jq '[.channels[] | {name, id, is_private}]'
Read Channel History
# Get recent messages from a channel (use channel ID)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/conversations.history?channel=CHANNEL_ID&limit=20" | \
jq '[.messages[] | {user, text, ts}]'
# Get messages from last 7 days
OLDEST=$(date -v -7d +%s)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/conversations.history?channel=CHANNEL_ID&oldest=${OLDEST}&limit=100" | \
jq '[.messages[] | {user, text, ts}]'
# Get messages within a specific date range (oldest and latest are unix timestamps)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/conversations.history?channel=CHANNEL_ID&oldest=${START_TS}&latest=${END_TS}&limit=100" | \
jq '[.messages[] | {user, text, ts}]'
List Users
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/users.list" | \
jq '[.members[] | {name, id, real_name}]'
# Resolve multiple user IDs (no batch endpoint — fetch all and filter)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/users.list" | \
jq '[.members[] | select(.id == "U1234" or .id == "U5678") | {id, name: .real_name}]'
Workspace Info
# Use auth.test (not team.info — team.info requires a scope we don't have)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/auth.test" | \
jq '{team, team_id, url}'
Search Messages
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/search.messages?query=keyword&count=10" | \
jq '[.messages.matches[] | {channel: .channel.name, text, ts}]'
Reactions
# Add a reaction
curl -s -X POST \
-H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
-H "Content-Type: application/json" \
-d '{"channel":"CHANNEL_ID","timestamp":"1234567890.123456","name":"thumbsup"}' \
"https://slack.com/api/reactions.add"
# Get reactions on a message
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/reactions.get?channel=CHANNEL_ID×tamp=1234567890.123456"
Thread Replies
# Get replies in a thread (use the parent message ts)
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
"https://slack.com/api/conversations.replies?channel=CHANNEL_ID&ts=1234567890.123456" | \
jq '[.messages[] | {user, text, ts}]'
Post to a Thread
curl -s -X POST \
-H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" \
-H "Content-Type: application/json" \
-d '{"channel":"CHANNEL_ID","text":"reply text","thread_ts":"1234567890.123456"}' \
"https://slack.com/api/chat.postMessage"
Tips
- Channel arguments accept
#nameformat for built-in commands, but direct API calls require channel IDs (e.g.CHANNEL_ID) - Use
--filterwith any built-in command to extract specific fields via jq - Pipe commands together using
--filter '.ts + "\n" + .channel'to chain send/update/delete - For direct API calls, always quote the URL to prevent shell glob expansion
- Pagination: most list endpoints support
cursorandlimitparameters — checkresponse_metadata.next_cursorin the response - To look up a user ID by name:
curl -s -H "Authorization: Bearer $(psst --global get SLACK_CLI_TOKEN)" "https://slack.com/api/users.list" | jq -r '.members[] | select(.real_name | test("Name"; "i")) | {name, id, real_name}' - Message permalinks:
https://<workspace>.slack.com/archives/<channel_id>/p<ts_without_dot>— remove the dot from the message timestamp (e.g. ts1769935497.539749becomesp1769935497539749)