Read, send, and manage Slack messages via the Web API
assistant skills install slackYou help users interact with their Slack workspace. All Slack operations use the Slack Web API directly via assistant oauth request --provider slack_channel -- there are no dedicated Slack tools. Use relative Slack API method paths such as /chat.postMessage; the provider supplies the Slack host.
Use these scripts to resolve Slack channel and user names to IDs. Results are cached locally so repeated lookups are free (no API calls).
| Command | Description |
|---|---|
bun skills/slack/scripts/slack-resolve.ts channel <name> | Resolve a channel name to its ID |
bun skills/slack/scripts/slack-resolve.ts user <name-or-email> | Resolve a user display name or email to their ID |
bun skills/slack/scripts/slack-resolve.ts channels [--refresh] | List all cached channels, or refresh the cache |
All scripts return JSON:
{ "ok": true, "data": { "id": "C...", "name": "general", ... } }{ "ok": false, "error": "..." }The cache is stored locally under ~/.vellum/workspace/data/slack-skill/. On first use the script fetches all channels/users from Slack and caches them. Subsequent lookups read from the cache with no API calls. Pass --refresh to force a refresh.
Use assistant oauth request --provider slack_channel to call any Slack Web API method. Auth is handled transparently -- the provider injects the bot token automatically. Pass relative method paths; do not include a host.
General pattern:
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C123","text":"Hello world"}' \
/chat.postMessage --json
The model knows the full Slack API from training data. Refer to https://api.slack.com/methods for the complete list of available endpoints.
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C0123456789","text":"Hello from the assistant!"}' \
/chat.postMessage --json
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C0123456789","limit":20}' \
/conversations.history --json
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C0123456789","ts":"1716000000.000001"}' \
/conversations.replies --json
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C0123456789","timestamp":"1716000000.000001","name":"thumbsup"}' \
/reactions.add --json
assistant oauth request --provider slack_channel \
-X POST \
-d '{
"channel":"C0123456789",
"text":"Fallback text",
"blocks":[
{"type":"header","text":{"type":"plain_text","text":"Weekly Update"}},
{"type":"section","text":{"type":"mrkdwn","text":"*Project Alpha*: on track\n*Project Beta*: needs review"}}
]
}' \
/chat.postMessage --json
File uploads use a multi-step flow: get an upload URL, upload the file, then complete the upload.
# Step 1: Get an upload URL
assistant oauth request --provider slack_channel \
-X POST \
-d '{"filename":"notes.txt","length":42}' \
/files.getUploadURLExternal --json
# Step 2: Upload file content to the returned upload_url (use curl directly)
# Step 3: Complete the upload
assistant oauth request --provider slack_channel \
-X POST \
-d '{"files":[{"id":"FILE_ID","title":"Meeting Notes"}],"channel_id":"C0123456789"}' \
/files.completeUploadExternal --json
assistant oauth request --provider slack_channel \
"/search.messages?query=project+launch+in%3A%23general" --json
assistant oauth request --provider slack_channel \
-X POST \
-d '{"users":"U0123456789"}' \
/conversations.open --json
bun skills/slack/scripts/slack-resolve.ts channel general to get the channel ID.assistant oauth request --provider slack_channel with that ID for the actual operation (send, read, react, etc.).bun skills/slack/scripts/slack-resolve.ts user <name> to get the user ID, then conversations.open to get the DM channel ID, then chat.postMessage to send the message.When you need to send a DM or look up a Slack user by name, check contacts first to avoid redundant API calls:
Before calling the resolve script: Use contact_search with query: "<name>" and channel_type: "slack". If a matching contact has externalUserId (Slack user ID) and externalChatId (DM channel ID), skip the API lookups and use those IDs directly with chat.postMessage via assistant oauth request --provider slack_channel.
When contact_search returns notes for the recipient, use them to inform the message's tone, formality, and content. Contact notes capture relationship context and communication preferences that should shape how you write to this person.
After resolving via script: When you had to use slack-resolve.ts user or conversations.open to resolve a user, save the contact with contact_upsert so you can find them by name next time. External Slack IDs (user ID, DM channel ID) are cached automatically by the messaging layer and should not be passed through contact_upsert.
Channel privacy must be respected at all times:
is_private on each channel before sharing content elsewhereWhen responding to messages from Slack channels, replies should be threaded. Pass thread_ts to chat.postMessage to reply in a thread rather than posting a new top-level message:
assistant oauth request --provider slack_channel \
-X POST \
-d '{"channel":"C0123456789","text":"Replying in thread","thread_ts":"1716000000.000001"}' \
/chat.postMessage --json
Before making any Slack API calls, verify that Slack is connected. If not connected, load the slack-app-setup skill (skill_load with skill: "slack-app-setup") and follow its guided flow. Do NOT improvise setup instructions -- the slack-app-setup skill is the single source of truth. Slack uses Socket Mode and does not require redirect URLs or any OAuth flow.
If a Slack API call fails due to missing or invalid credentials -- for example, an error indicating that the token is missing or invalid -- do NOT attempt to fix the credentials manually. Instead, load the slack-app-setup skill (skill_load with skill: "slack-app-setup") and follow its guided flow to set up or reconnect Slack. Tell the user something like "Slack needs to be reconnected" and start the setup skill.
chat.postMessage with blocks via assistant oauth request --provider slack_channelassistant notifications send via bash is fine -- it lets the notification router pick the best channel