Conversation is central to the chat experience, but messages do more than just communicate. Made interactive, messages inspire decisive, calculated action all from the comfort of Slack.
Provide users with direct paths to goals with message buttons:
Give them the ability to do something with a message via a message shortcut:
Or let users navigate through more nuanced options with message menus:
Interactive messages are much like other messages, only they contain buttons, a variety of menus types, or they have some custom message shortcuts available. Rather than remaining mostly static, interactive messages evolve over time.
Message buttons and menus may travel almost anywhere a message goes.
Attach them:
chat.postMessage
chat.postEphemeral
.Message shortcuts are available on any message, except for those spooky, ghostly ephemeral messages and the best thing is they haunt appear without needing any initial action, unlike buttons or menus.
Yes, interactive messages work on Enterprise Grid and on internal integrations built for your workspace. A Slack app is required.
Whether using buttons, message shortcuts, or menus, interactive messages flow like so:
A message exists, ready for action. If your app created it, it might have buttons, or it might have menus, and if it's not ephemeral it might have custom message shortcuts available. Now all the users who can read it can interact with it.
Users encounter your message and, inspired by its call to action, clicks or makes a selection. This triggers an invocation of your application's associated Request URL.
Slack sends a request to your Request URL, sending all the context needed to identify the originating message, the user executing the action, and the specific values you've associated with the action. This request also contains a response_url
you can use to continue interacting with the user or channel.
Your app responds to the action. If responding directly to the incoming invocation request, your provided message will replace the existing message. Or respond with an ephemeral message, visible only to the invoking user. Or respond with just HTTP 200 OK and delay continuing the interaction until later using the provided response_url
. There are many ways for your app to respond.
Meanwhile: Your application does whatever it does as a result of the intended action to be taken: enqueue a process, save a database row or some prince or princess captured in a castle. All the while your app may continue interacting with users through additional messages and evolving shortcuts.
By using the response_url
, your app can continue interacting with users up to 5 times within 30 minutes of the action invocation. Use this to continue through a workflow until it is complete.
Messages can evolve. By using chat.update
and your created message's message_ts
field, you can modify the original interactive message (including all of its attachments) to add or remove buttons or menus based on user interactions.
With many users interacting with many messages, this lifecycle repeats itself with all its various decisions and destinations.
Messages are truly a garden of forking paths.
While this document covers common aspects of working with both message menus and message buttons, you'll find even more nuance and detail in each action type's dedicated documentation:
Posting messages with buttons requires creating a Slack app. Create an app if you don't already have one.
Navigate to your application management tool and find your app's Interactive Components section.
Here you'll find a interface for setting your Request URL.
You can only configure one Request URL for your application. It will receive actions from all clicks happening throughout messages with buttons your app has produced, across all channels and workspaces. It's a master dispatch station of interactivity. If you're familiar with slash commands, you'll find it behaves very similarly.
In some ways, you're building a guided API on your servers for responding to interactive messages.
See Responding to message actions later in this doc for more detail on how to process these requests.
Request URLs must point to a TLS-enabled HTTPS URL located on a publicly accessible server with a valid SSL certificate.
This testing tool can help you understand whether your HTTPS implementation is valid and publicly accessible.
Don't have a SSL certificate yet? Consider using these low-cost providers:
Many find a HTTP proxying tool like ngrok useful while developing their app. This tutorial outlines getting started with ngrok.
To post messages with buttons and process their interactions, your app just needs to be capable of posting messages. If you have a bot user integration, your bot user already has permission to create messages.
Otherwise, you'll need to request OAuth permission scopes involved with posting messages:
incoming-webhook
(if you're sending messages via incoming webhooks)commands
(if you're using message shortcuts or slash commands)chat:write:user
(if you're sending interactive messages on behalf of users)chat:write:bot
(if you're sending interactive messages on behalf of a bot identity)Work may be a four letter word but workflow is eight. They make work great.
Interactive messages simplify multi-step processes requiring guided user input. Straightforward yes/no/maybe so, either/or/or/or decisions? Throw them a button or two. Do users need to choose from a litany of refined choices? Let them order from a message menu.
Each interaction moves a workflow toward completion.
There are many ways to post messages on Slack. Most of them support interactive messages like little attached riders in the conversation storm.
Most interactive messages begin their lives posted using chat.postMessage
, chat.postEphemeral
, or incoming webhooks. Perhaps they are notifications, or messages from a readily helpful bot user.
Some messages are made interactive, such as when elements are attached to messages mentioning watched link domains with app unfurl's chat.unfurl method.
Many interactive messages initiate through interaction itself — in response to a user-executed slash command or yet another interaction with a different or even the same (!!) interactive message.
The commonality is that your interactive messages must be posted from a Slack app.
chat.postMessage
, chat.postEphemeral
, chat.update
, and chat.unfurl
There's a quirk when posting message attachments, including buttons and menus, to our Web API methods. Whether you're posting a message with chat.postMessage
or chat.postEphemeral
, adding attachments to a link-bearing message with chat.unfurl
, or replacing them with chat.update
— they all only understand application/x-www-form-urlencoded
parameters. But if you're sending messages with response_url
or in direct response to an invocation or with an incoming webhook, messages are posted purely as application/json
.
The way out is somewhat confusing but fully functional: chat.update
, chat.postMessage
, and chat.postEphemeral
support an attachments
parameter that actually expects a URL-encoded string representation of a JSON array of attachments. Say that five times fast, blur your eyes, and apply the same philosophy to the unfurls
parameter in chat.unfurl
.
To send an array with a single attachment with a single action like this:
[
{
"callback_id": "tender_button",
"attachment_type": "default",
"actions": [
{
"name": "press",
"text": "Press",
"type": "button",
"value": "pressed"
}
]
}
]
You'll need to stringify (and optionally minify) and URL-encode it into a POST or URL parameter more like:
%5B%7B%22callback_id%22%3A%22tender_button%22%2C%22attachment_type%22%3A%22default%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22press%22%2C%22text%22%3A%22Press%22%2C%22type%22%3A%22button%22%2C%22value%22%3A%22pressed%22%7D%5D%7D%5D
Check out the callback_id
that identifies the button in this example. All interactive messages with buttons require a callback_id
. Slack uses them to show your app which button a user interacted with.
If working with a well-supported client library or framework, these details are likely blissfully hidden from you.
When users interact with buttons or menus provided by your app or click on an action associated with your app, you'll receive an action invocation at the Request URL you registered when configuring your Slack app.
There are many ways to respond to this action, but before you do anything you'll want to verify the request in fact came from Slack.
Important! Your Slack application panel contains a Signing Secret
used for interactive messages and slash commands. You'll find it in the App Credentials section of your app's Basic Information page.
When your Request URL is pinged, validate the x-slack-signature
header value you receive. If it does not match the signature you compute, do not respond to the request with a 200 OK or other message.
Action payloads include a type
field, allowing you to determine whether the invoked action originates from an interactive_message
or other interactive component, like dialogs.
Here's an example of a parsed payload
dispatched when users select an item from message menus:
{
"type": "interactive_message",
"actions": [
{
"name": "channel_list",
"type": "select",
"selected_options":[
{
"value": "C24BTKDQW"
}
]
}
],
"callback_id": "pick_channel_for_fun",
"team": {
"id": "T1ABCD2E12",
"domain": "hooli-hq"
},
"channel": {
"id": "C123ABC456",
"name": "triage-random"
},
"user": {
"id": "U123ABC456",
"name": "sbutterfield"
},
"action_ts": "1520966872.245369",
"message_ts": "1520965348.000538",
"attachment_id": "1",
"token": "lbAZE0ckwoSNJcsGWE7sqX5j",
"is_app_unfurl": false,
"original_message": {
"text": "",
"username": "Belson Bot",
"bot_id": "B9DKHFZ1E",
"attachments":[
{
"callback_id": "pick_channel_for_fun",
"text": "Choose a channel",
"id": 1,
"color": "2b72cb",
"actions": [
{
"id": "1",
"name": "channel_list",
"text": "Public channels",
"type": "select",
"data_source": "channels"
}
],
"fallback":"Choose a channel"
}
],
"type": "message",
"subtype": "bot_message",
"ts": "1520965348.000538"
},
"response_url": "https://hooks.slack.com/actions/T1ABCD2E12/330361579271/0dAEyLY19ofpLwxqozy3firz",
"trigger_id": "328654886736.72393107734.9a0f78bccc3c64093f4b12fe82ccd51e"
}
Now that that's out of the way, it's time to respond to the action.
There are several different ways to respond and each may be used in combination together for richer interactions.
When creating new messages or modifying old ones, consult the message field guide to understand how to construct all the available options.
Respond to the request we send to your Request URL with a JSON message body directly.
You must respond within 3 seconds. If it takes your application longer to process the request, we recommend responding with a HTTP 200 OK immediately, then use the response_url
to respond five times within thirty minutes.
Responding immediately with a JSON message body will replace the current message in its entirety by default. If you explicitly indicate that you want to create a new message instead, specify a literal false
in the replace_original
field.
response_url
Use the response URL provided in the post to:
You'll be able to use a response_url
five times within 30 minutes. After that, it's best to move on to new messages and new interactions.
chat.update
to modify the original message If you created your message using chat.postMessage
, you can modify the original message with chat.update
by providing the message_ts
value from the original message.
We helpfully provide the original message in the original_message
field of your Request URL invocation. The original_message
is not provided for ephemeral messages. Bot users can modify their messages too!
Interactive messages produced by apps using chat.update
can continue updating messages beyond any time window restrictions imposed on human members.
If you would like to let a user know when something goes wrong, return an ephemeral response containing a helpful error message. To do this, either respond directly to the action request, or use the provided response_url
.
You'll want to send a JSON payload that looks something like this:
{
"response_type": "ephemeral",
"replace_original": false,
"text": "Sorry, that didn't work. Please try again."
}
This sets your response as ephemeral
, neglects to replace the original (since you don't want to spill the beans on your sidebar dialog to everyone else in the channel), and sets the text to display to the user.
By replacing the original message, you can incrementally change that message's content to reflect the actions taken by members. By adding additional interactive messages, you can refine dialog options with users, either by broadcasting to the whole channel or focusing on particular users who've invoked actions via ephemeral messages.
As you replace messages using chat.update
or the replace_original
option, you cannot change a message's type from ephemeral
to in_channel
. Once a message has been issued, it will retain its visibility quality for life.
Since your interactive messages can respond or evolve with additional content and message buttons, this cycle between creating messages, processing responses, and replacing and generating new messages is potentially endless.
Be sure and review those interactive message guidelines.
If your service or application needs to associate a Slack member with a specific account within your product, you'll want to unobtrusively link their account to complete the action.
When your Request URL is triggered, you'll receive the user ID and team ID for the invoker. If they do not yet exist in your system, send them an ephemeral message containing a link they can follow to link accounts on your website.
This is a great opportunity to identify users with Sign in with Slack.
While interactive messages are built on top of typical messages, the workflow may involve many stages, with evolving request and response structures.
If you use chat.postMessage
or chat.postEphemeral
to send interactive messages, you'll need to handle presenting your message attachments as a JSON string placed within an application/x-www-form-urlencoded
request parameter while also balancing using straight-forward application/json
when working with response URLs.
For a comprehensive accounting of all the fields related to interactive messages, please consult the dedicated field guide.
Crafting the ideal message is never easy. Adding interactive flows and additional content while maintaining a productive flow of conversation is even harder!
We've put together a collection of best practices and guidelines to help you build the most effective and unobtrusive messages.
Here are some quick highlights:
primary
and danger
) sparingly.