Go to Slack

Enabling interactivity

Transform your Slack app into a powerful workflow partner by making messages interactive.

In our overview of message interactivity we told you about why you might want to publish interactive messages, and explained the general steps to make it happen.

In this guide we'll walk through the interactive message flow from a more technical perspective, and show you how to build a Slack app capable of interacting with users.


Getting started with message interactivity

Before we get into the details, there's a few things to do. First, if you haven't already, familiarize yourself with the interactive message flow and the high-level overview of the steps involved. You'll also need to have created a Slack app, though you won't need to have built it out with any features yet.

When you're ready, it's time to get started.

1. Publishing an interactive message

In order to kick off the interactive message flow, your app first needs to publish a message that contains some interactive elements.

We have a different guide that shows you how to compose rich message layouts. Making a message interactive simply involves including one of the interactive components in one of these layouts.

Consider this example from the rich message layouts guide:

{
  "channel": "C1H9RESGL",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "Danny Torrence left the following review for your property:"
      }
    },
    {
      "type": "section",
      "block_id": "section567",
      "text": {
        "type": "mrkdwn",
        "text": "<https://google.com|Overlook Hotel> \n :star: \n Doors had too many axe holes, guest in room 237 was far too rowdy, whole place felt stuck in the 1920s."
      },
      "accessory": {
        "type": "image",
        "image_url": "https://is5-ssl.mzstatic.com/image/thumb/Purple3/v4/d3/72/5c/d3725c8f-c642-5d69-1904-aa36e4297885/source/256x256bb.jpg",
        "alt_text": "Haunted hotel image"
      }
    },
    {
      "type": "section",
      "block_id": "section789",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Average Rating*\n1.0"
        }
      ]
    }
  ]
}

The example above is composed entirely of 'static' blocks: purely visual elements that do not offer any sort of interaction.

You can also add interactive components into blocks, letting you publish messages with inline buttons, date picker widgets, and more.

Let's add an actions block that contains a button element to the end of the message payload:

{
  "channel": "C1H9RESGL",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "Danny Torrence left the following review for your property:"
      }
    },
    {
      "type": "section",
      "block_id": "section567",
      "text": {
        "type": "mrkdwn",
        "text": "<https://google.com|Overlook Hotel> \n :star: \n Doors had too many axe holes, guest in room 237 was far too rowdy, whole place felt stuck in the 1920s."
      },
      "accessory": {
        "type": "image",
        "image_url": "https://is5-ssl.mzstatic.com/image/thumb/Purple3/v4/d3/72/5c/d3725c8f-c642-5d69-1904-aa36e4297885/source/256x256bb.jpg",
        "alt_text": "Haunted hotel image"
      }
    },
    {
      "type": "section",
      "block_id": "section789",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Average Rating*\n1.0"
        }
      ]
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
            "text": {
                "type": "plain_text",
                "text": "Reply to review",
                "emoji": false
            }
        }
      ]
    }
  ]
}

View this example

You could replace this button with any of the available interactive components, or you could add it as the accessory element in a section block.

Browse the interactive components to see a full list of what's available, or try the message builder tool to visually construct a message with interactive components.

For now, use the above message payload - with a real channel ID - and publish it.

Your app's message will be sitting in channel, waiting for someone to click the button. You now need to prepare the app to be able to handle everything that will happen once someone does.


2. Configure your app for interactions

In the request stage of the interactivity flow, your app will be sent a request payload when an interaction happens. Your app needs to be ready to handle those payloads.

At this stage, you should set a few options which enable interactivity for your app, and tell Slack where to send request payloads:

  1. Go to your apps list and select the one you want to use. This will load its settings pages.
  2. Now click on the Interactive Components feature in the navigation.
  3. When the page loads, toggle the Interactivity switch to on. You'll immediately be presented with a bunch of new options:
    • Request URL - the URL we'll send the request payload to when a user interacts with a message published by your app. You'll need to use a URL that you can setup to handle these payloads, as we'll describe later in this doc. If public distribution is active for your app, this needs to be an HTTPS URL (note: self-signed certificates are not allowed). If you're just building an internal integration solely for your own workspace, it can be plain HTTP.
    • Actions - these settings control custom actions for messages, a separate feature that we won't get into here, but you can read this doc to learn more. You can leave this blank for now.
    • Message Menus - this is a setting that is used by a type of interactive component, a select menu, in order to load a dynamic set of menu options from an external source. You can also leave this blank for now, but after we show you how to publish interactive messages in a later step, you can read the reference guide for this component to see how to configure it.
  4. Once you've filled in the Request URL, click the Save Changes button.

Now that you've setup your app, it can start receiving request payloads. But what should it do with them? Read on!


3. Prepare to receive request payloads

Going back to the interactivity flow, you'll remember that request payloads are sent to your app because a user has clicked on some kind of interactive component in one of your app's published messages.

A request payload is both a way of notifying your app about an interaction and a bundle of information containing the where and when (and many other Ws) of the interaction. It is vital to the interaction process, so your app has to be able to understand it.

The request will be made to your specified request URL in an HTTP POST. The body of that request will contain a payload parameter. Your app should parse this payload parameter as JSON.

After that, you'll have an object like this:

