Webhooks
With webhooks you are able to get notified when something happens in Trengo without having to poll the API.
Introduction
Webhooks are useful in a broad range of situations. When an event in Trengo occures, such as a new message or a new voice call, Trengo will perform an HTTP request to the URL you provide. Based on the payload of the request, you can determine which action you need to perform.
Configuration
Request payload
The payload of each webhook depends on the type of the event. Use this list to see what payload we will POST to your URL.
INBOUND
| Param | Description |
|---|---|
| message_id | Message ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| message | Message content |
| contact_name | Contact name |
| contact_email | Contact email (if present) |
| contact_identifier | Contact phone (if present) |
| channel_id | Channel ID |
OUTBOUND
| Param | Description |
|---|---|
| message_id | Message ID |
| ticket_id | Ticket ID |
| message | Message content |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
| contact_id | Contact ID |
| contact_name | Contact name |
| contact_email | Contact email (if present) |
| contact_identifier | Contact phone (if present) |
| channel_id | Channel ID |
NOTE
| Param | Description |
|---|---|
| message_id | Message ID |
| ticket_id | Ticket ID |
| message | Message content |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
TICKET_LABEL_ADDED
| Param | Description |
|---|---|
| ticket_id | Ticket ID |
| label_id | Label ID |
| label_name | Label name |
TICKET_LABEL_DELETED
| Param | Description |
|---|---|
| ticket_id | Ticket ID |
| label_id | Label ID |
| label_name | Label name |
TICKET_ASSIGNED
| Param | Description |
|---|---|
| ticket_id | Ticket ID |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
| team_id | Team ID |
| team_name | Team name |
| assigned_to | TEAM or USER |
TICKET_CLOSED
| Param | Description |
|---|---|
| ticket_id | ticket_id |
TICKET_REOPENED
| Param | Description |
|---|---|
| ticket_id | ticket_id |
| status | OPEN or ASSIGNED |
TICKET_MARKED_AS_SPAM
| Param | Description |
|---|---|
| ticket_id | Ticket ID |
TICKET_UNMARKED_AS_SPAM
| Param | Description |
|---|---|
| ticket_id | Ticket ID |
VOICE_CALL_STARTED
| Param | Description |
|---|---|
| channel_id | Channel ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| direction | INBOUND / OUTBOUND |
| from | Origination phone number |
| to | Destination phone number |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
VOICE_CALL_ENDED
| Param | Description |
|---|---|
| channel_id | Channel ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| direction | INBOUND / OUTBOUND |
| from | Origination phone number |
| to | Destination phone number |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
| duration | Duration in seconds |
VOICE_CALL_RECORDED
| Param | Description |
|---|---|
| channel_id | Channel ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| direction | INBOUND / OUTBOUND |
| from | Origination phone number |
| to | Destination phone number |
| user_id | User ID |
| user_name | User name |
| user_email | User email |
| duration | Duration in seconds |
| recording_url | Recording URL (when enabled) |
VOICE_CALL_MISSED
| Param | Description |
|---|---|
| channel_id | Channel ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| from | Origination phone number |
| to | Destination phone number |
| duration | Duration in seconds |
| recording_url | Recording URL (when enabled) |
| failure_reason | List of available reasons: NO_BALANCE, OUTSIDE_BUSINESS_HOURS, FULL_QUEUE, QUEUE_TIME_EXCEEDED, NO_AGENTS_AVAILABLE |
VOICE_CALL_ROUTE_NUMBER
| Param | Description |
|---|---|
| channel_id | Channel ID |
| ticket_id | Ticket ID |
| contact_id | Contact ID |
| ivr_menu_route_number_id | IVR menu route number ID |
| ivr_menu_id | IVR menu ID |
| action | Route action |
| trigger | Route number |
| team_id | Route to team ID |
| target_ivr_menu_id | Route to target IVR menu ID |
| phone_number | Route to phone number |
Verifying payload
We recommend verifying the webhook signature to make sure the data received is coming from Trengo.
The Trengo-Signature header is sent with every webhook, it consists of a timestamp and hash concatenated together with a ; in between.
Example signature: 1569508124;1298b36aefaeb7e0e93073744b954266515b69304b3dfe044cbf7203525061f4
The hash is a lowercase hexadecimal SHA256 HMAC digest of the timestamp, a . and the request's body. It uses your signing secret as key.
Compare the hash part of the signature to verify the webhook's authenticity.
<?php
$payload = @file_get_contents('php://input'); // The request's body
$signature = $_SERVER['HTTP_TRENGO_SIGNATURE']; // The Trengo-Signature header value
$signingSecret = 'my-secret'; // Your signing secret
// verify the signature
if (verify($payload, $signature, $signingSecret)) {
echo 'Valid signature.';
} else {
echo 'Invalid signature';
}
/**
* @param $payload
* @param $signature
* @param $signingSecret
* @return bool
*/
function verify($payload, $signature, $signingSecret) {
list($timestamp, $hash) = explode(';', $signature);
return hash_equals(hash_hmac('sha256', $timestamp . '.' . $payload, $signingSecret), $hash);
}var express = require('express');
var CryptoJS = require('crypto-js');
// start the express server
var app = express();
app.listen();
// parse the URLEncoded body and save the raw body to req.rawBody
app.use(
express.urlencoded({
verify: function (req, res, buf) {
req.rawBody = buf;
},
inflate: true,
type: "application/x-www-form-urlencoded"
})
);
// listen for POST requests to '/my-endpoint'
app.post('/my-endpoint', function(req, res) {
// replace with your signing secret, found in Trengo
var signingSecret = 'my-secret';
// get signature header
var signature = req.header('Trengo-Signature');
// get raw request body
var payload = req.rawBody;
// verify the signature
if (verify(payload, signature, signingSecret)) {
res.send('Valid signature');
// the parsed body is available at req.body
console.log(req.body);
} else {
res.send('Invalid signature');
}
});
// the function to verify the trengo-signature
function verify(payload, signature, signingSecret) {
// split the timestamp from the hash
var signatureParts = signature.split(';');
var timestamp = signatureParts[0];
var hash = signatureParts[1];
// get the raw digest bytes
var raw = CryptoJS.HmacSHA256(timestamp + '.' + payload, signingSecret);
// encode the raw bytes as hexadecimal digits
var hex = raw.toString(CryptoJS.enc.hex);
// and make the hexadecimal digits lowercase
var lowerCase = hex.toLowerCase();
// compare the result to the hash from the signature header
return lowerCase === hash;
}You can deploy a working example on Heroku to instantly test it out for free.
The repository is available here: https://github.com/rleroi/trengo-webhook-example-node
Updated 5 months ago
