Processing webhook events
General guidelines for processing Truto webhook events.
Webhook event payload structure
The webhook event is a JSON object with the following properties,
Body
idis the unique ID for the webhook event.eventis the name of the event. E.g,integrated_account:created. It's in the formattruto_entity:action, wheretruto_entitycan be a resource in Truto andactioncan becreated,updated,deleted, etc.payloadis the data associated with the action. In most cases, it will be updated resource.environment_idis the environment this webhook event is associated with.created_atis the time when this webhook event was generated.webhook_idis the ID of the webhook associated with this event.
An example webhook event sent when a new integrated account is connected is shown below,
json
{
"id": "3a0da6ba-b2d1-473f-957c-51f6825e3623",
"event": "integrated_account:created",
"payload": {
"id": "79a39d69-e27e-49cb-b9a9-79f5eea7aa26",
"tenant_id": "acme-1",
"environment_integration_id": "27a8c0ff-0c2e-4383-b651-0772b3515921",
"context": {},
"created_at": "2023-06-16T09:21:21.000Z",
"updated_at": "2023-06-16T09:21:21.000Z",
"is_sandbox": false,
"unified_model_override": {},
"environment_id": "ac15abdc-b38e-47d0-97a2-69194017c177",
"integration": {
"id": "1fa47bf3-5f1f-4b65-bcd0-8d07ab455e15",
"name": "helpscout",
"category": "helpdesk",
"is_beta": false,
"team_id": "68ea7267-2aec-4da0-b5d9-192cc84eb2de",
"sharing": "allow",
"default_oauth_app_id": null,
"created_at": "2023-02-16T09:27:09.000Z",
"updated_at": "2023-02-17T12:56:55.000Z"
},
"environment_integration": {
"id": "27a8c0ff-0c2e-4383-b651-0772b3515921",
"integration_id": "1fa47bf3-5f1f-4b65-bcd0-8d07ab455e15",
"environment_id": "ac15abdc-b38e-47d0-97a2-69194017c177",
"show_in_catalog": true,
"is_enabled": true,
"override": {},
"created_at": "2023-02-16T09:27:16.000Z",
"updated_at": "2023-02-16T09:27:16.000Z"
}
},
"environment_id": "ac15abdc-b38e-47d0-97a2-69194017c177",
"created_at": "2023-06-16T09:21:22.369Z",
"webhook_id": "077ec306-5756-43fa-9a06-0cc0da4eabe0"
}Header
For security, Truto adds a X-Truto-Signature header which contains the Base64 encoded SHA256 HMAC of the request body. For the request body shown above, the following header will be added to the request,
http
X-Truto-Signature: format=sha256,v=WDHLSeu0EEcUf9YV5N659YZ9EFOnfuIOrLj8UMBDhOoVerification of the webhook event
You can get the SHA256 HMAC in the v part of the header value and do the following steps to make sure the webhook event is legit and sent from Truto,
- Store the
vvalue sent as part ofX-Truto-Signatureheader.WDHLSeu0EEcUf9YV5N659YZ9EFOnfuIOrLj8UMBDhOoin the example above. - Calculate the SHA256 HMAC of the request body using the
secretattribute returned to you when you had created the webhook. IMPORTANT: Please use the raw string received as the body and not the JSON parsed version of it that most frameworks return you in the middleware function. - Calculate the Base64 encoded version of the data calculated in step 2. Make sure that the Base64 encoding is URL safe.
- Compare the value obtained in step 3 with the value stored in step 1. They should be equal. If they are not, then the payload has been tampered with and the webhook event is not generated by Truto.
js
const crypto = require("crypto");
function verifyWebhook(headerValue, secret, body) {
// Get HMAC from header
const hmacFromHeader = Buffer.from(headerValue.split("v=")[1], "base64");
// Calculate the SHA256 HMAC of the request body
const hmac = crypto.createHmac("sha256", secret);
hmac.update(body);
// Calculate the Base64 encoded version of the data
const calculatedHmac = Buffer.from(hmac.digest("hex"), "hex");
return crypto.timingSafeEqual(hmacFromHeader, calculatedHmac);
}
// Note: You need to pass the raw body of the webhook request to the verifyWebhook function. If you are using Express.js, you can get the raw body like this:
app.use(
bodyParser.json({
verify: function (req, res, buf) {
req.rawBody = buf.toString();
},
})
);
// And then in your route:
app.post("/webhook", (req, res) => {
let isVerified = verifyWebhook(
req.headers["x-truto-signature"],
secret,
req.rawBody
);
console.log(isVerified ? "Webhook Verified" : "Webhook Verification Failed");
});csharp
public static bool VerifyWebhook(string headerValue, string secret, string body)
{
// Get HMAC from header
var hmacFromHeader = Encoding.UTF8.GetBytes(headerValue.Split("v=")[1]);
var secretBytes = Encoding.UTF8.GetBytes(secret);
var bodyBytes = Encoding.UTF8.GetBytes(body);
// Calculate the SHA256 HMAC of the request body
var hmac = new HMACSHA256(secretBytes);
var calculatedHmac = hmac.ComputeHash(bodyBytes);
var base64OfCalculatedHmac = Convert.ToBase64String(calculatedHmac);
string urlSafeBase64String = base64OfCalculatedHmac.Replace('+', '-').Replace('/', '_').TrimEnd('=');
var generatedBytes = Encoding.UTF8.GetBytes(urlSafeBase64String);
return CryptographicOperations.FixedTimeEquals(hmacFromHeader, generatedBytes);
}