Go to Slack

Defining and handling message actions

The rundown
Read this if:You want to share and distribute your app.
Read first:Making messages interactive
Read next:Attaching interactive message menusAttaching interactive message buttons

Apps can register custom actions that allow users to do things with messages, similar to built-in actions like adding reactions or message sharing.

These custom actions provide a simple and visual way for people to interact with your app from Slack - perfect for users who aren't comfortable with the verbose nature of slash commands.

An app's actions work with all non-ephemeral messages and will appear for use in any workspace where the app is installed.

Once installed, your app's custom actions appear in the More actions section of each Slack message's context menu, beside default actions.

Want to learn more? The only action (pun definitely intended) to take is to read on (and on):

What are actions?

Actions allow your users to quickly send messages from Slack directly to your app, enabling them to create tasks, comment on messages, follow-up from tickets, and more.

Actions are one way of integrating your app with Slack, similar to the integration points provided by slash commands or interactive messages.

When an action is selected in Slack, your app will be notified with some relevant info; your app could then follow up by:

The potential use cases for custom actions are unbounded. Your app could generate a support ticket from a message, or file away information about an important lead, or maybe it'd let someone save the classic Toto song they just saw posted.

Here's an example for a hypothetical app called Yogi, with an action that would share a message from a channel to Yogi and create a task on that service:

Custom actions being used with Dialogs and a confirmation message

You can see how the custom actions are added to the More actions context menu, how the app triggered a dialog for more input, and followed up with a confirmation message.

If you're intrigued, and want to learn more, keep reading...

Setting up custom actions

In order to start using custom actions with your app, there are a few setup steps you'll need to quickly go through first.

Enabling interactive components

  • Go to the app management dashboard and either pick an existing app, or create a new one.

  • Then go to Features > Interactive Components and, if you haven't already, click on Enable Interactive Components.

  • Now you'll need to add your Request URL. This should be an endpoint in your app that an HTTP POST request will be sent to whenever someone uses one of that app's custom actions - don't worry, we'll explain more about what that HTTP request looks like later, but for now this should be the URL that you plan to use.

    If you're already using some other interactive components like dialogs or message buttons, you should just keep the existing Request URL and use the message_type field that will be sent to your URL with each POST to figure out what kind of component sent the request.

Creating a custom action

  • Click on the Create New Action button under Actions and fill in the following:
    • Action Name: a short, catchy call to action. If your action will trigger a dialog, we recommend including an ellipsis (...) at the end of the action name as a UI convention to indicate this.
    • Short Description: describe what the action does for the benefit of a potential user.
    • Callback ID: a string that will be sent to your Request URL when an action is used, we'll talk about this more later on.

Create new Action Dialog

  • Click that tempting green Create button, and you'll be sent back to the Interactive Components page.

  • On that page you'll either need to click the Save Changes button, or if you're just setting up interactive components for the first time, you'll need to click the Enable Interactive Components button (yes, you've already clicked one of those before, but you'll need to do it again at this point).

Adding the right permissions

To make the actions available in the UI, you need to enable the commands permission scope.

  • From the app dashboard, click the menu item OAuth & Permissions.

  • Under Scopes, type commands into the text field, select the correct permission to add it, then click Save Changes.

  • If your app isn't already requesting commands at installation, it may need to be reinstalled before actions may be used, and if your app is published in the App Directory, the changes will be reflected only after the app is reviewed and approved.

That's it! You've created your first custom action (and you can create 4 more - each app can have up to 5 custom actions) and are ready to prepare your app to receive the HTTP requests and do something in response.

Implementing actions in your app

When you create custom actions, and they are used by someone in a channel, congratulations! Now you've got to handle that eventuality, so here's what will happen:

  1. An HTTP POST request will be sent to your Request URL.
  2. Your app will process the payload contained in that request.
  3. Your app will do something in response.

The first step is handled by Slack, so let's run through the other two.

Processing the request payload

When an action is invoked, a request will be sent to the app's Request URL as configured above; the request body will contain a payload parameter that your app should parse for JSON.

