Webhooks Guide

Overview

Webhooks are a system of automated notifications indicating that an event has occurred in the CallFire system. Rather than requiring you to pull information via our API, webhooks push information to your destination when important events occur. Resource notifications are delivered via HTTP POST to a destination endpoint on your server and are sent based on the events you choose.

List of resources and its events supporting webhooks:

CallFire allows you to subscribe to multiple events on the same resource. After you create a listener, you can use a sample payload to simulate a webhook event to verify that your listener can successfully receive event data without any connectivity issues. After you verify your listener, subscribe it to a list of events. Finally, monitor the notifications that your listener receives when events occur.


How to Create Webhook

The following code samples show how to create webhooks in different programming languages. You can also use the curl utility for straight HTTP requests.

[[code-container]] [+curl] request:

#!/usr/bin/env bash

curl -u username:password -H "Content-Type:application/json" -X POST "https://api.callfire.com/v2/webhooks" -d '
    {
        "name":"Sms sent",
        "resource":"OutboundText",
        "events":["Finished"], 
        "callback":"https://callback-service.com/listener"
    }'
response:
{
  "id": 4321
}
[-curl] [+java]
import com.callfire.api.client.CallfireClient;
import com.callfire.api.client.api.common.model.ResourceId;
import com.callfire.api.client.api.webhooks.model.ResourceType;
import com.callfire.api.client.api.webhooks.model.Webhook;

class ApiClientSample {
    public static void main(String[] args) {
        CallfireClient client = new CallfireClient("api login", "api password");
        Webhook webhook = new Webhook();
        webhook.setName("sms sent");
        webhook.setResource(ResourceType.OUTBOUND_TEXT);
        webhook.getEvents().add(ResourceType.ResourceEvent.FINISHED);
        webhook.setCallback("https://callback-service.com/listener");
        ResourceId id = client.webhooksApi().create(webhook);
    }
}
[-java] [+csharp]
using System.Collections.Generic;
using CallfireApiClient;
using CallfireApiClient.Api.Common.Model;
using CallfireApiClient.Api.Webhooks.Model;

public class ApiClientSample
{
    public static void Main(string[] args)
    {
        var client = new CallfireClient("api_login", "api_password");
        var webhook = new Webhook
        {
            Name = "sms sent",
            Resource = ResourceType.OUTBOUND_TEXT,
            Events = new HashSet<ResourceEvent> {ResourceEvent.FINISHED},
            Callback = "https://callback-service.com/listener"
        };
        ResourceId resource = client.WebhooksApi.Create(webhook);
    }
}
[-csharp] [+js]
'strict'

const CallfireClient = require('callfire-api-client-js');
const client = new CallfireClient('api-login', 'api-password');

client.ready(() => {
    client.webhooks.createWebhook({
      body: {
        name: 'sms sent',
        resource: 'OutboundText',
        events: ['Finished'],
        callback: 'https://callback-service.com/listener'
      }
    })
      .then((response) => {
        console.log(response.obj);
      })
      .catch((err) => {
        console.log('request error ' + err.data);
      });
  },
  (clientError) => {
    console.log('client error ' + clientError);
  }
);
[-js] [+python]
from callfire.client import CallfireClient

client = CallfireClient('api-login', 'api-password')
response = client.webhooks.createWebhook(
    body={
        'name': 'sms sent',
        'resource': 'OutboundText',
        'events': ['Finished'],
        'callback': 'https://callback-service.com/listener'
    }
).result()

# see sample JSON response for this API
# on 'curl' samples tab
print(response)
[-python] [+php]
<?php

class ApiClientSample {

    public static function main() {
        $client =\CallFire\Api\DocumentedClient::createClient ("login", "password");
        $request = $client -> createWebhook();
        $body = '{
                    "name":"Sms sent",
                    "resource":"OutboundText",
                    "events":["Finished"],
                    "callback":"https://callback-service.com/listener"
                 }';
        $request->getOperationConfig()->setBodyParameter($body);
        $result = $client->request($request);
        $json = json_decode($result->getBody());
    }
}

ApiClientSample::main();
[-php] [[/code-container]]

You can find a description of all webhook API operations, JSON models, and request parameters at Webhooks API page

Callback JSON payload samples

[[code-container]] [+call]

