Skip to main content

Slack Module

Build workflows that interact with Slack workspaces: start a Session when someone writes to your app, reply in threads, and automate conversations.

The Slack Module adds a Slack start event and Slack behavior for the Output, Input and End Tasks on the SlackChannel Channel, plus a SlackBlockMessage Task for rich Block Kit messages and a SlackNotification Task that posts from any flow. When someone sends your Slack app a direct message or mentions it, a Session starts at the Slack start, with the message in {slack}. This lets you build Slack bots and automated responders using the same BPMN editor you use for chat and voice workflows. It requires Environment configuration: a Slack bot token and signing secret.

How it works​

Your Slack app sends every message it can see to the Environment's event URL (see Connect your Slack app). For each message a person writes:

  • If the message belongs to a live Session (same conversation, see Session Mode), it continues that Session: the Input Task waiting for an answer receives it.
  • Otherwise it starts a Session at the Process's Slack start, if the Environment's Start sessions from settings allow that kind of message: a direct message to the app, an @mention of the app, or (off by default) any message in a channel the app is in. The message is in {slack}; the first Input Task waits for the user's next message.
  • Messages from bots (your app's own included), edits, deletions and channel joins never start or continue anything, and Slack's retries of a message already handled are ignored.

The app answers Slack at once and the Session runs a moment later, so Slack never times out waiting. A start that cannot run (a plan limit, a Process without a Slack start) frees the conversation within 15 minutes. At each I/O node the Slack-specific behavior runs: Output Tasks post to the Slack channel or thread, Input Tasks wait for the user's next Slack message, and the SlackBlockMessage Task sends Block Kit messages.

Session identity can be scoped per thread, per channel, or per user in a channel, controlled by the Session Mode setting in the Module's Environment configuration. A Session keeps its conversation until it ends, or until 24 hours pass without a message.

Configuration​

The Slack Module is configured per Environment each Environment supplies its own Slack bot token and signing secret, so your Development and Production Environments can each talk to their own Slack app. Set these values in the Module's Environment configuration.

FieldTypeRequiredDescription
Bot TokenstringYesYour Slack bot token, starting with xoxb-. Stored securely.
Signing SecretstringYesThe Slack app signing secret used to verify that incoming requests really come from Slack.
Session ModeenumNoWhat one Session is: PerThread (a top-level message and its thread replies), PerChannel (a channel, or a direct-message conversation), or PerUserPerChannel (one user in a channel). Default: PerThread.
Use Threads for RepliesbooleanNoWhen enabled, bot replies are posted as thread replies under the message that started the Session. Default: on.
Direct messagesbooleanNoStart sessions from: a direct message to the app starts a Session. Default: on.
@mentionsbooleanNoStart sessions from: a message that mentions the app in a channel starts a Session. Default: on.
Channel messagesbooleanNoStart sessions from: any message in a channel the app is in starts a Session, without a mention. Default: off (every message in the channel would start one).
Default ChannelstringNoChannel ID (e.g. C01ABCDEF) the SlackNotification and SlackBlockMessage Tasks post to when their Channel is empty outside a Slack Session.

The Start sessions from settings only decide whether a message with no Session opens one: a message in a live Session always continues it. They replace the former Auto-Start Workflow on Message setting, which had no effect.

With PerThread, a direct message written outside the bot's thread is a new conversation: users answering in the main direct-message view rather than in the thread start a new Session each time. Use PerChannel (or PerUserPerChannel) when your users chat with the app in direct messages without opening the thread.

Connect your Slack app​

In your app's settings at api.slack.com/apps:

  1. Event Subscriptions: turn events on and set the Request URL to https://<your engine host>/Slack/event/<environment token> (the token of the Environment the Process is deployed to). Slack checks the URL once; the Module answers it with the signing secret already set in the configuration.

  2. Subscribe to bot events:

    EventScope it needsNeeded for
    message.imim:historyDirect messages: starting and continuing Sessions.
    app_mentionapp_mentions:read@mentions starting Sessions.
    message.channelschannels:historyReplies in public channels, including the thread replies that continue a Session started by a mention, and channel-message starts.
    message.groupsgroups:historyThe same in private channels.
  3. App Home > Show Tabs: enable the Messages Tab and Allow users to send Slash commands and messages from the messages tab, so people can write to the app directly.

  4. OAuth & Permissions > Bot Token Scopes: besides the scopes above, chat:write (posting), optionally users:read (fills {slack.profileName}), and files:read / files:write when the flow receives or sends files (see below). Reinstall the app after changing scopes and paste the new bot token in the configuration.

  5. Invite the app to the channels where it should listen (/invite @your-app).

Bot token scopes for files​

Sending and receiving files needs two extra Bot Token Scopes on your Slack app, besides the ones it already uses to post and read messages:

ScopeNeeded for
files:writeOutputTask and SlackNotification Attachments: uploading files to Slack.
files:readInputTask Store files in and the Slack start's {slack.files}: downloading the files a user sends.

To add them: open your app at api.slack.com/apps, go to OAuth & Permissions > Scopes > Bot Token Scopes, add the scopes, then reinstall the app to your Slack workspace. Reinstalling issues a new bot token: paste it into the Bot Token field of the Environment configuration.

A flow that uses files without the scope stops at that Task with error 4008 (ModuleMissingConfiguration), and the message names the missing scope. An OutputTask checks this before posting anything, so the user never gets the text without its files.

Tasks​

The Slack Module provides a Slack start, channel-specific behavior for the standard I/O Tasks defined by the Standard Module, a SlackBlockMessage Task for rich content and a SlackNotification Task that posts to a channel from any flow. The property panel for the standard Tasks is identical to the chat versions; only the runtime behavior differs.

Slack start​

The start event of a Slack conversation. Use it as the Process's start (or one of its starts, next to a Chat start or a WhatsApp start: the engine runs the start that serves the incoming Channel).

Properties​

PropertyTypeRequiredExampleDescription
Slack variablevariableNo{slack}Receives the message that started the Session (see below). Default: {slack}.

The {slack} variable​

PathExampleDescription
{slack.text}I need help with my invoiceThe text of the message, without the mention of the app.
{slack.files}The files that came with the message: {slack.files.count}, {slack.files.names}, {slack.files.0} (for the Read file Task or an Output's Attachments). Needs the files:read scope.
{slack.from}U01ABCDEFThe Slack user who wrote it (also {execution.channelSender}).
{slack.profileName}MariaThe user's display name, when Slack tells it (needs the users:read scope); empty otherwise.
{slack.to}C01ABCDEFThe Slack channel (D… for a direct message) the Session replies to.
{slack.team}T01ABCDEFThe Slack workspace.
{slack.thread}1700000000.000100The thread replies go to: the message's thread, or the message itself.
{slack.messageId}1700000000.000100Slack's id (ts) of the message.
{slack.channel}SlackChannelThe Session's Channel.
{slack.isTest}falseTrue when the start ran in the test chat.

Behavior​

  1. A message that may start a Session (see How it works) starts one on the SlackChannel Channel at this event.
  2. The Session is bound to the Slack channel, thread and user, and the message is the first entry of the conversation history.
  3. The message's files are downloaded and saved like an InputTask's Store files in. A file that cannot be kept (too large, deleted, the files:read scope missing, storage full) is noted in the Session log and the Session starts anyway.
  4. {slack} is written and the Token moves on at once. The first InputTask posts its prompt and waits for the user's next message: the message that started the Session is already in {slack.text}.

Testing from the chat. Start a chat on the Environment in the editor (or with the assistant's chat_start_session tool, giving test data such as { "text": "hi", "from": "U123", "to": "D456" }). With a text, {slack} is written from the test data at once (from, to, team, thread, profileName are optional); without one, the Slack start asks for the message and your next chat message (and the files you attach) becomes {slack.text} / {slack.files}. {slack.isTest} is true, and the rest of the flow runs on the chat. On any other Channel (a call, a form) the Slack start stops the Session with error 8025 (StartWrongChannel).

Starting from your own systems. POST /Slack/start/<environment token> with Authorization: Bearer <signing secret> and a JSON body { "channel": "C01ABCDEF", "user": "U01ABCDEF", "initialMessage": "…", "threadTs": "…" } starts a Session in that conversation (nothing happens when it already has one); {slack.text} is the initial message.

Processes built with the plain Start keep working on Slack: a Slack message still starts them and the conversation works as before, but they get no {slack} variable. Replace the Start with a Slack start to use it.

Example​

A user writes I need help with my invoice to the app in a direct message. A Session starts, {slack.text} holds the message and {slack.profileName} the user's name, and the Token moves on, typically to an OutputTask replying Hi {slack.profileName}, let me look into it. in the thread.

OutputTask (Channel: SlackChannel)​

Posts a text message, and optionally files, to the Slack channel or thread.

Properties​

PropertyTypeRequiredExampleDescription
Output MessagestringYesHi {userName}, your ticket is open.The text posted to Slack. Supports {variable} placeholders for dynamic values.
Attachmentsfile fieldNoref-session:{report}, ref-storage:terms.pdfFiles posted with the message, separated by commas: Session files (from the Standard Module's Create file Task, or a file list such as an InputTask's Store files in) or saved files. See File fields. Needs the files:write scope.

Behavior​

  1. The Task resolves every {variable} placeholder against the current Session variables.
  2. Special characters in the resolved text are posted as literal text, so user-supplied content cannot trigger @channel pings or disguised links.
  3. If Attachments is set, the files are uploaded to Slack first: at most 10 files and 15 MB per message. A file that is too large, missing or not a file stops the Task with an error, and nothing is posted.
  4. It posts the message to the Slack channel or to the conversation's thread when Use Threads for Replies is on, then shares the files right below it, in the same thread. When the message resolves to empty text, only the files are posted.
  5. The conversation history of the Session lists the files with the message.
  6. If Slack rejects the message or the files, the Session records an error instead of continuing silently.

Example​

Output Message Your order {orderId} has shipped. with {orderId} = A-1042 posts Your order A-1042 has shipped. as a reply in the conversation's thread, and the Token immediately continues to the next node.

InputTask (Channel: SlackChannel)​

Posts a prompt and waits for the Slack user's next message.

Properties​

PropertyTypeRequiredExampleDescription
Prompt MessagestringYesWhat is your order number?Message posted to Slack before waiting for a reply. Supports {variable} placeholders.
Store result instringYes{userReply}On the SlackChannel Channel, type the variable as a {name} reference. The user's reply text is saved there.
Store files instringNo{docs}The files that came with the reply, as a file list: {docs.count}, {docs.names}, {docs.0}, {docs.0.name}. Needs the files:read scope. Leave empty when the flow does not use files.

Behavior​

  1. The Task resolves {variable} placeholders in the prompt and posts it to the channel or thread.
  2. The Token pauses at this node and the Session waits for the next message from the Slack user in the same conversation.
  3. The reply text is written into the configured variable and the Token resumes.
  4. If Store files in is set, the files of the reply are downloaded from Slack and kept as file variables (in your Workspace storage, or inline, as the Standard Module's File storage setting says). At most 5 files and 10 MB per reply are kept; a file past these limits, deleted, or shared from another organization (Slack Connect) is skipped and noted in the Session log. With no files the list is empty ({docs.count} is 0).
  5. Without Store files in, nothing is downloaded: the name, type and size of each file are stored in {<variable>_files} (for example {userReply_files}), and the conversation history lists the file names.

Example​

Prompt Message What is your order number?, Store result in {orderId}. The bot posts the question in the thread and the Session waits. The user replies A-1042; the Token resumes and {orderId} now holds A-1042, ready for a later Task to look the order up.

EndTask (Channel: SlackChannel)​

Finalizes the Slack Session.

Properties​

The EndTask has no properties.

Behavior​

The Slack conversation is released and the Session is marked as completed: the user's next message can start a new Session. A Session that ends any other way (an error, a flow without an EndTask) releases it too. The EndTask posts nothing: to close with a message, place an OutputTask right before the End Event.

Example​

OutputTask Output Message Thanks {userName}, your request is closed. followed by the End Event: with {userName} = Maria the thread reads Thanks Maria, your request is closed., then the Session ends and the thread is released.

SlackBlockMessage (any Channel)​

Sends a rich message using Slack Block Kit formatting: sections, images, buttons and other layout blocks. In a Slack Session it replies in the conversation; from any other flow (a web chat, a form, a call, a schedule) it posts to a channel with the Environment's bot token, like SlackNotification.

Properties​

PropertyTypeRequiredExampleDescription
ChannelstringNoC01ABCDEF or #opsEmpty: in a Slack Session, the conversation (its thread when Use Threads for Replies is on); in any other flow, the Default Channel of the configuration. A channel ID or name posts there instead, outside any thread. Supports {variable} placeholders. The bot must be a member of the channel.
Notification textstringNoOrder {orderId} is readyPlain text Slack shows in notifications, previews and to screen readers instead of the blocks. Default: New message.
Block Kit JSONstringYes[{"type":"section","text":{"type":"mrkdwn","text":"Order *{orderId}* is ready."}}]The blocks: a JSON array of blocks, a single block, or the { "blocks": [...] } that Slack's Block Kit Builder produces. Put {variable} placeholders inside text values; each value is inserted as escaped text, so user-supplied content cannot add blocks, buttons or links, nor ping @channel. A field holding a single {variable} and nothing else uses that variable's value as the JSON itself, for blocks built earlier in the flow (for example by a JavaScript Task).

A minimal Block Kit JSON value with a header and a section:

[
{ "type": "header", "text": { "type": "plain_text", "text": "Order update" } },
{ "type": "section", "text": { "type": "mrkdwn", "text": "Order *{orderId}* shipped to {customerName}." } }
]

Behavior​

  1. The Task resolves every {variable} placeholder in the Block Kit JSON. Each value is escaped as it is inserted, so it stays inside its text field.
  2. JSON that is not valid Block Kit (not an array of blocks, a block without type, empty) stops the Task with error 3005 on its error branch; nothing is posted.
  3. It posts the blocks where Channel says. In a Slack Session the message joins the conversation history (as its Notification text, or the JSON).
  4. If Slack rejects the message (unknown block type, the bot not in the channel), the Task takes its error branch with error 4001 and Slack's reason.

In the editor's test chat the Session runs on the Chat Channel, so an empty Channel means the Default Channel: the message is really posted there (or the Task stops with 4012 when there is none).

Example​

Block Kit JSON [{"type":"section","text":{"type":"mrkdwn","text":"*Ticket {ticketId}* was assigned to {agentName}."}}] with {ticketId} = T-88 and {agentName} = Luca posts a formatted section reading Ticket T-88 was assigned to Luca, and the Token continues to the next node.

info

Block Kit supports many block types: sections, images, dividers, context, and interactive elements. See the Slack Block Kit documentation for the payload structure, and use Slack's Block Kit Builder to design the JSON before pasting it into the Task. Button clicks are not delivered to the flow: ask for the answer with an InputTask.

SlackNotification (any Channel)​

Posts a message, optionally with files, to a Slack channel from any flow: a web chat, a form, a voice call, a schedule or an email. Unlike the Tasks above, it does not need a Slack Session: it is a one-way post to a channel your Slack app is in.

note

This Task was called SlackPostMessage before October 2026. A flow built with the old Task must remove it and add SlackNotification in its place (same fields), then be redeployed.

Properties​

PropertyTypeRequiredExampleDescription
ChannelstringNoC01ABCDEF or #leadsThe channel to post to: its ID or its name. Supports {variable} placeholders. Empty uses the Default Channel of the Environment configuration. The bot must be a member of the channel.
MessagestringYes*New lead*: {form.fields.email}Slack-formatted text. Supports {variable} placeholders; their values are posted as literal text, so submitted content cannot trigger @channel pings or disguised links.
Attachmentsfile fieldNoref-session:{report}Files posted right below the message, separated by commas: Session files (from the Standard Module's Create file Task, file lists such as an InputTask's Store files in) or saved files (ref-storage:terms.pdf). At most 10 files and 15 MB. Needs the files:write scope.

Behavior​

  1. The Task resolves the Channel and the Message. An empty Message, or no Channel and no Default Channel, stops the Task with error 3005.
  2. If Attachments is set, the files are uploaded to Slack first. A missing files:write scope (4008) or a bad file (3005) stops the Task before anything is posted.
  3. It posts the message to the channel, then shares the files right below it.
  4. Any failure routes the Task's error branch, and the flow continues either way. If the message went out but the files could not be shared, the error says so.

The post is not part of a conversation: it is not threaded, does not wait for a reply, and is not added to the Session's conversation history.

Example​

A scheduled flow builds the day's leads as a CSV with the Standard Module's Create file Task (Store result in {report}). SlackNotification with Channel #sales, Message *Today's leads* are in the attached file. and Attachments ref-session:{report} posts the message in #sales with the CSV right below it.

Common recipes​

Answer a support request in its own thread​

Greet the user, collect the details, and confirm all inside one Slack thread so the channel stays readable.

  1. Environment configuration Session Mode PerThread, Use Threads for Replies on, Start sessions from @mentions on.
  2. Slack start A user mentions the app: @helpdesk my laptop won't start. {slack.text} holds my laptop won't start.
  3. OutputTask Output Message Hi {slack.profileName}! I can open a support ticket for you. posts the greeting as a reply in the thread started by the user's message.
  4. InputTask Prompt Message Anything else I should know?, Store result in {issueText}. The Session waits for the user's reply in the same thread.
  5. OutputTask Output Message Thanks! Your ticket has been created: "{slack.text}" ({issueText}).
  6. EndTask the Session ends and the thread is released.

Post a rich order summary and collect a confirmation​

Show the order with Block Kit formatting, then branch on the user's reply with the Standard Module's SwitchTask.

  1. SlackBlockMessage Block Kit JSON [{"type":"section","text":{"type":"mrkdwn","text":"*Order {orderId}*\nTotal: {orderTotal}"}}] posts the formatted summary.
  2. InputTask Prompt Message Reply *yes* to confirm or *no* to cancel., Store result in {confirmReply}.
  3. SwitchTask (Standard Module) Variable to evaluate {confirmReply}; Case: Op Contains, Value yes, Branch confirm; Default branch other. Label the outgoing Sequence Flows confirm and other.
  4. OutputTask, one per branch confirm posts Order {orderId} confirmed!; other posts Order {orderId} was cancelled.
  5. EndTask ends the Session.

Return a processed file in the same thread​

The user sends a CSV, the flow reads it and sends back a report.

  1. Slack app Bot Token Scopes include files:read and files:write (see Bot token scopes for files).
  2. InputTask Prompt Message Send me the CSV export., Store result in {note}, Store files in {upload}.
  3. Read file (Standard Module) File {upload.0}, Read as Text, Store result in {csvText}.
  4. Create file (Standard Module) builds the report from the processed data, Store result in {report}.
  5. OutputTask Output Message Here is your report, {execution.channelSender}., Attachments {report}. The message and the file appear in the thread.
  6. EndTask ends the Session.

Best practices​

  1. Use thread-based Session Mode PerThread keeps each conversation isolated in its own Slack thread, preventing cross-talk between users.
  2. Secure your signing secret never share or expose the signing secret set in the Environment configuration. Rotate it if compromised.
  3. Test from the chat first the Slack start runs in the editor's test chat with test data, so you can try the flow before connecting a Slack app. Keep Channel messages off unless every message in the channel should start a Session.
  4. Design Block Kit JSON in Slack's Block Kit Builder paste a working payload into the SlackBlockMessage Task, then add {variable} placeholders inside text fields only.

Error codes​

CodeNameDescription
3005TaskInvalidDataA required Task field (e.g. the message or the store variable) is empty, an Attachments entry is not a usable file, or the Block Kit JSON is not valid. See full details
4001ModuleExceptionSlack rejected the message or an unexpected error occurred while calling Slack. See full details
4008ModuleMissingConfigurationThe Bot Token is not set in the Environment configuration, or it lacks the files:write / files:read scope a file Task needs. See full details
4012ModuleInvalidDataThe Session is not bound to a Slack conversation, or SlackBlockMessage has no channel to post to. See full details
4022ModuleLogWarningA file the user sent was skipped (over the limits, deleted, without a download link, or not readable when the Session started); the Session continues. See full details
8025StartWrongChannelA Slack start ran on another Channel (a call, a form): add a start for that Channel. See full details
9007PlanStorageLimitReachedThe files of a reply could not be kept: the Workspace storage is full. See full details