Go to Slack

Making messages interactive

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.

A gorgeous message

Provide users with direct paths to simple goals with message buttons:

An approval workflow powered by message buttons

Or let users navigate through more nuanced options with message menus:

Triforce Project PM

The interactive message framework

Interactive messages are much like other messages, only they contain buttons or a variety of menus types. Rather than remaining mostly static, interactive messages evolve over time.

Message buttons and menus may travel almost anywhere a message goes.

Attach them:

Yes, interactive messages work on Enterprise Grid and on internal integrations built for your workspace. A Slack app is required.

Lifecycle of a typical interactive message flow:

Whether using buttons or menus, interactive messages flow like so:

  1. Your application produces a message containing actions. Maybe it has buttons. Maybe it has menus. Maybe it has both! Now all the users who can read it can interact with it.

  2. 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.

  3. 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.

  4. 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 even more simply 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.

  5. 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 actions.

  6. 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.

  7. 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.

Interaction types

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:

  • Message buttons are brief, decisive, and compelling decision-making aids. We recommend starting with buttons if you're new to interactive messages.
  • Message menus allow more studied conclusions to be drawn with dynamic, purpose-driven drop downs.

Readying your application for interactive messages

Posting messages with buttons requires creating a Slack app. Interactive messages cannot be posted using a legacy custom integration. Create an app if you don't already have one.

Preparing your Request URL

Navigate to your application management tool and find your app's Interactive Components section.

A screenshot of the sidebar where you find this dialog

Here you'll find a interface for setting your Request URL.

A screenshot of the message actions configuration dialog

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 users later in this doc for more detail on how to process these requests.

Request URL SSL certificate requirements

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, simple providers:

Many find a HTTP proxying tool like ngrok useful while developing their app. This tutorial outlines getting started with ngrok.

Asking for the appropriate scopes

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 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)

Building workflows

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. Simple 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.

Posting interactive messages

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 catch is that your interactive messages must be posted from a Slack apptest tokens and legacy custom integrations will not work with interactive messages.

Using 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

If working with a well-supported client library or framework, this detail is likely blissfully hidden from you.

Receiving action invocations

When users interact with buttons or menus provided by 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.

Validating tokens

Important! Your Slack application record contains a verification code used for interactive messages and slash commands. You'll find it in the App Credentials section of your app's Basic Information page.

The verification codes used by slash commands and message buttons

When your Request URL is executed, validate the token field value you receive as part of the payload against your recorded value. If they do not match, do not respond to the request with a 200 OK or other message.

Now that that's out of the way, it's time to respond to the action.

Responding to message actions

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.

Responding right away

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 false in the replace_original field.

Responding incrementally with response_url

Use the response URL provided in the post to:

  • Replace the current message
  • Respond with a public message in the channel
  • Respond with an ephemeral message in the channel that only the acting user will see

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.

Using 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.

Responding with an error message

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.

Replacing the original message

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.

Determining user identity against your service

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.

Review our guidelines

Field guide

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.

Guidelines and best practices

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:

  • Though messages may contain up to 20 attachments, messages containing buttons or menus shouldn't have more than one or two attachments.
  • Each attachment can contain up to 5 message actions, but it's best to keep options limited and decisive.
  • Use confirmation dialogs, colors, and differentiated button types (primary and danger) sparingly.
  • Message action buttons, menus, and confirmation dialogs may not contain Slack's formatting markup.

Glossary

  • Message Button: A user interface for invoking Attachment Actions. Buttons can be added, removed, changed, and of course clicked. Clicking a button triggers its associated Attachment Action.
  • Message Menu: Another user interface for invoking Attachment Actions. With message menus, a single action may hold many different values for the user to select from. The selection can be pre-populated, dynamically-driven, or you can use a custom type letting you easily utilize user and channel pickers.
  • Interactive Message: Mutable messages appearing in Slack, providing users with message buttons that applications may respond to and modify.
  • Attachments: Message Attachments are contained within messages, and typically offer a means to include rich formatting in messages, such as images, color, and lightweight key/value pairs. They may also contain Attachment Actions.
  • Attachment Actions: Objectives a member may interact with within a Message Attachment, executing an Request URL. The user will see a message button. The result of an invocation may change something in the calculus universe, and if desired, within the parent interactive message.
  • Request URL: URLs associated with your application to complete specific attachment actions. Slack will use this URL when members click buttons that trigger Attachment Actions.
  • External Suggestions URL: URLs associated with your app for dynamically populating message menus with customized options.