Mattermost Platform

Mattermost integrations: Sending and receiving data with outgoing webhooks

Outgoing webhooks are the easiest way to send a request or data to another program from a public channel

(Originally published at controlaltdeliet.be)

In a previous article, you learned how to receive data from an external source. In this article, you learn how to send a request or data to an external source using outgoing webhooks.

As you learned from the first article in this series, we already receive alerts in Mattermost when the temperature of our fridge is too high. But what if we want to send a request to our fridges to give us the current temperature? If everything has returned to normal, we don’t need to go to the fridges and check them physically.

This can be accomplished using outgoing webhooks.

Outgoing webhooks are only available in public channels and you can’t send any variables with them. If you want to send variables or send and receive data in private channels or group messages, you need to use slash commands, which I’ll discuss further in the third installment of this series.

Create an outgoing webhook in four steps

1. Go to the Menu and choose Integrations

2. At the following screen, choose Outgoing Webhooks

3. After that, you’ll be able to see your existing outgoing webhooks and have the option to add a new one.

4. Next, set up the outgoing webhook.

Follow these steps:

  1. Give the outgoing webhook a title.
  2. Give the outgoing webhook a description.
  3. Specify the Content Type of the request you will receive (e.g., www-form-urlencode or JSON).
  4. Select a channel or specify the trigger words to send an HTTP POST request to the webhook. (Trigger words only work in public channels. For using your outgoing webhook in a private messages you need to use a slash command.)
  5. Select the default channel that receives the answers that the outgoing webhook will send.
  6. Choose if you want to lock the incoming webhook to the channel or that it is allowed to send to other channels as well.
  7. Choose if the trigger word has to be an exact match.
  8. Give the URLs where the POST-request will be sent to. Jump to this if you are planning to send to internal IP-addresses! We will be using HTTPS and if you have a self-signed certificate, you need to enable unsecure connections. Here is how you do that.
  9. Give the username that will be displayed.
  10. Give the URL for the profile picture that will be displayed.
  11. Click the save button.

At this point, you’ll receive confirmation that the webhook is created. You’ll also get a very important token that we will need in our next step.

Write some code

Congratulations! You made an outgoing webhook. Now, we’ll write some code that can listen to your requests.

First things first: We will send encrypted HTTPS requests. It’s a good habit.

Being by making a self-signed certificate:

openssl req -nodes -new -x509 -keyout server.key -out server.cert
cat server.key server.cert > server.pem     

Now, the magical Python code (below is a NodeJS example):

#!/usr/bin/python3
import bottle
from bottle import Bottle, post, run, ServerAdapter,response
###CHANGE THE FOLLOWING LINES TO YOUR CONFIGURATION###
serverport = 4443
token="yourbrandnewtoken"
certfile="/path/to/your/fresh/created/server.pem"

@post("/temperature")
def hello():
   response.set_header('MATTERMOST_TOKEN', token)
   response.content_type = 'application/json'
   return '{"text":"All is going well, thank you for your kind interest."}'
        
###COMMENT THIS CLASS IF YOU DON'T WANT HTTPS###
class SSLWSGIRefServer(ServerAdapter):
   def run(self, handler):
      from wsgiref.simple_server import make_server, WSGIRequestHandler
      import ssl
      if self.quiet:
          class QuietHandler(WSGIRequestHandler):
             def log_request(*args, **kw): pass
          self.options['handler_class'] = QuietHandler
        srv = make_server(self.host, self.port, handler, **self.options)
        srv.socket = ssl.wrap_socket (
            srv.socket,
            certfile=certfile, 
            server_side=True)
        srv.serve_forever()
        
  if __name__ == "__main__":
    '''UNCOMMENT IF YOU WANT TO RUN THE SCRIPT IN THE BACKGROUND###
    try:
        # Store the Fork PID
        pid = os.fork()
    
        if pid > 0:
            print ('PID: %d' % pid)
            os._exit(0)
    
    except :
        print ('Unable to fork. Error: ')
        os._exit(1)
    '''
srv = SSLWSGIRefServer(host="0.0.0.0", port=serverport)
run(server=srv)

Important note: Your response has to be JSON-formatted.

const https = require('https');
const fs = require('fs');

//MODIFY THE NEXT 4 LINES
const token="yourbrandnewtoken";
const keyfile="/path/to/your/just/created/server.key";
const certfile="/path/to/your/just/created/server.cert";
const serverport=4443;    
    
const options = {
    key: fs.readFileSync(keyfile),
    cert: fs.readFileSync(certfile)
    };
    
https.createServer(options, (req, res) => {
  res.writeHead(200, {'Content-Type': 'application/json'});
  res.writeHead(200, {'MATTERMOST_TOKEN': token});
  res.write('{"text":"All is going very well, thank you for your kind interest."}');
  res.end();
  }).listen(serverport);

More options

Your response isn’t limited to just text. There are some options you can send along with your response.

Commenting on the post instead of answering with a new post

When you add a response_type field to your response, there are three possible options:

  • “comment” is a reply to the message that triggered the outgoing webhook
  • “post” creates a new post
  • the blank “” creates a new post
{"text":"All is going very well, thank you for your kind interest.","post":"comment"}

Changing the username

If you want to use another username appearing as sender, you can add the username variable. This setting is default disabled and must be enabled in the Managment Console!

{"text":"All is going very well, thank you for your kind interest.","username":"The Coolest Fridge"}

Changing the profile picture

If you want to change the profile picture, add the icon_url in the request.

'{"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png","text": "The Fonz says the fridge is cool enough"}'

Adding emoticons

If you want to add an emoticon in the text, add the shortcode like :tada: in the text.

'{"text": "Wow, this works :tada:"}'

Adding more lay-out

If you are not familiar with Markdown layout you can find an introduction in the Mattermost Documentation and in this post.

'{"text": "| Left-Aligned  | Center Aligned  | Right Aligned |
| :------------ |:---------------:| -----:|
| Left column 1 | this text       |  $100 |
| Left column 2 | is              |   $10 |
| Left column 3 | centered        |    $1 |"}'

Allow untrusted internal connections

If you want to send your requests to your internal network, you have to specify to which hosts you want to send. Mattermost can only send to the specified internal hosts or hostnames! Adjust this setting by navigating as follows: System Console -> Environment -> Developer.

Unsecure outgoing connections

If you are using self-signed certificates like in our example, you need to allow outgoing HTTPS connections with self-signed unverified certificates. You’ll find this setting here: System Console -> Enviroment -> Web Server.

Debugging

Is something not working as expected? Go to the System Console, click Environment, and you find the Logging section.

In the Logging section, enable Webhook Debugging and set Log Level to Debug.

You’ll find all the relevant information in the logfiles (or in the console if you enabled logging to the console). My most common mistake is a typo in the request. It gives me an “Unable to parse incoming data” error.

More information

For more comprehensive information, check out the official Mattermost developer documentation on outgoing webhooks. And you can always ask for help on the Mattermost Forum, too.

mm

Tom De Moor is the official reviewer of the Dutch translation of Mattermost. He is a technology lover with a broad outlook who uses PHP, Python, NodeJS, MariaDB, MongoDB, and Raspberry Pis on a daily basis. He is also an official drone pilot.