{
  "type": "block_actions",
  "team": { 
    "id": "T9TK3CUKW", 
    "domain": "example" 
  },
  "user": {
    "id": "UA8RXUSPL",
    "username": "jtorrance",
    "team_id": "T9TK3CUKW"
  },
  "api_app_id": "AABA1ABCD",
  "token": "9s8d9as89d8as9d8as989",
  "container": {
    "type": "message_attachment",
    "message_ts": "1548261231.000200",
    "attachment_id": 1,
    "channel_id": "CBR2V3XEX",
    "is_ephemeral": false,
    "is_app_unfurl": false
  },
  "trigger_id": "12321423423.333649436676.d8c1bb837935619ccad0f624c448ffb3",
  "channel": { 
    "id": "CBR2V3XEX", 
    "name": "review-updates" 
  },
  "message": {
    "bot_id": "BAH5CA16Z",
    "type": "message",
    "text": "This content can't be displayed.",
    "user": "UAJ2RU415",
    "ts": "1548261231.000200",
    ...
  },
  "response_url": "https://hooks.slack.com/actions/AABA1ABCD/1232321423432/D09sSasdasdAS9091209",
  "actions": [
    {
      "action_id": "WaXA",
      "block_id": "=qXel",
      "text": { 
        "type": "plain_text", 
        "text": "View", 
        "emoji": true 
      },
      "value": "click_me_123",
      "type": "button",
      "action_ts": "1548426417.840180"
    }
  ]
}

In order to understand how your app should interpret a payload like this, let's take a look at some of the more important fields:

  • type - this helps identify which type of interactive component sent the payload; An interactive element in a block will have a type of block_actions, whereas an interactive element in an attachment will have a type of interactive_message.
  • trigger_id - a temporary ID for the message interaction in your app. This value can be used to open a modals as shown in the modals guide.
  • response_url - if you want to respond to the interaction with a message immediately after receiving the payload, this URL should be used as shown below.
  • user - the user who interacted to trigger this request.
  • message - the message that the user initiated the interaction from. This has been shortened in the example, but will include the full structure of the message, including blocks. If the originating message for the interaction was an ephemeral message, this part will not be included.
  • actions - contains data from the specific interactive component that was used. Interactive messages can contain blocks with multiple interactive components, and those components can each have multiple values selected by users. Taken together these fields help provide the full context of the interaction:
    • block_id - this will identify the block within a message that contained the interactive component that was used. See the reference guide for the block you're using for more info on the block_id field.
    • action_id - this identifies the interactive component itself. Some blocks can contain multiple interactive components, so the block_id alone may not be specific enough to identify the source component. See the reference guide for the interactive element you're using for more info on the action_id field.
    • value - set by your app when you composed the message, this is the value that was specified in the interactive component when an interaction happened. For example, a select menu will have multiple possible values depending on what the user picks from the menu, and value will identify the chosen option. See the reference guide for the interactive element you're using for more info on the value field.

The token value represents the deprecated verification token feature. You should validate the request payload, however, and the best way to do so is to use the signing secret provided to your app.

The rest of the payload identifies the source of the action, giving your app info such as the channel the source message was in, or the workspace (team) that channel is in.

The entirety of this payload will contain all the contextual info that your app needs to be able to figure out what kind of interaction happened.

Use this structure and these fields to interpret the request - you can use as much or as little of the info as your app needs.

For the purposes of this guide, you should grab the channel object, and store that channel's id for later. You'll also want to grab the response_url here too.

Now that your app can receive and understand request payloads, it needs to decide what it will do after one of them is sent.


4. Choose interaction responses

As shown in the interactivity overview, there are two different types of responses to any interaction - an acknowledgment sent via HTTP response, and composed responses sent via a special URL.

Acknowledgment responses

All apps must, as a minimum, acknowledge the receipt of a valid request payload.

To do that, your app must reply to the HTTP POST request with an HTTP 200 OK response.

If your app doesn't acknowledge the request within 3000ms of receiving it, the user may see an error message. On the flip side, the user won't see anything in the event of an acknowledgment response.

If you want to show the user some kind of success message, or anything more substantial, you'll need to use a composed response.

Composed responses using a response_url

If your app has a more complex response in mind, such as those mentioned in our interactivity overview, you can use the response_url contained in the request payload.

This response_url is unique to each request payload, and can be used to send up to 5 responses during a 30 minute window after receipt of the payload.

Within these responses, you can include the JSON of a new message. This message can be composed according to the same message composition guides as any other, with a few exceptions that we'll explain below.

You can use a response_url in the exact same way as an incoming webhook, by making an HTTP POST to it and including a message payload in the HTTP body.

Here's a simple cURL example that you can adapt into your own app:

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "text": "Thanks for your request, we'll process it and get back to you."
}

Replace the example POST URL with the response_url that your app grabbed from the request payload earlier, and publish it.

Publishing ephemeral responses

A message published via response_url will be sent to the same channel as the source interactive message, visible to all users.

If you want to publish an ephemeral message, include an attribute response_type with your message JSON, and set its value to ephemeral:

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "text": "Thanks for your request, we'll process it and get back to you.",
    "response_type": "ephemeral"
}
Updating the source message in response

Any messages published via response_url will leave the source message untouched. You can however use response_url to update the source message of an interaction.

Include an attribute replace_original and set it to true:

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "replace_original": "true",
    "text": "Thanks for your request, we'll process it and get back to you."
}

Non-ephemeral messages can also be updated using chat.update.

Deleting the source message in response

You can also delete the source message of an interaction entirely using response_url.

Include delete_original as the sole attribute in your response_url JSON, with the value set to true:

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "delete_original": "true"
}

Non-ephemeral messages can also be deleted using chat.delete.

If you include a new message playload and delete_original, the original message will be deleted, and your new message published.

Other types of responses

While the method above allows you to publish a message back to the source channel of the interaction, there are lots of other ways you can respond:

Additionally, if your app needs more than 30 minutes to respond with a message, you'll need to publish it in the standard way.


Through this guide you've learned how to add interactive components to messages, prepared your app to handle payloads, and created a simplified interactive flow. From here you can dive into our reference guides to learn more about the different types of blocks you can use, and deepen what your app is capable of doing for users.