A lightweight command-line interface for interacting with the Slack Web API.
This project is not affiliated with Slack or Salesforce. If you're looking to build Slack apps with workflows, triggers, and datastores, check out the official Slack CLI.
What's the difference?
| Feature | slck (this project) | Official Slack CLI |
|---|---|---|
| Purpose | Direct API access for automation & scripting | Build and deploy Slack apps |
| Use cases | Send messages, manage channels, search, CI/CD integration | Workflows, triggers, datastores, app development |
| Authentication | Bot/User OAuth tokens | Slack app credentials |
| Complexity | Simple, single binary | Full development framework |
When to use slck:
- Sending notifications from CI/CD pipelines
- Automating channel management
- Scripting message operations
- Quick API interactions from the terminal
- Integrating Slack into shell scripts or automation tools
Homebrew (recommended)
brew install open-cli-collective/tap/slack-chat-cliNote: This installs from our third-party tap.
Chocolatey
choco install slack-chat-cliWinget
winget install OpenCLICollective.slack-chat-cliSnap
sudo snap install ocli-slackNote: After installation, the command is available as
slck.
APT (Debian/Ubuntu)
# Add the GPG key
curl -fsSL https://open-cli-collective.github.io/linux-packages/keys/gpg.asc | sudo gpg --dearmor -o /usr/share/keyrings/open-cli-collective.gpg
# Add the repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/open-cli-collective.gpg] https://open-cli-collective.github.io/linux-packages/apt stable main" | sudo tee /etc/apt/sources.list.d/open-cli-collective.list
# Install
sudo apt update
sudo apt install slckNote: This is our third-party APT repository, not official Debian/Ubuntu repos.
DNF/YUM (Fedora/RHEL/CentOS)
# Add the repository
sudo tee /etc/yum.repos.d/open-cli-collective.repo << 'EOF'
[open-cli-collective]
name=Open CLI Collective
baseurl=https://open-cli-collective.github.io/linux-packages/rpm
enabled=1
gpgcheck=1
gpgkey=https://open-cli-collective.github.io/linux-packages/keys/gpg.asc
EOF
# Install
sudo dnf install slckNote: This is our third-party RPM repository, not official Fedora/RHEL repos.
Binary download
Download .deb, .rpm, or .tar.gz from the Releases page - available for x64 and ARM64.
# Direct .deb install
curl -LO https://github.com/open-cli-collective/slack-chat-api/releases/latest/download/slck_VERSION_linux_amd64.deb
sudo dpkg -i slck_VERSION_linux_amd64.deb
# Direct .rpm install
curl -LO https://github.com/open-cli-collective/slack-chat-api/releases/latest/download/slck-VERSION.x86_64.rpm
sudo rpm -i slck-VERSION.x86_64.rpmgo install github.com/open-cli-collective/slack-chat-api/cmd/slck@latestgit clone https://github.com/open-cli-collective/slack-chat-api.git
cd slack-chat-api
make buildCredentials are stored in the OS keyring via the shared cli-common/credstore
library:
| Platform | Credential Storage |
|---|---|
| macOS | Keychain |
| Windows | Credential Manager |
| Linux | Secret Service (D-Bus); fails closed if a working keyring is locked. Encrypted-file backend only when there is no keyring at all, or by explicit opt-in (keyring.backend: file in config.yml). |
Tokens are never written to a plaintext file. Non-secret config
(credential_ref, workspace) lives in ~/.config/slack-chat-api/config.yml.
At runtime the only source of a token is the OS keyring. Environment
variables are not read as credentials at runtime (per the Open CLI
Collective Secret-Handling Standard §1.11) — they are accepted only as
ingress during setup, e.g. slck init --bot-token-from-env SLACK_BOT_TOKEN
or op read ... | slck set-credential --key bot_token --stdin.
Migrating from an older slck? On first run, any tokens from a previous version (old macOS Keychain items under service
slck/slack-chat-api, or the legacy~/.config/slack-chat-api/credentialsfile) are moved into the keyring automatically, once, then the originals are removed. A one-line notice is printed to stderr (and a_migrationblock to JSON output).
-
Go to api.slack.com/apps → Create New App → From an app manifest
-
Select your workspace
-
Paste this manifest (JSON tab recommended):
{ "display_information": { "name": "Slack Chat API" }, "features": { "bot_user": { "display_name": "Slack Chat API", "always_online": false } }, "oauth_config": { "scopes": { "bot": [ "channels:history", "channels:manage", "channels:read", "chat:write", "emoji:read", "files:read", "groups:history", "groups:read", "reactions:write", "team:read", "users:read" ], "user": [ "search:read" ] } }, "settings": { "org_deploy_enabled": false, "socket_mode_enabled": false } }YAML alternative (if you prefer)
display_information: name: Slack Chat API features: bot_user: display_name: Slack Chat API always_online: false oauth_config: scopes: bot: - "channels:history" - "channels:manage" - "channels:read" - "chat:write" - "emoji:read" - "files:read" - "groups:history" - "groups:read" - "reactions:write" - "team:read" - "users:read" user: - "search:read" settings: org_deploy_enabled: false socket_mode_enabled: false
Extended manifest — every read scope slck can use, plus all writes (DMs, group DMs, file uploads, canvas CRUD, etc.)
Use this if you want maximum capability out of the box — reading DMs and group DMs, uploading file attachments to any channel/DM/group-DM, creating/editing/deleting canvases, resolving
@subteammentions, extended user profile data, etc. Every scope here is either needed by an existing slck command or unlocks a capability that commonly extends one (e.g.im:historysoslck messages threadworks on DMs,files:writeforslck messages send --file,canvases:writeforslck canvas create).{ "display_information": { "name": "Slack Chat API" }, "features": { "bot_user": { "display_name": "Slack Chat API", "always_online": false } }, "oauth_config": { "scopes": { "bot": [ "app_mentions:read", "bookmarks:read", "calls:read", "canvases:read", "canvases:write", "channels:history", "channels:manage", "channels:read", "chat:write", "chat:write.public", "dnd:read", "emoji:read", "files:read", "files:write", "groups:history", "groups:read", "groups:write", "im:history", "im:read", "im:write", "links:read", "lists:read", "metadata.message:read", "mpim:history", "mpim:read", "mpim:write", "pins:read", "reactions:read", "reactions:write", "reminders:read", "remote_files:read", "search:read.files", "search:read.mpim", "search:read.public", "search:read.users", "team:read", "usergroups:read", "users.profile:read", "users:read" ], "user": [ "search:read", "search:read.files", "search:read.im", "search:read.mpim", "search:read.private", "search:read.public", "search:read.users" ] }, "pkce_enabled": false }, "settings": { "org_deploy_enabled": false, "socket_mode_enabled": false, "token_rotation_enabled": false, "is_mcp_enabled": false } }Upgrading an existing install? Paste the full manifest above into your app's Features → App Manifest tab (replacing the previous manifest), click Save Changes, then click Install App → Reinstall to Workspace to grant the new scopes. Copy the fresh
xoxb-…token and runslck init(orslck set-credential). -
Click Create → Install to Workspace → Allow
-
Copy the Bot User OAuth Token (starts with
xoxb-) -
Run the interactive setup:
slck init # Paste your token when prompted (input is not echoed back)
Your token is stored in the OS keyring (Keychain / Credential Manager / Secret Service). It is never written to a plaintext file.
NOTE: If you plan on sending messages or taking actions using your user token (See: Choosing Between Bot and User Tokens), you'll need to adjust the manifest above to have all the same scopes configured for your user as your bot (with the exception of the "channels:manage" scope, which only applies to bots).
The token is read only from stdin or a named env var — never a flag or
positional value (so it can't leak via shell history or ps):
# From a pipe (preferred for automation):
op read 'op://Personal/slck/bot_token' | slck set-credential --key bot_token --stdin
# Or, during full init, from a named env var (the env var is consumed at
# setup time only; it is NOT read on subsequent runs):
slck init --bot-token-from-env SLACK_BOT_TOKEN --user-token-from-env SLACK_USER_TOKENBecause the token lives in the keyring after setup, you do not wrap every
invocation in op. Stage it once:
op read 'op://Personal/slck/bot_token' | slck set-credential --key bot_token --stdin
op read 'op://Personal/slck/user_token' | slck set-credential --key user_token --stdinReplace the op://… references with your own 1Password secret references.
Re-run only when the token rotates.
The manifest above includes these scopes:
| Scope | Purpose |
|---|---|
channels:read |
List public channels, get channel info |
channels:history |
Read message history from public channels |
channels:manage |
Create, archive, set topic/purpose, invite users |
chat:write |
Send, update, delete messages |
emoji:read |
List custom workspace emoji |
files:read |
Download files, get file info |
groups:read |
List private channels |
groups:history |
Read message history from private channels |
reactions:write |
Add/remove reactions |
team:read |
Get workspace info |
users:read |
List users, get user info |
search:read |
Search messages and files (user token only) |
The extended manifest (see the collapsible section above) adds these capabilities on top of the default:
| Scope | Unlocks |
|---|---|
im:history / im:read / im:write |
Read messages in DMs, list DMs, open new DMs to post to |
mpim:history / mpim:read / mpim:write |
Same for multi-person DMs (group DMs) |
files:write |
Upload files — slck messages send --file in any channel/DM/group-DM |
canvases:write |
Create, edit, delete canvases — slck canvas create/edit/delete |
groups:write |
Create/archive private channels |
chat:write.public |
Post to public channels the bot isn't a member of |
users.profile:read |
Extended user profile (status, timezone, custom fields) |
usergroups:read |
Resolve @subteam / user-group mentions |
app_mentions:read |
Receive @-mention events (needed for event subscriptions) |
reactions:read |
List reactions on a message |
Other *:read scopes (bookmarks, pins, reminders, links, lists, calls, dnd, metadata.message, remote_files) |
Read-only access to those domains; mostly forward-compat for commands that may use them |
This CLI supports two types of Slack tokens:
| Token Type | Prefix | Commands | How to Get |
|---|---|---|---|
| Bot token | xoxb- |
channels, users, messages, workspace | OAuth & Permissions → Bot User OAuth Token |
| User token | xoxp- |
search | OAuth & Permissions → User OAuth Token |
Most commands use the bot token. Search commands require a user token.
Setting up both tokens:
# Bot token (for channels, users, messages, workspace)
op read 'op://Personal/slck/bot_token' | slck set-credential --key bot_token --stdin
# User token (for search)
op read 'op://Personal/slck/user_token' | slck set-credential --key user_token --stdinOr run slck init for a guided, interactive setup of both.
Getting a user token:
- Go to api.slack.com/apps → Your app
- OAuth & Permissions → User Token Scopes → Add
search:read - Reinstall app to workspace (if already installed)
- Copy the User OAuth Token (starts with
xoxp-)
Setup-time env-var ingress (read once during slck init, never at runtime):
| Flag | Description |
|---|---|
slck init --bot-token-from-env NAME |
Read the bot token from env var NAME at setup |
slck init --user-token-from-env NAME |
Read the user token from env var NAME at setup |
These flags are available on all commands:
| Flag | Short | Default | Description |
|---|---|---|---|
--output |
-o |
text |
Output format: text, json, or table |
--no-color |
false |
Disable colored output | |
--as-user |
false |
Use user token | |
--as-bot |
false |
Use bot token | |
--version |
-v |
Show version information | |
--help |
-h |
Show help for any command |
By default, all commands other than search use your bot token. You can set the SLCK_AS_USER env var to true to make your user token the default. You can also use flags to specify which token to use for any specific command (and this will override the default behavior set by your env var).
# Send a message as yourself (using user token)
slck messages send --as-user C1234567890 "Hey team!"
# Set default to user token via environment variable
export SLCK_AS_USER=true
slck messages send C1234567890 "Uses user token by default"
# If the default is user token, you can override back to bot token for a specific command
export SLCK_AS_USER=true
slck messages send --as-bot C1234567890 "Uses bot token"# List all channels
slck channels list
# List with options
slck channels list --types public_channel,private_channel # Include private channels
slck channels list --limit 50 # Limit results
slck channels list --exclude-archived=false # Include archived channels
# Get channel info
slck channels get C1234567890
# Create a channel
slck channels create my-new-channel
slck channels create private-channel --private
# Archive/unarchive
slck channels archive C1234567890
slck channels unarchive C1234567890
# Set topic/purpose
slck channels set-topic C1234567890 "New topic"
slck channels set-purpose C1234567890 "Channel purpose"
# Invite users
slck channels invite C1234567890 U1111111111 U2222222222| Command | Flags | Description |
|---|---|---|
list |
--types, --limit, --exclude-archived |
List channels |
get <id> |
Get channel details | |
create <name> |
--private |
Create a channel |
archive <id> |
--force |
Archive a channel (prompts for confirmation) |
unarchive <id> |
Unarchive a channel | |
set-topic <id> <topic> |
Set channel topic | |
set-purpose <id> <purpose> |
Set channel purpose | |
invite <id> <user>... |
Invite users to channel |
# List all users
slck users list
slck users list --limit 50
# Get user info
slck users get U1234567890
# Search users
slck users search "john"
slck users search "john@company.com" --field email
slck users search "John Smith" --field display_name
slck users search "bot" --include-bots| Command | Flags | Description |
|---|---|---|
list |
--limit |
List all users |
get <id> |
Get user details | |
search <query> |
--limit, --field, --include-bots |
Search users by name, email, or display name |
| Flag | Default | Description |
|---|---|---|
--limit |
1000 |
Maximum users to search through |
--field |
all |
Search field: all, name, email, display_name |
--include-bots |
false |
Include bot users in results |
# Send a message (uses Block Kit formatting by default)
slck messages send C1234567890 "Hello, *world*!"
# Send using --channel flag (alternative to positional arg)
slck messages send --channel general "Hello team"
# Send from stdin (use "-" as text argument)
echo "Hello from stdin" | slck messages send C1234567890 -
cat message.txt | slck messages send C1234567890 -
# Send plain text (no formatting)
slck messages send C1234567890 "Plain text" --simple
# Send with custom Block Kit blocks
slck messages send C1234567890 "Fallback" --blocks '[{"type":"section","text":{"type":"mrkdwn","text":"*Bold*"}}]'
# Reply in a thread
slck messages send C1234567890 "Thread reply" --thread 1234567890.123456
# Upload files with a message
slck messages send C1234567890 "Here's the report" --file ./report.csv
slck messages send C1234567890 --file ./a.csv --file ./b.csv
# Update a message
slck messages update C1234567890 1234567890.123456 "Updated text"
slck messages update C1234567890 1234567890.123456 "Plain update" --simple
# Delete a message
slck messages delete C1234567890 1234567890.123456
# Get channel history
slck messages history C1234567890
slck messages history C1234567890 --limit 50
slck messages history C1234567890 --oldest 1234567890.000000 # After this time
slck messages history C1234567890 --latest 1234567890.000000 # Before this time
# Get thread replies
slck messages thread C1234567890 1234567890.123456
slck messages thread C1234567890 1234567890.123456 --limit 50
slck messages thread C1234567890 1234567890.123456 --since 1234567890.000000 # Only newer replies
# Add/remove reactions
slck messages react C1234567890 1234567890.123456 thumbsup
slck messages unreact C1234567890 1234567890.123456 thumbsup| Command | Flags | Description |
|---|---|---|
send <channel> <text> |
--thread, --blocks, --simple, --channel, --file |
Send a message (use - for stdin) |
update <channel> <ts> <text> |
--blocks, --simple |
Update a message |
delete <channel> <ts> |
--force |
Delete a message (prompts for confirmation) |
history <channel> |
--limit, --oldest, --latest |
Get channel history |
thread <channel> <ts> |
--limit, --since |
Get thread replies |
react <channel> <ts> <emoji> |
Add reaction | |
unreact <channel> <ts> <emoji> |
Remove reaction |
Note: Search requires a user token (
xoxp-*). See Token Types.
# Search messages
slck search messages "quarterly report"
slck search messages "in:#general bug fix"
slck search messages "from:@alice project update"
# Search files
slck search files "budget spreadsheet"
slck search files "type:pdf report"
# Search all (messages + files)
slck search all "project proposal"
slck search all "quarterly" --sort timestamp
# With pagination
slck search messages "error" --count 50 --page 2
# Using query builder flags (alternative to modifiers in query string)
slck search messages "meeting" --in "#general"
slck search messages "update" --from "@alice"
slck search messages "report" --scope public
slck search messages "project" --after 2025-01-01 --before 2025-12-31
slck search messages "link" --has-link
slck search files "document" --type pdf| Modifier | Example | Description |
|---|---|---|
in: |
in:#channel or in:@user |
Search in specific channel or DM |
from: |
from:@username |
Content from specific user |
before: |
before:2025-01-01 |
Before date |
after: |
after:2025-01-01 |
After date |
has:link |
Messages containing links | |
has:reaction |
Messages with reactions | |
type: |
type:pdf |
Files of specific type |
| Command | Flags | Description |
|---|---|---|
messages <query> |
--count, --page, --sort, --sort-dir, --highlight, --scope, --in, --from, --after, --before, --has-link, --has-reaction |
Search messages |
files <query> |
--count, --page, --sort, --sort-dir, --highlight, --scope, --in, --from, --after, --before, --type, --has-pin |
Search files |
all <query> |
--count, --page, --sort, --sort-dir, --highlight, --scope, --in, --from, --after, --before, --has-link, --has-reaction |
Search messages and files |
| Flag | Short | Default | Description |
|---|---|---|---|
--count |
-c |
20 |
Results per page (max 100) |
--page |
-p |
1 |
Page number (max 100) |
--sort |
-s |
score |
Sort by: score or timestamp |
--sort-dir |
desc |
Sort direction: asc or desc |
|
--highlight |
false |
Highlight matching terms |
These flags provide an alternative to using modifiers in the query string:
| Flag | Description |
|---|---|
--scope |
Search scope: all, public, private, dm, mpim |
--in |
Filter by channel (e.g., #general or general) |
--from |
Filter by user (e.g., @alice or alice) |
--after |
Content after date (YYYY-MM-DD) |
--before |
Content before date (YYYY-MM-DD) |
--has-link |
Messages containing links |
--has-reaction |
Messages with reactions |
--type |
File type filter (files only, e.g., pdf, image) |
--has-pin |
Files that are pinned (files only) |
# Get workspace info
slck workspace info# List custom workspace emoji
slck emoji list
# Include aliases
slck emoji list --include-aliases| Command | Flags | Description |
|---|---|---|
list |
--include-aliases |
List custom workspace emoji |
# Download a file by ID
slck files download F0AHF3NUSQK
# Download to a specific path
slck files download F0AHF3NUSQK --output report.pdf
# Download using a Slack file URL
slck files download "https://files.slack.com/files-pri/T.../F0AHF3NUSQK/file.csv"| Command | Flags | Description |
|---|---|---|
download <file-id-or-url> |
--output, -O |
Download a Slack file |
# Create a standalone canvas
slck canvas create --title "Sprint Review" --text "# Sprint Review\n\nContent here"
slck canvas create --title "Report" --file report.md
echo "# Report" | slck canvas create --title "Report" --file -
# Create a channel canvas (pinned to channel tab)
slck canvas create --channel C1234567890 --file runbook.md
# Edit a canvas
slck canvas edit F12345ABC --text "# Updated content"
slck canvas edit F12345ABC --file updated.md
# Delete a canvas
slck canvas delete F12345ABC| Command | Flags | Description |
|---|---|---|
create |
--title, --text, --file, --channel |
Create a standalone or channel canvas |
edit <canvas-id> |
--text, --file |
Replace canvas content |
delete <canvas-id> |
Delete a canvas |
Note: Canvas commands require additional Slack app scopes not included in the default manifest. See Slack Canvas API docs for required scopes.
# Show authenticated identity (bot, user, workspace)
slck me# Guided interactive setup (bot + optional user token)
slck init
# Set one credential from stdin (scriptable; no value on the command line)
op read 'op://Personal/slck/bot_token' | slck set-credential --key bot_token --stdin
# Show current config status (backend, ref, which keys present — never values)
slck config show
# Delete stored token(s)
slck config delete-tokenIngress is at the top level, not under config:
| Command | Flags | Description |
|---|---|---|
slck init |
--bot-token-from-env, --user-token-from-env, --bot-token-stdin, --overwrite, --no-verify |
Guided setup; stores into the keyring |
slck set-credential |
--key, --stdin, --from-env, --ref |
Set one credential (stdin/env only) |
config subcommands (none accept a secret value):
| Command | Flags | Description |
|---|---|---|
set-token |
Removed — errors out, points to set-credential |
|
show |
Show backend, ref, which keys are present (never values) | |
delete-token |
--force, --type |
Delete stored token(s) |
test |
Test authentication for configured tokens |
The delete-token command accepts a --type flag:
--type bot- Delete only the bot token--type user- Delete only the user token--type all- Delete both tokens (default)
All commands support multiple output formats via the --output (or -o) flag:
# Default text output
slck channels list
# JSON output (for scripting)
slck channels list --output json
slck users get U1234567890 -o json
# Table output (aligned columns)
slck channels list --output table# Bash
slck completion bash > /etc/bash_completion.d/slck
# Zsh
slck completion zsh > "${fpath[1]}/_slck"
# Fish
slck completion fish > ~/.config/fish/completions/slck.fish
# PowerShell
slck completion powershell > slck.ps1Commands have convenient aliases:
| Command | Aliases |
|---|---|
canvas |
cv |
channels |
ch |
users |
u |
messages |
msg, m |
search |
s |
emoji |
e |
workspace |
ws, team |
| Variable | Description |
|---|---|
SLCK_AS_USER |
Set to true or 1 to default to user token instead of bot token |
SLACK_CHAT_API_KEYRING_BACKEND |
Force the keyring backend (e.g. file) — non-secret selector (§1.4) |
SLACK_CHAT_API_KEYRING_PASSPHRASE |
Passphrase for the encrypted-file backend (the one runtime secret-env exception, §1.4) |
NO_COLOR |
Disable colored output when set |
XDG_CONFIG_HOME |
Custom config directory (default: ~/.config) |
SLACK_API_TOKEN/SLACK_USER_TOKENare no longer read at runtime (§1.11). Useslck init --bot-token-from-env/slck set-credentialto stage a token into the keyring instead.
Slack silently truncates messages exceeding 40,000 characters. slck validates message length before sending and returns an error with suggestions:
- Upload as a file:
slck messages send C123 --file ./content.txt - Create a canvas:
slck canvas create --title "Title" --file ./content.md
Bot tokens (xoxb-) cannot unarchive channels due to a Slack API limitation. When a channel is archived, the bot is automatically removed from it, and bot tokens require membership to unarchive.
Workarounds:
- Unarchive channels via the Slack UI
- Use a user token (
xoxp-) instead of a bot token
MIT