Create your endpoint
The first thing to do, is to create an API with an endpoint accepting POST requests.
In this example, we will be using Node.js and Expressjs.
$ mkdir webhook-api
$ cd webhook-api
$ npm init -y
$ npm install --save express
Then, create a small HTTP server:
// index.js
const express = require('express')
const crypto = require('crypto')
// Constants declarations
const PORT = 8080
const KEY = 'PbjuWrm83Z7ZkflrSWyfEd0yz4Dygmk3' // The same key to set-up later in the dashboard
const ALGO = 'sha256'
const app = express()
// Enable Express to use JSON data format
app.use(express.json())
// Declare your endpoint: POST http://localhost:8080/webhook
app.post('/webhook', (req, res) => {
// Get the "challenge" value from the request's body
const { challenge } = req.body
// Get the signature of the request from the request's headers
const signature = req.headers['x-hub-signature']
// Now let's generate the HMAC SHA256 Hex digest of the
// challenge to verify the authenticity of the request
const hash = crypto
.createHmac(ALGO, KEY)
.update(JSON.stringify({ challenge }))
.digest('hex')
// Now check the integrity
if (hash !== signature) {
// Do something with the integrity failure
}
// Now get the full notification data
const notification = req.body
// Do stuff with the received notification payload
// Now let's build the response to send to Bodyguard
const response = { challenge }
// And send it back
res
.header('Content-Type', 'application/json')
.send(response)
})
// Run your tiny API
app.listen(PORT, () => console.log(`Webhook example API running on port ${PORT}`))
Then, simply run your API:
$ node index.js
Webhook example API running on port 8080
Expose your endpoint on the Internet
Now that we have our tiny API running and waiting for incoming notifications, it HAS to be accessible from the Internet and through HTTPS protocol.
The following steps are ONLY meant for DEVELOPMENT purpose. Do NOT do that in production.
To expose your endpoint as required, you can use NGROK: See NGROK documentation for installation steps.
Once installed and set up correctly, simply run this command:
$ ngrok http 8080
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Account <your_ngrok_registered@email.address> (Plan: Free)
Version 2.3.40
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://ede9-109-2-22-27.ngrok.io -> http://localhost:8080
Forwarding https://ede9-109-2-22-27.ngrok.io -> http://localhost:8080
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
You are not forced to run this command from your project directory, it will work from anywhere, unless your port 8080 is aleady in use.
As we can see here, NGROK deployed 2 temporary Internet-accessible endpoints that tunnel toward your locally running API:
http://ede9-109-2-22-27.ngrok.io and https://ede9-109-2-22-27.ngrok.io
As said above, we will ONLY send notification through HTTPS connections, so we will use the second one for the next steps.
Endpoint testing
Before being able to register and use your Webhook, a testing process has to be performed in order to make sure that everything is well-configured and ready to use.
So, when you finished entering the informations about your Webhook on the Dashboard, click Test integration button to manually launch the testing process.
It will simply send an HTTP POST request to the Webhook URL you provided, with this simple payload:
{
"challenge": "7cbe2b6a0a5e58934bc3c5ea77be7de6de77e633a1432f0e1883584d135045cd"
}
See the Challenge section for more informations about the response to send.
Simply back the Challenge as requested in the Challenge section, and the testing process will be validated.
If you do not send the expected response, the testing process will fail and you will NOT be able to register and use your Webhook.
MessageUpdated payload
{
"version": 1,
"organization": {
"id": "5d849494-2b0e-49fc-85d5-7197ca62d7fe",
"name": "Bodyguard"
},
"time": 1630940073706,
"eventType": "MESSAGE_UPDATED",
"challenge": "54cddd8ead45bb5661ce229ca67b674e576b959f596851428c4f711724b9cb95",
"messageUpdatedData": {
"previousMessage": {
"resourceType": "TEXT",
"type": "HATEFUL",
"classifications": [
"BODY_SHAMING",
"INSULT"
],
"publishedAt": "2021-06-11T03:42:00.420Z",
"directedAt": "SINGLE_PERSON",
"recommendedAction": "KEEP",
"severity": "HIGH",
"text": "lol",
"reference": "bodyguard/6d0f43f2-5058-4cf1-869f-14a6a52a8b4b"
},
"newMessage": {
"resourceType": "TEXT",
"type": "HATEFUL",
"classifications": [
"BODY_SHAMING",
"INSULT"
],
"publishedAt": "2021-06-11T03:42:00.420Z",
"directedAt": "SINGLE_PERSON",
"recommendedAction": "REMOVE",
"severity": "HIGH",
"text": "lol",
"reference": "bodyguard/6d0f43f2-5058-4cf1-869f-14a6a52a8b4b"
},
"channelId": "cbc601f1-dec6-4594-9476-51628a17499a",
"sourceId": "81310ae5-228e-438e-9720-35f737b9ca9f"
}
}
PostUpdated payload
{
"version": 1,
"organization": {
"id": "5d849494-2b0e-49fc-85d5-7197ca62d7fe",
"name": "Bodyguard"
},
"time": 1630940073706,
"eventType": "CONTENT_UPDATED",
"challenge": "54cddd8ead45bb5661ce229ca67b674e576b959f596851428c4f711724b9cb95",
"contentUpdatedData": {
"previousContent": {
"permalink": "https://link.to.post",
"title": "Hey check this out",
"type": "VIDEO",
"status": "PUBLISHED",
"identifier": "post-1234-abcd"
},
"newContent": {
"permalink": "https://link.to.post",
"title": "Hey check this out",
"type": "VIDEO",
"status": "REMOVED",
"identifier": "post-1234-abcd"
},
"sourceId": "81310ae5-228e-438e-9720-35f737b9ca9f"
}
}
AuthorUpdated payload
{
"version": 1,
"organization": {
"id": "5d849494-2b0e-49fc-85d5-7197ca62d7fe",
"name": "Bodyguard"
},
"time": 1630940073706,
"eventType": "AUTHOR_UPDATED",
"challenge": "54cddd8ead45bb5661ce229ca67b674e576b959f596851428c4f711724b9cb95",
"authorUpdatedData": {
"previousAuthor": {
"birthdate": "1989-10-19T00:00:00.000Z",
"gender": "MALE",
"permalink": "https://link.to.profile",
"status": "ACTIVE",
"identifier": "user/1a2b3c4d"
},
"newAuthor": {
"birthdate": "1989-10-19T00:00:00.000Z",
"gender": "MALE",
"permalink": "https://link.to.profile",
"status": "BANNED",
"identifier": "user/1a2b3c4d"
},
"sourceId": "81310ae5-228e-438e-9720-35f737b9ca9f"
}
}