Go to Slack

Making messages more interactive with buttons

Simplify complex workflows and empower users to take decisive action by adding interactive buttons to your messages. Make your notifications, slash commands, and bot users more intuitive with progressively evolving responses. All you need is a Slack app.

A gorgeous message

An approval workflow powered by message buttons

Want to build rich interactions like this? Let's cover everything you need to know.


Interactive messages with buttons are just like other messages, except they contain buttons that invoke remote actions on your server.

The lifecycle of an interactive message is something like:

  1. Your application produces a message containing buttons. Maybe the message originated in response to an invoked slash command, or in response to a bot user's trigger phrase. Or maybe your app posted the message manually using an incoming webhook or chat.postMessage. In any case, your Slack app produced a message with buttons, offering users a chance to interact with it.

  2. Users encounter your message and, inspired by its call to action, clicks one of your buttons. This triggers an invocation of your application's associated Action URL.

  3. Slack sends a request to your Action URL, sending all the context needed to identify the originating message, the user that executed the action, and the specific values you've associated with the button. This request also contains a response_url you can use to continue interacting with the user or channel.

  4. Your application responds to the action. If you respond directly to the incoming invocation request, your provided message will replace the existing message. It's also possible to respond with an ephemeral message, visible only to the invoking user. Or you can just respond with HTTP 200 OK and wait to continue the interaction until later using the response_url provided as part of the action.

  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 continue interacting with users through additional message buttons.

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

So that's the map. Now, the territory.

Readying your application for message buttons

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

Preparing your Action URL

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

A screenshot of the sidebar where you find this dialog

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

A screenshot of the message actions configuration dialog

You can only configure one action URL for your application. It will receive actions from all clicks happening throughout messages with buttons your app has produced, across all channels and teams. 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.

Action URL SSL certificate requirements

Action 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:

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)

Crafting your message

You'll be building messages that contain attachments and attachment actions. You may want to review how basic message formatting and message attachments typically work.

Interactive messages are made up of very simple building blocks that can be combined, modified, and removed based on your server's responses to create interactive experiences.

Here's a message with buttons containing simple values for the most important fields you'll use when composing interactive messages. Your messages may get more complex than this, and we'll dive into a more complex example later.

An interactive message containing a list of games to play

This message has some light text, three specific buttons, including one that looks like a destructive action.

