Subscribing to events
You can subscribe to receive real-time notifications from Chatlayer whenever certain events occour. Those notifications are delivered to you via webhook calls.
At the moment we support the following event types
-
Message events (
MESSAGE_EVENT
) -
Tracking events (
TRACKING_EVENT
)
Follow this guide to add a Tracking event step to your bot flow and start generation custom tracking events. Adding a Tracking event step to your bot will allow you to create custom dashboards and funnels to assist in the analysis of your bot performance.
Before subscribing, you must create a route in a service own by you that will receive and process the events. Your service must be publicly accesible over the internet, use SSL and listen on port 443.
The following table lists the types of notifications your handler must process. The X-Message-Type
request header contains the notification’s type.
Notification Type | Description |
---|---|
webhookcallbackverification | Contains the challenge used to prove that you own the event handler. This is the first event you’ll receive after subscribing to an event. See Responding to a challenge request. |
notification | Contains the event’s data. See Processing an event. |
revocation | Contains the reason why Chatlayer revoked your subscription. See Revoking your subscription. |
You must respond to notification requests within a few seconds. If your server takes too long to respond, the request will time out. If you fail to respond quickly enough too many times, the subscription’s status changes to notification_failures_exceeded
and Chatlayer will revoke the subscription.
If your server can’t process a notification request quickly enough, consider writing the event to temporary storage and processing the notification after responding with 2XX.
Processing an event
When an event that you subscribed to occurs, Chatlayer sends your event handler a notification message that contains the event’s data. Notification messages set the X-Message-Type
header to notification
. For example:
X-Message-Type: notification
Your response should return a 2XX status code.
The following snippet shows a simple JavaScript implementation that responds to an event notification request:
// Notification request headers
const MESSAGE_TYPE = "X-Message-Type".toLowerCase();
// Notification message types
const MESSAGE_TYPE_NOTIFICATION = "notification";
// Get JSON object from body, so you can process the message.
let notification = JSON.parse(req.body);
if (MESSAGE_TYPE_NOTIFICATION === req.headers[MESSAGE_TYPE]) {
// TODO: Do something with event's data.
console.log(`Event type: ${notification.subscription.type}`);
console.log(JSON.stringify(notification.event, null, 4));
res.sendStatus(204);
}
Responding to a challenge request
When you subscribe to an event, Chatlayer sends you a verification challenge to make sure that you own the event handler specified in the request. Challenge messages set the X-Message-Type
header to webhook_callback_verification
. For example:
X-Message-Type: webhook_callback_verification
The challenge
field in the body of the request contains the challenge value that you must respond with.
{
"challenge": "applebanana",
"subscription": {
"id": "437ae806-8094-4d12-b79a-89950d84615c",
"status": "webhook_callback_verification_pending",
"created_at": "2024-10-22T11:49:22.128Z"
}
}
Your response must return a 200 status code, the response body must contain the raw challenge value, and you must set the Content-Type
response header to the length of the challenge value. If successful, your subscription is enabled.
Simple Node.js example
The following is a simple event handler that uses Node.js and Express.
To run the example, you must have Node.js installed.
- Open a terminal window
-
Create a folder for the example and
cd
to it -
Run
npm init
-
Run
npm install express --save
- Create a file and name it app.js
- Copy the example to app.js
-
Update the
getSecret()
function to get the secret key that you use when you subscribe to events -
Run
node app.js
const crypto = require("crypto");
const express = require("express");
const app = express();
const port = 8080;
// Notification request headers
const MESSAGE_ID = "X-Message-Id".toLowerCase();
const MESSAGE_TIMESTAMP = "X-Message-Timestamp".toLowerCase();
const MESSAGE_SIGNATURE = "X-Message-Signature".toLowerCase();
const MESSAGE_TYPE = "X-Message-Type".toLowerCase();
// Notification message types
const MESSAGE_TYPE_VERIFICATION = "webhook_callback_verification";
const MESSAGE_TYPE_NOTIFICATION = "notification";
const MESSAGE_TYPE_REVOCATION = "revocation";
// Prepend this string to the HMAC that's created from the message
const HMAC_PREFIX = "sha256=";
app.use(
express.raw({
// Need raw message body for signature verification
type: "application/json",
})
);
app.post("/eventsub", (req, res) => {
let secret = getSecret();
let message = getHmacMessage(req);
let hmac = HMAC_PREFIX + getHmac(secret, message); // Signature to compare
if (true === verifyMessage(hmac, req.headers[MESSAGE_SIGNATURE])) {
console.log("signatures match");
// Get JSON object from body, so you can process the message.
let notification = JSON.parse(req.body);
if (MESSAGE_TYPE_NOTIFICATION === req.headers[MESSAGE_TYPE]) {
// TODO: Do something with the event's data.
console.log(`Event type: ${notification.subscription.type}`);
console.log(JSON.stringify(notification.event, null, 4));
res.sendStatus(204);
} else if (MESSAGE_TYPE_VERIFICATION === req.headers[MESSAGE_TYPE]) {
res
.set("Content-Type", "text/plain")
.status(200)
.send(notification.challenge);
} else if (MESSAGE_TYPE_REVOCATION === req.headers[MESSAGE_TYPE]) {
res.sendStatus(204);
console.log(`${notification.subscription.type} notifications revoked!`);
console.log(`reason: ${notification.subscription.status}`);
console.log(
`condition: ${JSON.stringify(
notification.subscription.condition,
null,
4
)}`
);
} else {
res.sendStatus(204);
console.log(`Unknown message type: ${req.headers[MESSAGE_TYPE]}`);
}
} else {
console.log("403"); // Signatures didn't match.
res.sendStatus(403);
}
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
function getSecret() {
// TODO: Get secret from secure storage. This is the secret you pass
// when you subscribed to the event.
return "your secret goes here";
}
// Build the message used to get the HMAC.
function getHmacMessage(request) {
return (
request.headers[MESSAGE_ID] +
request.headers[MESSAGE_TIMESTAMP] +
request.body
);
}
// Get the HMAC.
function getHmac(secret, message) {
return crypto.createHmac("sha256", secret).update(message).digest("hex");
}
// Verify whether our hash matches the hash that Chatlayer passed in the header.
function verifyMessage(hmac, verifySignature) {
return crypto.timingSafeEqual(
Buffer.from(hmac),
Buffer.from(verifySignature)
);
}