Slack actions
Who this is for: people and AI assistants who write workflow steps that tell a team in Slack about something, for example a failed run, a new customer or an access review that is due.
Before you start: you need a Slack connection. See Connect Slack.
These actions are part of our curated platform library. Use them in any workflow as uses: library/slack.<name>@v1. Each action uses one connection slot, slack, which takes a connection made from the Slack template. You find the same actions in the app under Library, and your AI assistant reads their full contracts with the MCP tool get_action.
| Action | What it does | Bot token scope |
|---|---|---|
library/slack.post-message@v1 |
Posts one message to a channel or a person (changes data) | chat:write, and chat:write.public to post in a public channel without joining |
library/slack.find-user-by-email@v1 |
Finds one person by email address (only reads) | users:read and users:read.email |
library/slack.list-conversations@v1 |
Lists channels and conversations (only reads) | channels:read, groups:read, mpim:read or im:read, by type |
library/slack.list-members@v1 |
Lists the members of one channel (only reads) | channels:read, or groups:read for a private channel |
library/slack.join-conversation@v1 |
Lets the app join one public channel (changes data) | channels:join |
library/slack.open-dm@v1 |
Opens a direct message with one person (changes data) | im:write |
Add only the scopes for the actions you use, see Give the app only the scopes it needs.
What every action does the same way
Section titled “What every action does the same way”- Only what the workflow needs. Slack answers with much more than the actions return. A person lookup returns the user ID and a few flags, and names only when you ask for them. A channel list returns IDs, names and flags, not topics or descriptions.
- Pages, with a limit. Slack returns long lists a page at a time. The list actions read the pages for you, up to
max_pagespages andmax_itemsitems. When more was left, the output saystruncated: true. - When Slack slows you down. If Slack answers
429 Too Many Requestswith a wait time of up to a minute, Liitora waits and tries again, within the step’s timeout. A throttled request was not carried out, so a message is never posted twice this way. If it keeps happening, the step fails and says so. - Errors. Slack can answer a refusal with a success status, so every action checks Slack’s
okfield. A failed step says what it tried to do, Slack’s error code, and what to check, such as a missing scope or an app that isn’t in the channel. Slack’s own error text is left out. - Dry runs and test runs change nothing. In a dry run, Liitora holds back the actions that change data and logs them as “would have sent”. The step succeeds with
dry_run: trueandstatus: dry_run. A test run that uses a production connection holds them back the same way. The actions that only read still run for real. - A person approves it first. Like every workflow, a version that uses these actions runs for real only after an admin approves it. See Approve a workflow.
slack.post-message
Section titled “slack.post-message”Posts one message to a channel, a direct message or a person. Each run posts a new message.
| Input | Required | What it is |
|---|---|---|
channel |
yes | Where to post: a channel ID like C024BE91L, a direct message ID (D...), a user ID (U...) for the app’s Messages tab with that person, or a public channel name like general. |
text |
yes | The message text, at most 40,000 characters. Slack advises at most 4,000. With blocks, it is what Slack shows in notifications and reads to screen readers. |
text_format |
no | plain (default): the text is shown as written. &, < and > are escaped, so run data can’t form links or mentions. mrkdwn: Slack’s formatting is applied, including links and mentions written in the text. |
blocks |
no | Block Kit blocks, at most 50. They are sent as given, so escape run data in them yourself. |
thread_ts |
no | The ts of a message, to reply in its thread. |
reply_broadcast |
no | With thread_ts: also show the reply in the channel (default false). |
unfurl_links |
no | Let Slack show previews of web pages linked in the message (default false). |
unfurl_media |
no | Let Slack show previews of images, video and audio linked in the message (default false). |
Outputs: status (posted or dry_run), changed (true when the message was posted), dry_run, channel (the ID of the conversation the message went to) and ts (the message’s timestamp, which identifies it).
Needs chat:write. The app can post only in channels it is a member of, unless it also has chat:write.public, which lets it post in any public channel. If Slack answers with an internal error or a timeout, the message may have been posted anyway, so the step fails and says so. Check the channel before you retry the step, or you may post twice.
- id: tell name: Tell the team uses: library/slack.post-message@v1 connections: slack: slack with: channel: C024BE91L text: "Invoice ${{ trigger.body.number }} failed to sync."slack.find-user-by-email
Section titled “slack.find-user-by-email”Finds one person in your Slack workspace by email address.
| Input | Required | What it is |
|---|---|---|
email |
yes | The person’s email address. It is sent as given. |
include_names |
no | Also return real_name and display_name (default false: only the ID and flags). |
Outputs: found (true or false) and user (id, team_id, deleted and is_bot, and the names with include_names, or null).
Needs users:read and users:read.email. A person Slack doesn’t know, or who was deactivated, gives found: false, not an error.
slack.list-conversations
Section titled “slack.list-conversations”Lists the conversations the app can see.
| Input | Required | What it is |
|---|---|---|
types |
no | Which conversations to list: public_channel (default), private_channel, mpim (group messages) and im (direct messages). |
exclude_archived |
no | Leave archived channels out (default true). |
page_size |
no | Conversations per page, 1 to 1000 (default 200). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Conversations to return at most, 1 to 10000 (default 1000). |
Outputs: conversations (each with id, name, is_private, is_archived, is_member, is_im, is_mpim, and user for a direct message), pages and truncated.
Each type needs its own scope: channels:read, groups:read, mpim:read or im:read. Private channels, group messages and direct messages are listed only when the app is in them.
slack.list-members
Section titled “slack.list-members”Lists the user IDs of the members of one conversation.
| Input | Required | What it is |
|---|---|---|
channel |
yes | The conversation’s ID, like C024BE91L. |
page_size |
no | Members per page, 1 to 1000 (default 200). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Members to return at most, 1 to 10000 (default 1000). |
Outputs: members (the user IDs), pages and truncated.
Needs channels:read, or groups:read for a private channel the app is in.
slack.join-conversation
Section titled “slack.join-conversation”Lets the app join one public channel, so it can post there.
| Input | Required | What it is |
|---|---|---|
channel |
yes | The public channel’s ID, like C024BE91L. |
Outputs: status (joined, already_member or dry_run), changed (true when this step made the app join), dry_run and channel.
Needs channels:join. When the app is in the channel already, the step succeeds with status: already_member. The app can’t join a private channel this way: someone in it has to invite the app.
slack.open-dm
Section titled “slack.open-dm”Opens the direct message between the app and one person, or returns the one that is already open.
| Input | Required | What it is |
|---|---|---|
user |
yes | The person’s user ID, like U024BE91L. Find it with slack.find-user-by-email. |
Outputs: status (opened or dry_run), dry_run and channel (the direct message’s ID, to post to with slack.post-message).
Needs im:write. To post to a person you can also pass their user ID straight to slack.post-message.
- id: who name: Find the new joiner in Slack uses: library/slack.find-user-by-email@v1 connections: slack: slack with: email: ${{ trigger.body.email }} - id: dm name: Open a direct message if: ${{ steps.who.outputs.found }} uses: library/slack.open-dm@v1 connections: slack: slack with: user: ${{ steps.who.outputs.user.id }}Sources
Section titled “Sources”Written from Slack’s own documentation:
- Web API basics and Pagination
- Rate limits
- chat.postMessage and Formatting message text
- Unfurling links in messages
- users.lookupByEmail
- conversations.list and conversations.members
- conversations.join and conversations.open
- chat:write.public scope and users:read.email scope