Go to Slack

Message Buttons with Node.js

By Steve Hammond

Message buttons in Slack enable a whole new level of interaction between users and the apps they love. This tutorial will guide you through a very basic implementation of the feature. At the end, you'll have a great foundation for building an app that delights your users.

Here’s what you’ll build! 🔨

end product button interaction

It’s a simple app… and that’s the point! By conquering the basics, building interesting and complex interactions suddenly becomes much more attainable.

Before you get started

Message buttons are only available to Slack apps and will not work with stand-alone custom integrations. For the purposes of this tutorial, you will need to have created an app and have installed it on your team using our OAuth process and have included the commands scope.

🚨 If you are new to Slack apps and haven’t yet worked with the OAuth process, it might be best starting HERE and coming back to this tutorial. 🚨

We'll use a Node.js app as an example. The concepts and patterns should readily transfer if your weapon of choice is Python, Ruby, or another web-friendly language.

What you’ll need

  • Slack app (listed here) installed on your team with the commands OAuth scope
  • Server running Node.js
  • Publicly accessible server with a valid SSL certificate
  • Following Node.js modules:

The beginning of your Node.js server file should resemble:

var express = require('express')
var request = require('request')
var bodyParser = require('body-parser')
var app = express()
var urlencodedParser = bodyParser.urlencoded({ extended: false })

Enable interactive messages

First, let’s setup a Request URL to handle interactive message requests:

Important: The Request URL must point to a TLS-enabled HTTPS URL located on a publicly accessible server with a valid SSL certificate.

  1. Select your app here: https://api.slack.com/apps

  2. Click the Interactive Messages link in the menu.

  3. Click Enable Interactive Messages, enter your Request URL and click Enable Interactive Messages again.

    enable interactive messages

This URL is the endpoint on your server for all action messages triggered by users in Slack. You only need one Request URL as the data needed to decipher between users, teams, and button events is delivered with each request. You’ll see more about this later. Promise.

Send buttons from a slash command trigger

Let’s quickly add a slash command to your app called /send-me-buttons:

  1. Select your app here: https://api.slack.com/apps

  2. Click the Slash Commands link in the menu

  3. Click Create New Command, enter /send-me-buttons in the Command field and enter your endpoint to receive your slash command events in the Request URL field. Note that commands bundled with apps only accept a POST routing method.

    create new command

  4. Take note of the Verification Token found on your app’s settings page in Slack under the App Credentials link. This token will be passed with each execution of a slash command and can be used to verify the authenticity of the request.

Add a POST route for slash commands

Now add the route you entered as the Request URL in the previous step. It should look something like this:

app.post('/slack/slash-commands/send-me-buttons', (req, res) =>{

Respond to slash command with message buttons

The request sent to your slash command Request URL is URL encoded so you’ll have to decode this to retrieve the response_URL. You will use this URL to post your button message to your user in Slack.

Add the urlencodedParser variable that you defined earlier as a callback to decode the request body into JSON. The buttons are sent within the attachment of a message as an array of objects called actions. Simply add the code below and there you have it!

app.post('/slack/slash-commands/send-me-buttons', urlencodedParser, (req, res) =>{
    res.status(200).end() // best practice to respond with empty 200 status code
    var reqBody = req.body
    var responseURL = reqBody.response_url
    if (reqBody.token != YOUR_APP_VERIFICATION_TOKEN){
        res.status(403).end("Access forbidden")
        var message = {
            "text": "This is your first interactive message",
            "attachments": [
                    "text": "Building buttons is easy right?",
                    "fallback": "Shame... buttons aren't supported in this land",
                    "callback_id": "button_tutorial",
                    "color": "#3AA3E3",
                    "attachment_type": "default",
                    "actions": [
                            "name": "yes",
                            "text": "yes",
                            "type": "button",
                            "value": "yes"
                            "name": "no",
                            "text": "no",
                            "type": "button",
                            "value": "no"
                            "name": "maybe",
                            "text": "maybe",
                            "type": "button",
                            "value": "maybe",
                            "style": "danger"
        sendMessageToSlackResponseURL(responseURL, message)

Add the following function to send your JSON message to Slack using the response_url that you received in the previous step. You’ll use this function again when you respond to button actions from your user.

function sendMessageToSlackResponseURL(responseURL, JSONmessage){
    var postOptions = {
        uri: responseURL,
        method: 'POST',
        headers: {
            'Content-type': 'application/json'
        json: JSONmessage
    request(postOptions, (error, response, body) => {
        if (error){
            // handle errors as you see fit

As a fun side project, try some different formatting options with the Message Builder.

Receive and respond to button clicks

Now that your Node.js app is setup to send buttons to Slack, let’s configure a POST route to accept button actions.

When a user clicks on a button, your server will be sent a URL encoded request containing a body parameter called payload which will contain data you’ll need to handle the user’s action and to respond accordingly.

Pass the urlencodedParser variable once again as a callback to retrieve the payload. The payload is itself a URL encoded JSON string so use the JSON.parse() method to extract the desired data.

You’ll then invoke your sendMessageToSlackResponseURL function passing the response_url and JSON message to respond as you did with the slash command.

Your interactive messages Request URL handler will look like this:

app.post('/slack/actions', urlencodedParser, (req, res) =>{
    res.status(200).end() // best practice to respond with 200 status
    var actionJSONPayload = JSON.parse(req.body.payload) // parse URL-encoded payload JSON string
    var message = {
        "text": actionJSONPayload.user.name+" clicked: "+actionJSONPayload.actions[0].name,
        "replace_original": false
    sendMessageToSlackResponseURL(actionJSONPayload.response_url, message)

To better understand the above code, here’s an example of the payload you’ll receive when your user clicks the "yes" button:


Last steps

  1. Save your server file

  2. Restart your Node.js app

  3. Execute the /send-me-buttons slash command from your Slack client

  4. Start clicking buttons!


What’s next?

Now that you’ve added message buttons to your app, here’s a list of topics and features to investigate as you enhance your app:

  • Explore richer attachment formatting in your responses

  • Use the chat.update Web API method and the timestamp value to replace the original message. Check our docs for hints.

  • Add a database to your app to manage installations

  • Check out the complete message buttons documentation to see what’s available to your button-enabled app! 🎉

Related documentation