How to Integrate Webhooks with the Slack API

The first part of the Slack API tutorial series covered Slash commands. Slash commands listen for trigger words in a channel. If it finds those words, it will invoke an external URL that you provide and that web service can then respond to that command. We saw the example of how you could process a “/categories” command and provide it a list of API categories.

As mentioned in the earlier tutorial, Slack provides multiple other ways for you to integrate into the communications platform. These mechanisms include among others a Real Time Messaging API, Incoming/Outgoing Webhooks and Slash commands. In this tutorial, we are going to look at the Incoming and Outgoing Webhooks API Integration to further extend the platform via your custom functionality.

Webhooks - An Introduction

Before we jump into Webhooks integration in Slack, it would be good to define what they are. A Webhook, in simple terms, is a user-defined HTTP callback. It is a mechanism for the system to notify you about an event. How does it notify you? By doing a HTTP POST to the Webhook (i.e. the Web URL that you define). In terms of implementation, you can write an API endpoint that is capable of receiving a HTTP POST in an application stack of your choice.
 
The advantage of a Webhook is that there is no persistent open connection to the system where you keep filtering for events that you are interested in. It is an asynchronous mechanism where you will wait for the system to notify you. The HTTP POST payload will contain the details of the event. Slack supports both incoming and outgoing Webhooks. The incoming Webhook is a URL to which you can post a message into the Slack Channel. The outgoing Webhook is a URL to which Slack will notify you if there are any trigger words entered in a channel. This way the connection is kept to a minimum and you only get the notification if there is an event that meets the conditions.
 
Slack also provides a full real-time API option where you can get information on all the events that are happening in your Slack environment. Examples of such events include a user typing something, channels getting created/destroyed and more. The Real Time Messaging API requires that you establish a connection first with Slack and then you will get notified of events over a persistent WebSockets channel.

Incoming Webhooks

Incoming Webhooks is a mechanism to send messages to your Slack Channel from external sources. These external sources could be any application or service that is capable of sending a JSON payload over HTTP into a Slack Channel. Each Channel will be identified by a unique Incoming Webhook URL to which you can post the message from outside. This configuration is done via the Integrations for your channel.

Examples of an Incoming Webhook could be any periodic messages that you could be sending to the channel. Alternately, if there is some event or notification that your external application deems fit to be sent to the channel, then that can be sent too. Let us look first at a high level schematic diagram of how it works:

webhooks schematic

The flow is given below:

  1. You configure Incoming Webhooks in the Integrations for your channel. Once you set this up, Slack provides a unique incoming Webhook URL and associates that with a channel.
  2. When your External App is ready to send a message to the channel, it will POST a JSON payload to the incoming Webhook URL that you got in step 1.
  3. Slack will determine if a configuration exists.
  4. If one exists, it will route the message to your channel. The message payload will contain items like text, icon and more.

Incoming Webhook Integration Setup

Assuming that you are the Administrator for your Slack Team Account, log in to the Slack Team Portal and go to Configure Integrations as shown below:

Slack configure integrations

This will bring up the Configure Integrations page where you can add various integration services to your Team. Since we are interested in Incoming Webhooks Integration, just type “incoming” in the filter search input box as shown below:

Slack filter search incoming

Click on View and it should bring up a form to configure the Incoming Webhooks Integration.

The first step is to specify the channel that you want to send the messages to as shown below. We have chosen the #general channel over here.

Slack channel general

Click on Add Incoming Webhooks Integration. This will setup the Integration for you and provide you with a publicly reachable Webhook URL. Scroll down to the Integration Settings section and you should see the details with the channel that you selected the URL that you need to post the message to. 

Slack add incoming webhooks integration

Remember to click on Save Settings and you should be all set with the Incoming Webhook configured and waiting for your message.

Incoming Webhook Payload

The next thing to understand is the message that your external service needs to send to the Webhook URL. You can send not just text messages but even richly formatted messages that could include attachments.

