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.

An example webhook client in Node.js is available here: https://github.com/rleroi/trengo-webhook-example-node

Deploy to Heroku

Configuration

Webhooks can be configured via Settings > Apps & Integrations > Webhooks. You may also programmatically set a webhook via the Rest API. The list below describes the available webhook types and triggers.

TypeTriggers
INBOUNDWhen an inbound message is received
OUTBOUNDWhen an outbound message is sent
NOTEWhen an internal message is created
TICKET_LABEL_ADDEDWhen a label is added to a ticket
TICKET_LABEL_DELETEDWhen a label is removed from a ticket
TICKET_ASSIGNEDWhen a ticket is assigned to an agent
TICKET_CLOSEDWhen a ticket is closed
TICKET_REOPENEDWhen a ticket is reopened
TICKET_MARKED_AS_SPAMWhen a ticket is marked as spam
TICKET_UNMARKED_AS_SPAMWhen a ticket is unmarked as spam
VOICE_CALL_STARTEDWhen a voice call has started
VOICE_CALL_ENDEDWhen a voice call has ended
VOICE_CALL_RECORDEDWhen a voice call has been recorded
VOICE_CALL_MISSEDWhen a voice call is missed
VOICE_CALL_ROUTE_NUMBERWhen an IVR action is sent

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

ParamDescription
message_idMessage ID
ticket_idTicket ID
contact_idContact ID
messageMessage content
contact_nameContact name
contact_emailContact email (if present)
contact_identifierContact phone (if present)
channel_idChannel ID

OUTBOUND

ParamDescription
message_idMessage ID
ticket_idTicket ID
messageMessage content
user_idUser ID
user_nameUser name
user_emailUser email
contact_idContact ID
contact_nameContact name
contact_emailContact email (if present)
contact_identifierContact phone (if present)
channel_idChannel ID

NOTE

ParamDescription
message_idMessage ID
ticket_idTicket ID
messageMessage content
user_idUser ID
user_nameUser name
user_emailUser email

TICKET_LABEL_ADDED

ParamDescription
ticket_idTicket ID
label_idLabel ID
label_nameLabel name

TICKET_LABEL_DELETED

ParamDescription
ticket_idTicket ID
label_idLabel ID
label_nameLabel name

TICKET_ASSIGNED

ParamDescription
ticket_idTicket ID
user_idUser ID
user_nameUser name
user_emailUser email
team_idTeam ID
team_nameTeam name
assigned_toTEAM or USER

TICKET_CLOSED

ParamDescription
ticket_idticket_id

TICKET_REOPENED

ParamDescription
ticket_idticket_id
statusOPEN or ASSIGNED

TICKET_MARKED_AS_SPAM

ParamDescription
ticket_idTicket ID

TICKET_UNMARKED_AS_SPAM

ParamDescription
ticket_idTicket ID

VOICE_CALL_STARTED

ParamDescription
channel_idChannel ID
ticket_idTicket ID
contact_idContact ID
directionINBOUND / OUTBOUND
fromOrigination phone number
toDestination phone number
user_idUser ID
user_nameUser name
user_emailUser email

VOICE_CALL_ENDED

ParamDescription
channel_idChannel ID
ticket_idTicket ID
contact_idContact ID
directionINBOUND / OUTBOUND
fromOrigination phone number
toDestination phone number
user_idUser ID
user_nameUser name
user_emailUser email
durationDuration in seconds

VOICE_CALL_RECORDED

ParamDescription
channel_idChannel ID
ticket_idTicket ID
contact_idContact ID
directionINBOUND / OUTBOUND
fromOrigination phone number
toDestination phone number
user_idUser ID
user_nameUser name
user_emailUser email
durationDuration in seconds
recording_urlRecording URL (when enabled)

VOICE_CALL_MISSED

ParamDescription
channel_idChannel ID
ticket_idTicket ID
contact_idContact ID
fromOrigination phone number
toDestination phone number
durationDuration in seconds
recording_urlRecording URL (when enabled)
failure_reasonList of available reasons: NO_BALANCE, OUTSIDE_BUSINESS_HOURS, FULL_QUEUE, QUEUE_TIME_EXCEEDED, NO_AGENTS_AVAILABLE

VOICE_CALL_ROUTE_NUMBER

ParamDescription
channel_idChannel ID
ticket_idTicket ID
contact_idContact ID
ivr_menu_route_number_idIVR menu route number ID
ivr_menu_idIVR menu ID
actionRoute action
triggerRoute number
team_idRoute to team ID
target_ivr_menu_idRoute to target IVR menu ID
phone_numberRoute 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.

Deploy to Heroku

The repository is available here: https://github.com/rleroi/trengo-webhook-example-node