Here's the JSON used to compose this message:

    "text": "Would you like to play a game?",
    "attachments": [
            "text": "Choose a game to play",
            "fallback": "You are unable to choose a game",
            "callback_id": "wopr_game",
            "color": "#3AA3E3",
            "attachment_type": "default",
            "actions": [
                    "name": "chess",
                    "text": "Chess",
                    "type": "button",
                    "value": "chess"
                    "name": "maze",
                    "text": "Falken's Maze",
                    "type": "button",
                    "value": "maze"
                    "name": "war",
                    "text": "Thermonuclear War",
                    "style": "danger",
                    "type": "button",
                    "value": "war",
                    "confirm": {
                        "title": "Are you sure?",
                        "text": "Wouldn't you prefer a good game of chess?",
                        "ok_text": "Yes",
                        "dismiss_text": "No"

Try it in the message builder!

To identify a few key parts of this message, besides just the presentation:

If this message appeared within a channel or direct message, and a user clicked on Chess, your registered action URL would receive a payload of JSON identifying the specific set of message buttons:

  • the callback_id you set when creating the message
  • the specific name of the clicked button
  • and the corresponding value of that same clicked button

Let's review all the fields related to creating interactive messages. If you're curious about other formatting opportunities, check our our formatting guide and our details on attaching content to messages.

Fields to make messages interactive

Some of these attributes must be adapted to POST parameters when using chat.postMessage.

Top-level message fields

See the formatting guide for tips on arranging your text.

Field Type Required? Description
text string No The basic text of the message. Only required if the message contains zero attachments.
attachments attachment array No Provide a JSON array of attachment objects. Adds additional components to the message. Messages can contain no more than 20 attachments.
response_type string No Expects one of two values:
  • in_channel — display the message to all users in the channel where a message button was clicked. Messages sent in response to invoked button actions are set to in_channel by default.
  • ephemeral — display the message only to the user who clicked a message button. Messages sent in response to Slash commands are set to ephemeral by default.
This field cannot be specified for a brand new message and must be used only in response to the execution of message button action or a slash command response. Once a response_type is set, it cannot be changed when updating the message.
replace_original boolean No Used only when creating messages in response to a button action invocation. When set to true, the inciting message will be replaced by this message you're providing. When false, the message you're providing is considered a brand new message.
delete_original boolean No Used only when creating messages in response to a button action invocation. When set to true, the inciting message will be deleted and if a message is provided, it will be posted as a brand new message.

Attachment fields

Consult the guide to attaching content to messages for more flavor on working with attachments. Attachments house message buttons.

These fields should be presented as a hash within an array presented in the message's attachments field.

Field Type Required? Description
title string No Provide this attachment with a visual header by providing a short string here.
fallback string Yes A plaintext message displayed to users using an interface that does not support attachments or interactive messages. Consider leaving a URL pointing to your service if the potential message actions are representable outside of Slack. Otherwise, let folks know what they are missing.
callback_id string Yes The provided string will act as a unique identifier for the collection of buttons within the attachment. It will be sent back to your message button action URL with each invoked action. This field is required when the attachment contains message buttons. It is key to identifying the interaction you're working with.
color string No used to visually distinguish an attachment from other messages. Accepts hex values and a few named colors as documented in attaching content to messages. Use sparingly and according to best practices.
actions action array Yes A collection of actions (buttons) to include in the attachment. Required when using message buttons and otherwise not useful. A maximum of 5 actions may be provided.

Action fields

The actions you provide will be rendered as message buttons to users. Be sure and consult our best practices and storyboard your button interactions.

These fields should be provided as a JSON hash within an array as part of an attachment definition defined in the attachments field of a message.

Field Type Required? Description
name string Yes Provide a string to give this specific action a name. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. If multiple actions share the same name, only one of them can be in a triggered state.
text string Yes The user-facing label for the message button representing this action. Cannot contain markup. Best to keep these short and decisive.
style string No Your buttons can have a little extra visual importance added to them, which is especially useful when providing logical default action or highlighting something destructive.
  • default — Yes, it's the default. Buttons will look simple.
  • primary — Use this sparingly, when the button represents a key action to accomplish. You should probably only ever have one primary button within a set.
  • danger — Use this when the consequence of the button click will result in the destruction of something, like a piece of data stored on your servers. Use even more sparingly than primary.
type string Yes Provide nothing but button here. There are no other types of actions today.
value string No Provide a string identifying this specific action. It will be sent to your Action URL along with the name and attachment's callback_id. If providing multiple actions with the same name, value can be strategically used to differentiate intent.
confirm confirmation hash No If you provide a JSON hash of confirmation fields, your button will pop up dialog with your indicated text and choices, giving them one last chance to avoid a destructive action or other undesired outcome.

Confirmation fields

Protect users from destructive actions or particularly distinguished decisions by asking them to confirm their button click one more time. Use confirmation dialogs with care.

These fields should be presented as a JSON hash buried deep within the confirm field of an action within the actions array that's also part of an attachment that's inside the attachments array field of a message.

Field Type Required? Description
title string No Title the pop up window. Please be brief.
text string Yes Describe in detail the consequences of the action and contextualize your button text choices.
ok_text string No The text label for the button to continue with an action. Keep it short. Defaults to Okay.
dismiss_text string No The text label for the button to cancel the action. Keep it short. Defaults to Cancel.

You'll use a combination of all the above fields when producing, updating, and transforming interactive messages.

Here's another example to consider from a local comic book shop that uses Slack for team collaboration:

For a message that looks like this: Looks like a great comic book is back in stock and it's time to ask employees what they think

Use JSON like this:

    "text": "New comic book alert!",
    "attachments": [
            "title": "The Further Adventures of Slackbot",
            "fields": [
                    "title": "Volume",
                    "value": "1",
                    "short": true
                    "title": "Issue",
                    "value": "3",
            "short": true
            "author_name": "Stanford S. Strickland",
            "author_icon": "http://a.slack-edge.com/7f18https://a.slack-edge.com/bfaba/img/api/homepage_custom_integrations-2x.png",
            "image_url": "http://i.imgur.com/OJkaVOI.jpg?1"
            "title": "Synopsis",
            "text": "After @episod pushed exciting changes to a devious new branch back in Issue 1, Slackbot notifies @don about an unexpected deploy..."
            "fallback": "Would you recommend it to customers?",
            "title": "Would you recommend it to customers?",
            "callback_id": "comic_1234_xyz",
            "color": "#3AA3E3",
            "attachment_type": "default",
            "actions": [
                    "name": "recommend",
                    "text": "Recommend",
                    "type": "button",
                    "value": "recommend"
                    "name": "no",
                    "text": "No",
                    "type": "button",
                    "value": "bad"

Try it the message builder!

Responding to message actions

The time has come for your application to respond to a message action.

Your Action URL will receive a HTTP POST request, including a payload body parameter, itself containing an application/x-www-form-urlencoded JSON string.

Here's an example invocation you may receive for the comic book recommendation example above:

  "actions": [
      "name": "recommend",
      "value": "yes"
  "callback_id": "comic_1234_xyz",
  "team": {
    "id": "T47563693",
    "domain": "watermelonsugar"
  "channel": {
    "id": "C065W1189",
    "name": "forgotten-works"
  "user": {
    "id": "U045VRZFT",
    "name": "brautigan"
  "action_ts": "1458170917.164398",
  "message_ts": "1458170866.000004",
  "attachment_id": "1",
  "token": "xAB3yVzGS4BQ3O9FACTa8Ho4",
  "original_message": {"text":"New comic book alert!","attachments":[{"title":"The Further Adventures of Slackbot","fields":[{"title":"Volume","value":"1","short":true},{"title":"Issue","value":"3","short":true}],"author_name":"Stanford S. Strickland","author_icon":"https://api.slack.comhttps://a.slack-edge.com/bfaba/img/api/homepage_custom_integrations-2x.png","image_url":"http://i.imgur.com/OJkaVOI.jpg?1"},{"title":"Synopsis","text":"After @episod pushed exciting changes to a devious new branch back in Issue 1, Slackbot notifies @don about an unexpected deploy..."},{"fallback":"Would you recommend it to customers?","title":"Would you recommend it to customers?","callback_id":"comic_1234_xyz","color":"#3AA3E3","attachment_type":"default","actions":[{"name":"recommend","text":"Recommend","type":"button","value":"recommend"},{"name":"no","text":"No","type":"button","value":"bad"}]}]},
  "response_url": "https://hooks.slack.com/actions/T47563693/6204672533/x7ZLaiVMoECAW50Gw1ZYAXEM"

Let's break this action URI response into its component pieces. You can use these fields to decide what to do next.

Action URL invocation payload

Field Type Description
actions action array An array of actions that were clicked, including the name and value of the actions, as you prepared when creating your message buttons. Though presented as an array, at this time you'll only receive a single action per incoming invocation.
  • name — the string correlating to the name attribute set in the originating action
  • value — the string correlating to the value attribute set in the originating action
callback_id string The string you provided in the original message attachment as the callback_id. Use this to identify the specific set of actions/buttons originally posed. If the value of an action is the answer, callback_id is the specific question that was asked.
team team hash A small set of string attributes about the team where this action occurred.
  • id — A unique identifier for the Slack team where the originating message appeared
  • domain — The slack.com subdomain of that same Slack team, like watermelonsugar
channel channel hash Where it all happened — the user inciting this action clicked a button on a message contained within a channel, and this hash presents attributed about that channel.
  • id — A string identifier for the channel housing the originating message. Channel IDs are unique to the team they appear within.
  • name — The name of the channel the message appeared in, without the leading # character.
user user hash The clicker! The action-invoker! The button-presser! These attributes tell you all about the user who decided to interact your message.
  • id — A string identifier for the user invoking the action. Users IDs are unique to the team they appear within.
  • name — The name of that very same user.
action_ts string The time when the action occurred, expressed in decimal epoch time, wrapped in a string. Like "1458170917.164398"
message_ts string The time when the message containing the action was posted, expressed in decimal epoch time, wrapped in a string. Like "1458170917.164398"
attachment_id string A 1-indexed identifier for the specific attachment within a message that contained this action. In case you were curious or building messages containing buttons within many attachments.
token string This is the same string you received when configuring your application for interactive message support, presented to you on an app details page. Validate this to ensure the request is coming to you from Slack. See below.
original_message object A object hash containing JSON expressing the original message that triggered this action. This is especially useful if you don't retain state or need to know the message's message_ts for use with chat.update This value is not provided for ephemeral messages.
response_url string A string containing a URL, used to respond to this invocation independently from the triggering of your action URL.

Validating Action URL tokens

Your Slack application record contains a verification code used for interactive messages and slash commands.

The verification codes used by slash commands and message buttons

When your action 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.

How to respond to message button actions

There are several different ways you can respond and they can be used in combination together for richer interactions.

When creating new messages or modifying old ones, consult the message field guide above to continue using interactive elements within a progressive workflow.

Respond to the message right away

Respond to the request we send to your Action 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.

Respond incrementally with the 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.

Use chat.update to modify the original message instead

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 action URL invocation. 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 team members.

Responding to the user with an error message

If you would like to let the user know that something went wrong, you can return an ephemeral response containing a helpful error message. To do this, you can either respond directly to the Action request, or use the provided response_url.

You'll want to send a JSON payload that looks like this:

  "response_type": "ephemeral",
  "replace_original": false,
  "text": "Sorry, that didn't work. Please try again."

Replacing the original message

By replacing the original message, you can incrementally change that message's content to reflect the actions taken by team 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 we keep mentioning.

Identifying user identity against your service

If your service or application needs to associate a Slack team member with a specific account within your product, you'll want to unobtrusively link their account to complete the action.

When your Action 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.

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 shouldn't have more than one or two attachments.
  • Each attachment can contain up to 5 message buttons and corresponding 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 and confirmation dialogs may not contain Slack's formatting markup.

Review our guidelines


  • Message Button: The user interface for invoking Attachment Actions. Buttons can be added, removed, changed, and of course clicked. Clicking a button triggers its associated Attachment Action.
  • 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 team member may interact with within a Message Attachment, executing an action 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.
  • Action URL: URLs associated with your application to complete specific attachment actions. Slack will use this URL when team members click buttons that trigger Attachment Actions.