If you visit the Integration settings for your Incoming Webhook, you will find that Slack even provides you with a sample Curl command to post a message, so that you can test it out without having an external service running.

As per the documentation, you can send a message to the URL in one of the following two ways:

  • Send a POST request with the JSON payload provided in a request parameter named payload.
  • Send a POST request with the JSON payload in the Body of the request.

Now, let us look at what the JSON payload consists of. You can provide multiple attributes in the JSON payload. Some of them include:

  • Text : This is the text message that needs to be sent.
  • Channel : This is the name of the channel to which you want to post the message. Alternately, you can override the channel name too.
  • Username : The friendly name of the Bot that posts the message.
  • Icon_emoji : An icon for your message. This can be a URL that points to an external image or it can one of various emoji values. For e.g. “:computer:”

Let us look at a sample JSON payload that is given below:

{
“text” : “There were 10 APIs published to ProgrammableWeb today”,
“channel” : “#general”,
“username” : “ProgrammableWeb Bot”,
“icon_emoji” : “:computer:”
}

If you send this JSON payload in the Request Body or as the parameter payload to the Incoming Webhook URL, you will get the following notification in your #general channel as shown below:

Slack modification

Source Code Snippet

Let us take a look at the code snippet that is needed now to post to the Incoming Webhook URL. We have taken the same example from the first part and extended it with another request handler that will be invoked once at day at best. The application is in Go Language but there is no reason you could not do this in any other language capable of doing a HTTP POST. 

package main

import (
      "appengine"
      "appengine/urlfetch"
      "encoding/json"
      "fmt"
      "net/http"
      "net/url"
)

func init() {
      http.HandleFunc("/", handler)
      http.HandleFunc("/reminder", reminder_handler)
}

const (
      incoming_webhook_url string = "YOUR_INCOMING_WEBHOOK_URL"
)

type Message struct {
      Channel    string `json:"channel"`
      Text       string `json:"text"`
      Username   string `json:"username"`
      Icon_emoji string `json:"icon_emoji"`
}

func handler(w http.ResponseWriter, r *http.Request) {

      //Read the Request Parameter "command"
      command := r.FormValue("command")

      //Ideally do other checks for tokens/username/etc

      if command == "/categories" {
            fmt.Fprint(w, "Travel\nMapping\nSports\nFinancial\nMusic\nFood\nWeather")
      } else {
            fmt.Fprint(w, "I do not understand your command.")
      }
}

func reminder_handler(w http.ResponseWriter, r *http.Request) {
      c := appengine.NewContext(r)

      //Invoke the Slack Team + Channel Endpoint for incoming Webhook

      m := Message{"#general", apis_today(), "ProgrammableWeb-Bot", ":computer:"}
      b, err := json.Marshal(m)
      if err != nil {
            c.Errorf("%v", err)
            return
      }

      client := urlfetch.Client(c)

      v := url.Values{}
      v.Set("payload", string(b))
      _, err = client.PostForm(incoming_webhook_url, v)
      if err != nil {
            c.Errorf("Exception while posting to Slack Channel : %v", err)
            return
      }
}

func apis_today() string {
      //Ideally go and fetch the number of new APIs added today
      num_apis := 10
      return fmt.Sprintf("There were %v APIs added today", num_apis)
}

Let us look at the source code in brief:

  1. We define a constant incoming_webhook_url that will hold the value of the Incoming Webhook URL that you got from your Integration Settings.
  2. We define a struct in Go Language that contains the JSON Payload that we will be sending. The message struct will have the attributes like text, icon_emoji, username and channel name.
  3. Finally, look at the function reminder_handler. We create an instance of the message with the values for the attributes and then send them in the payload parameter via the POST to the Webhook URL. It’s as simple as that.

If you are deploying this on Google App EngineTrack this API, you can make use of the Cron Jobs support that will invoke your reminder_handler method once for every time interval that you define. For example you could run that once a day, or every few hours or minutes, etc. 

Romin Irani Romin loves learning about new technologies and teaching it to others. His passion is to help developers succeed.

Comments