{  
   "timestamp":1469540830377,
   "webhookId":3749003,
   "webhookName":"Test Webhook",
   "resourceType":"OutboundCall",
   "events":[  
      {  
         "timestamp":1469540830377,
         "webhookId":3749003,
         "resourceType":"OutboundCall",
         "eventType":"Finished",
         "resource":{  
            "id":1024990109003,
            "fromNumber":"12132212384",
            "toNumber":"12132041238",
            "state":"FINISHED",
            "campaignId":13194908003,
            "batchId":11305143003,
            "contact":{  
               "id":537189974003,
               "firstName":"testFirstName",
               "lastName":"testLastName",
               "zipcode":"90025",
               "homePhone":"12132041238",
               "workPhone":"12132041238"
            },
            "inbound":false,
            "created":1469565220000,
            "modified":1469565232000,
            "labels":[  
               "Test Label 1",
               "Test Label 2"
            ],
            "finalCallResult":"AM",
            "records":[  
               {  
                  "id":571797202003,
                  "billedAmount":1,
                  "finishTime":1469565233000,
                  "result":"AM",
                  "originateTime":1469565225000,
                  "answerTime":1469565227000,
                  "duration":6
               }
            ],
            "agentCall":false
         }
      }
   ]
}
[-call] [+text]
{  
   "timestamp":1469540830377,
   "webhookId":3749003,
   "webhookName":"Test Webhook",
   "resourceType":"OutboundText",
   "events":[  
      {  
         "timestamp":1469540830377,
         "webhookId":3749003,
         "resourceType":"OutboundText",
         "eventType":"Finished",
         "resource":{  
            "id":1023905054003,
            "fromNumber":"12132041238",
            "toNumber":"14246525473",
            "state":"FINISHED",
            "campaignId":9362663003,
            "contact":{  
               "id":537189974003,
               "firstName":"testFirstName",
               "lastName":"testLastName",
               "zipcode":"90025",
               "homePhone":"12132041238",
               "workPhone":"12132041238"
            },
            "inbound":false,
            "created":1469540830000,
            "modified":1469540828000,
            "labels":[  
               "Test"
            ],
            "message":"Test Message",
            "finalTextResult":"RECEIVED",
            "records":[  
               {  
                  "id":570794145003,
                  "billedAmount":1.0,
                  "finishTime":1469540830000,
                  "message":"Test Message",
                  "textResult":"RECEIVED"
               }
            ],
            "media":[  
            ]
         }
      }
   ]
}
[-text] [+call-broadcast]
{  
   "timestamp":1469538171888,
   "webhookId":3745003,
   "webhookName":"Test Webhook",
   "resourceType":"CallBroadcast",
   "events":[  
      {  
         "timestamp":1469538171888,
         "webhookId":3745003,
         "resourceType":"CallBroadcast",
         "eventType":"Finished",
         "resource":{  
            "id":13175997003,
            "name":"Test Webhook",
            "status":"STOPPED",
            "lastModified":1469538170000,
            "fromNumber":"12132212384",
            "localTimeRestriction":{  
               "enabled":true,
               "beginHour":8,
               "beginMinute":0,
               "endHour":21,
               "endMinute":0
            },
            "schedules":[  
               {  
                  "id":1617524003,
                  "startDate":{  
                     "year":2016,
                     "month":7,
                     "day":28
                  },
                  "startTimeOfDay":{  
                     "hour":9,
                     "minute":0,
                     "second":0
                  },
                  "stopTimeOfDay":{  
                     "hour":17,
                     "minute":0,
                     "second":0
                  },
                  "timeZone":"America/Los_Angeles",
                  "daysOfWeek":[  
                     "MONDAY"
                  ]
               }
            ],
            "retryConfig":{  
               "maxAttempts":1,
               "minutesBetweenAttempts":60,
               "retryResults":[  

               ],
               "retryPhoneTypes":[  
                  "FIRST_NUMBER"
               ]
            },
            "maxActive":100,
            "labels":[  
               "Label1"
            ],
            "resumeNextDay":false,
            "recipients":[  
               {  
                  "contactId":1232412334
               }
            ],
            "sounds":{  
               "liveSoundText":"Test",
               "liveSoundId":3363224003,
               "machineSoundText":"Test",
               "machineSoundId":3363224003,
               "transferSoundText":"Test",
               "transferSoundId":3363224003
            },
            "answeringMachineConfig":"LIVE_WITH_AMD",
            "maxActiveTransfers":1
         }
      }
   ]
}
[-call-broadcast] [+text-broadcast]
{  
   "timestamp":1469538745454,
   "webhookId":3747003,
   "webhookName":"Test Webhook",
   "resourceType":"TextBroadcast",
   "events":[  
      {  
         "timestamp":1469538745454,
         "webhookId":3747003,
         "resourceType":"TextBroadcast",
         "eventType":"Finished",
         "resource":{  
            "id":13163667003,
            "name":"TEST Text Broadcast",
            "status":"STOPPED",
            "lastModified":1469538744000,
            "fromNumber":"12132041238",
            "localTimeRestriction":{  
               "enabled":true,
               "beginHour":8,
               "beginMinute":0,
               "endHour":21,
               "endMinute":0
            },
            "schedules":[  
               {  
                  "id":1617524003,
                  "startDate":{  
                     "year":2016,
                     "month":7,
                     "day":28
                  },
                  "startTimeOfDay":{  
                     "hour":9,
                     "minute":0,
                     "second":0
                  },
                  "stopTimeOfDay":{  
                     "hour":17,
                     "minute":0,
                     "second":0
                  },
                  "timeZone":"America/Los_Angeles",
                  "daysOfWeek":[  
                     "MONDAY"
                  ]
               }
            ],
            "retryConfig":{  
               "maxAttempts":1,
               "minutesBetweenAttempts":60,
               "retryResults":[  

               ],
               "retryPhoneTypes":[  
                  "FIRST_NUMBER"
               ]
            },
            "maxActive":100,
            "labels":[  
               "Label1"
            ],
            "resumeNextDay":false,
            "recipients":[  
               {  
                  "message":"Test Message",
                  "contactId":1232412334
               }
            ],
            "message":"Test Message",
            "bigMessageStrategy":"SEND_MULTIPLE",
            "media":[  
            ]
         }
      }
   ]
}
[-text-broadcast] [+contact-list]
{
   "timestamp":1496758301032,
   "webhookId":3745004,
   "webhookName":"testContactListValidationFinish",
   "resourceType":"ContactList",
   "events":[
      {
         "timestamp":1496758301032,
         "webhookId":3745004,
         "resourceType":"ContactList",
         "eventType":"ValidationFinished",
         "resource":{
            "id":10,
            "name":"API Contact List 2017-06-06 07:11:24",
            "size":2,
            "created":1496758285000,
            "status":"ACTIVE"
         }
      }
   ]
}
[-contact-list] [+text-delivery-report]
{
   "timestamp": 1502221022807,
   "webhookId": 1,
   "webhookName": "ASA DLR Test",
   "resourceType": "OutboundTextDeliveryReport",
   "events": [
      {
         "timestamp": 1502221022807,
         "webhookId": 1,
         "resourceType": "OutboundTextDeliveryReport",
         "eventType": "Received",
         "resource": {
            "id": 7420017300281076000,
            "actionId": 123123123123,
            "campaignId": 123456789,
            "toNumber": "12135551234",
            "fromNumber": "12138888888",
            "timestamp": 1502221022571,
            "deliveryState": "DELIVERED",
            "deliveryCategory": "DELIVERED"
         }
      }
   ]
}
[-text-delivery-report] [[/code-container]]

