Examples
Create your webhook endpoint
The first thing to do is to create an API with an endpoint accepting POST requests.
const express = require('express')
const crypto = require('crypto')
// Constants declarations
const PORT = 8080
const KEY = 'YOUR_OWN_SECRET_KEY' // 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
// request body using the secret key to verify the authenticity of the request.
// Note that the signature header has been generated with a stringified JSON without any whitespaces.
// Therefore, to generate your own expected signature, your body MUST be stringified without any separator nor whitespace.
const hash = crypto.createHmac(ALGO, KEY).update(JSON.stringify(req.body)).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
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.
To expose your endpoint as required, you can use ngrok or localtunnel.
Once installed and set up correctly, run this command:
cURL
❯ 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
As we can see, ngrok
has deployed two temporary internet-accessible endpoints that tunnel toward your locally running API:
http://ede9-109-2-22-27.ngrok.io
https://ede9-109-2-22-27.ngrok.io
As mentioned earlier, we will use ONLY HTTPS connections to send notifications. Therefore, we will use the second option for the following steps.
Test your endpoint
Before you can register and use your webhook, a testing process must be performed to ensure that everything is well-configured and ready to use. After you have entered the information about your webhook on the dashboard, click the "Test Integration" button to manually launch the testing process.
It will send an HTTP POST request to the webhook URL you provided, with this simple payload:
Payload
{
"challenge": "7cbe2b6a0a5e58934bc3c5ea77be7de6de77e633a1432f0e1883584d135045cd"
}
To validate the testing process, simply return the challenge as requested in the challenge section. If you don't send the expected response, the testing process will fail and you won't be able to register and use your Webhook.
Take a look at some use cases:
MessageUpdated payload
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"
}
}
ContentUpdated payload
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
Payload
{
"version": 1,
"organization": {
"id": "5d849494-2b0e-49fc-85d5-7197ca62d7fe",
"name": "Bodyguard"
},
"time": 1630940073706,
"eventType": "AUTHOR_UPDATED",
"challenge": "54cddd8ead45bb5661ce229ca67b674e576b959f596851428c4f711724b9cb95",
"authorUpdatedData": {
"previousAuthor": {
"classifications": [],
"flaggedAt": null,
"permalink": "https://link.to.profile",
"status": "ACTIVE",
"identifier": "user/1a2b3c4d"
},
"newAuthor": {
"classifications": [],
"flaggedAt": null,
"permalink": "https://link.to.profile",
"status": "BANNED",
"identifier": "user/1a2b3c4d"
},
"sourceId": "81310ae5-228e-438e-9720-35f737b9ca9f"
}
}