Who is calling???


1/27/19: Update: Elastic SIP trunks now support CNAM. This method outlined below is still useful for passing caller name with programmable voice.

A couple months ago I switched my Dad’s business over to Twilio Elastic SIP trunks. I have been very happy with the service. Not only was it really easy to deploy, but the service has been rock solid. The only downside that I discovered during my initial testing was that Twilio doesn’t deliver the caller name in the SIP header! Twilio offers a service for lookups and there are a couple add-ons that will do caller name lookups, however, they don’t alter the SIP headers. In the solution below, I am using the addOn OpenCNAM by telo. This will work with Twilio’s lookup service, but the function will need to be altered to capture the correct data. I found OpenCNAM to be more accurate. It took me a bit of trial and error, but I finally figured out a solution! In order to make this work, follow the steps below.

  1. Twilio Function: Here we will send the caller name as a custom SIP header
  2. CUBE SIP Profile: Using SIP Profiles on the Cisco Unified Border Element, we can insert the customer header containing the Caller Name into the P-Asserted-Identity SIP header

Twilio Function:


exports.handler = function (context, event, callback) {
  if (event.AddOns) {
    fromName = getName(event.AddOns);
  } else {
    fromName = event.From; // send the original calling number if no AddOns are found

  let twiml = new Twilio.twiml.VoiceResponse();
  let dial = twiml.dial();
  dial.sip("sip:[email protected]?fromName=" + fromName); // replace with a URI directed at CUBE

  callback(null, twiml);

var getName = function (input) {
  var addOn = JSON.parse(input);
  //var callerName = JSON.parse();
  if (addOn.results.opencnam.result.name) {
    var name = addOn.results.opencnam.result.name;

    // remove spaces, Twilio doesn't allow spaces to be passed in the custom headers
    name = name.replace(/\s/g, "_");

    // limit to 15 characters
    name = name.substring(0, 15);

    console.log("returning name: ", name);
    return name;
  } else {
    // if a name isn't found, the number will be returned
    console.log("returning number: ", addOn.results.opencnam.result.number);

    return addOn.results.opencnam.result.number;


A handful of things need to be done on CUBE. First a sip-copylist and sip-profile will need to be created.


voice class sip-copylist 1

voice class sip-copylist 1
sip-header X-fromName

sip-profiles voice class sip-profiles 1

voice class sip-profiles 1
request INVITE peer-header sip X-fromName copy "(.*)" u01
request INVITE sip-header P-Asserted-Identity modify ".*(<.*)" "P-Asserted-Identity: \"\u01\" \1"

Once the copylist and profile have been created, they will be applied to an inbound and outbound dial-peer.

dial-peer example

dial-peer voice 1 voip
 description ** SP to CUBE catchall **
 session protocol sipv2
 incoming called-number .
 voice-class sip copy-list 1
 voice-class sip bind control source-interface FastEthernet0/0
 voice-class sip bind media source-interface FastEthernet0/0
 dtmf-relay rtp-nte sip-kpml sip-notify
 codec g711ulaw
 no vad

dial-peer voice 1000 voip
 description ** CUBE to CUCM **
 destination-pattern 1000
 session protocol sipv2
 session target ipv4:<CUCM IP Address>
 voice-class sip profiles 1
 voice-class sip options-keepalive down-interval 10
 voice-class sip bind control source-interface GigabitEthernet1/0.10
 voice-class sip bind media source-interface GigabitEthernet1/0.10
 dtmf-relay rtp-nte
 codec g711ulaw
 no vad

The final step is to point the phone number at the Twilio Function. You should now see caller name on inbound calls!

Hopefully this will save some time for the next person trying to figure this out!


– Brad