After you've parsed it, you'll have something like this:

  "token": "Nj2rfC2hU8mAfgaJLemZgO7H",
  "callback_id": "chirp_message",
  "type": "message_action",
  "trigger_id": "13345224609.8534564800.6f8ab1f53e13d0cd15f96106292d5536",
  "response_url": "https://hooks.slack.com/app-actions/T0MJR11A4/21974584944/yk1S9ndf35Q1flupVG5JbpM6",
  "team": {
    "id": "T0MJRM1A7",
    "domain": "pandamonium",
  "channel": {
    "id": "D0LFFBKLZ",
    "name": "cats"
  "user": {
    "id": "U0D15K92L",
    "name": "dr_maomao"
  "message": {
    "type": "message",
    "user": "U0MJRG1AL",
    "ts": "1516229207.000133",
    "text": "World's smallest big cat! <https://youtube.com/watch?v=W86cTIoMv2U>"

Let's take a look at some of the attributes here:

  • callback_id - the action's callback ID that you defined earlier.
  • type - this helps identify which type of interactive component sent the request; the specific type for a custom action will be message_action.
  • trigger_id - a temporary ID for the message interaction in your app. The value can be used to open a dialog as shown below.
  • response_url - if you want to respond to the action with a message after receiving the payload, this URL should be used as shown below.
  • user - the user who clicked on the action to trigger this request.
  • message - the message that the user initiated the action with.
  • token - this is a deprecated verification token shared between your app and Slack, previously used to check that an incoming request originates from Slack.

The token value, which represents a verification token, is deprecated. The best way to verify the authenticity of a Slack request is to use the signing secret provided to your app.

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

Now you've parsed the payload basketball that Slack passed to you, it's time to start figuring out your next move so you finish with a slam dunk.

Responding to actions

As soon as your app receives the request sent by an action a countdown begins, because this message will self-destruct in 3 seconds.

In other words, if your app doesn't respond with an HTTP status 200 OK within 3000ms of receiving the request, the user who clicked to initiate the action will receive the following error message:

Actions timeout error message

The most basic response that your app can make to an action is to simply acknowledge that it was successfully received by returning that 200 status code - the user won't see any success message, or any other kind of response, but they won't see any error message either.

As a best practice, we recommend that your app should at a minimum use the response_url to notify the user with an appropriate in-channel or ephemeral message, even if it's just a simple acknowledgement of the action, and we'll show you how to do that below.

Publishing response messages

If you want to send a message from the app as a response to an action, you can use the response_url received in the action's payload to post an in-channel or ephemeral message up to 5 times in the 30 minutes after the source action happens.

To do this, simply POST a JSON payload to that response_url - in the same way as any other write method - that will look something like this:

    "text": "It's 80 degrees right now.",
    "attachments": [
            "text":"Partly cloudy today and tomorrow"
    "response_type": "in_channel"

These JSON elements are mostly self-explanatory, but here's what they do:

  • text (required) - the message text; as with any other type of message this is customizable with standard message formatting).
  • attachments - an array of added message attachments.
  • response_type - indicates how the message should be posted. If set to ephemeral, the message will be only visible to the user who triggered the action. If set to in_channel, it will be shared by the app to the channel that the source message was in. This defaults to ephemeral but we recommend that you always declare your intended type.

As well as responding with messages, you might want to do something a little bit more sophisticated so here's a reminder of some ways you can respond to an action. The data you got from the action's payload above is generally all you need if you're going to respond using one of those APIs, but there are some specific restrictions to using dialogs as a response that we'll mention below.

Opening a dialog

If your app needs extra info to complete the task, you'll want to open a dialog to get that info from the user - the BlueBird sample we showed up top uses a dialog to confirm the exact message to be shared, for example.

When you need a dialog, your app should open it immediately by using the trigger_id it received in the action's payload with dialog.open. That 3000ms restriction we mentioned before is important again here because that's the limit for how long your app can wait before opening a dialog; you'll receive a trigger_expired error after this point.

After the dialog is completed by the user, you might want to follow that up with a message response, or at least simply confirm to the user that the dialog was successfully completed; you can do that using the payload's response_url, as above.


  • Each app can have up to 5 custom actions.
  • Actions are not available with ephemeral messages.