How to Authenticate Webhook Requests

CallFire webhooks have a secret token that is used as a signing key to the HmacSHA1 hash of JSON payload, which is returned in a 'X-CallFire-Signature' header. This header can be used to verify that the callback POST is coming from CallFire. You must provide a webhook.token when creating a webhook. You can change a webhook's secret key at any time. CallFire will immediately begin using the new key to sign requests.

Verifying request signature

[[code-container]] [+java]

import java.security.SignatureException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class ApiRequestVerifier {

    public String getHmacSignature(String data, String key) throws SignatureException {
        String result;
        SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
        Mac mac;
        try {
            mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(data.getBytes());
            result = Base64.encodeBase64String(rawHmac).trim();
        } catch (Exception e) {
            throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
        }
        return result;
    }
}

// then check signature in listener code
// String data = "{\"name\":\"test webhook\", \"callback\":\"sms:callfire\"}";
// String secret = "mysecrets";
// Assert.assertEquals("v14pF7d5C1+CHNIEWlg+sw9v0Xg=", new ApiRequestVerifier().getHmacSignature(data, secret));
[-java] [+php]
<?php

function getHmacSignature($data, $secret) {
    return base64_encode(hash_hmac('sha1', $data, $secret, true));
}

// then check signature in listener code
// $data = "{\"name\":\"test webhook\", \"callback\":\"sms:callfire\"}";
// $secret = "mysecrets";
// assert("v14pF7d5C1+CHNIEWlg+sw9v0Xg=" == getHmacSignature($data, $secret));
[-php] [[/code-container]]

Read more about Callfire API security and authentication

The differences between API v1.1 Subscriptions and API v2 Webhooks

Despite the fact that Subscriptions and Webhooks are built to do the same job, at the same time both APIs are completely different, including the object model of their notifications. Below you can find a comparison table of both APIs:

Features Subscriptions API Webhooks API Notes
Filtering by to/from number, broadcast ID, etc. + - Currently webhooks do not support filtering, you have to do it on your side, otherwise you can use Subscription which supports filtering
Multiple events per single instance - +
Different type of notification format (JSON, XML, EMAIL) + - Subscriptions support choice of notification format e.g. JSON, XML or send notification via email
Partial notification - + Webhooks support partial notification response in order to increase API performance, read more on Partial Response page
One-time notifications - + Webhooks support through singleUse field
Notification Signatures - + CallFire can sign a notification with your secret key and let you validate an incoming request, the process described in previous paragraph.

Which one to use ?