/ Twilio

Building an IVR using Twilio

Firstly let me introduce myself - my name is Julian and I work on the Customer Engagement squad here at Zuto.

I started around three months ago and have really enjoyed getting to learn a new industry and business domain, and helping to deliver improvements to some of our underlying systems and processes.

Our squad is currently working on a really exciting project using the Twilio platform. Twilio offer a range of solutions and APIs to help communicate with customers, such as APIs for;

  • Sending and receiving emails and SMS
  • Making and receiving voice calls
  • Building realtime video applications

And even services to allow you to build AI chatbots!

Dancing Bender

Twilio offer APIs that can make it easy to build your own contact centre - and in this set of blog posts I'm going to look at how you might build an Interactive Voice Response (IVR) using a number of Twilio APIs. So let's get started!

So, how do we build an IVR?

The first thing we need to do is create a Twilio account! Twilio offer a free trial account that offers a good amount of available credit - allowing you to try out lots of features. You can sign up for a Twilio trial account here.

Once you have your Twilio account in place, you can sign in and visit the Twilio Console. You should see something that looks similar to the screenshot below:

twilio-dashboard

The Twilio Console is the hub that you will likely return to often, to view details of your services - and in our case details of your IVR.

Getting started with Twilio

Now we have our Twilio account, we can start writing some code! First, we'll provision a phone number (you can't have an IVR without calling a number!) using Twilio's APIs. Whilst you could create all the resources we'll talk about in this blog using the Twilio Console, using their APIs is a nice way to get familiar with the APIs that underpin the platform. To that end we'll create a simple .NET Core (3.1) Console application that will allow us to look up an available phone number, and provision it on our account.

Creating our Twilio Console App

Nothing too strenuous here - we'll just use the out of the box template for a C# Console app:

  1. Open Visual Studio 2019 and choose "Create a new project"
  2. Select the "C# Console App (.NET Core)" template and select a location to store the solution
  3. Call the project something snappy (SuperTwilioApp if you like!) and when prompted select .NET Core 3.1

Provisioning a phone number

Twilio provide a handy SDK that is a wrapper around the REST API used to manage Twilio resources. Our console app will make use of this to lookup available phone numbers and to actually buy a new phone number. In summary the steps we'll carry out are;

  1. Search for an available (local not international!) phone number that matches our requirements
  2. Specify the address that will be associated with the phone number (this is for regulatory reasons, details of which can be found here)
  3. Buy the number!

Keep it secret, keep it safe!

All interactions using the Twilio REST API first require us to initialise a TwilioClient instance, using some information specific to each Twilio account; namely our "Account Sid" and an "Auth Token".

Add an appsettings.json file to the Console App project, with the following structure to represent our Twilio values (remembering to ensure it's copied to our output directory):

{
  "Twilio": {
    "AccountSid": "This will be retrieved using User Secrets",
    "AuthToken":  "This will be retrieved using User Secrets"
  } 
}

You'll notice that values for the AccountSid and AuthToken are not specified here. As these values give anyone with access to them the ability to create resources in your Twilio account (potentially costing you money!) we want to be careful with them. Therefore it makes sense to not store them directly in our code (or accidentally commit them to source control!), but to instead make use of the .NET Secret Manager tool that allows us to store secrets in a file called secrets.json. This file is stored in a system-protected, user specific location that is from the project tree (and is therefore not subject to source control).

The linked article details the various ways the Secret Manager can be initialised in a project, but Visual Studio 2019 offers a really handy shortcut - simply right-click the Console App project and select Manage User Secrets from the context menu. This will open up the secrets.json file in our IDE and adds the necessary UserSecrets configuration element to our .csproj file.

In the secrets.json file that opens up, add the following:

{
  "Twilio:AccountSid": "Your account sid here",
  "Twilio:AuthToken":  "Your auth token here"
}

NOTE: You'll notice that in this file the JSON structure we specified earlier is flattened.

And finally edit the project and add the following environment variable for running the project in development:

twilio-launchsettings

With this in place we can now write the code necessary to load up our configuration, and see how our secrets get nicely injected in.

Setup application configuration

To read the config from our appsettings.json file, we'll use a trimmed down version of the configuration builder code you've likely seen in an ASP.NET Core application;

private static IConfiguration BuildApplicationConfiguration()
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json");

    if (IsDevelopmentEnvironment())
    {
        builder.AddUserSecrets<Program>();
    }

    return builder.Build();
}

private static bool IsDevelopmentEnvironment()
{
    var environment = Environment.GetEnvironmentVariable("NETCORE_ENVIRONMENT");
    return environment == "Development";
}

The IsDevelopmentEnvironment method uses the environment variable we set up earlier to determine if we are running in dev, and if so add in the UserSecrets configuration source. This will ensure that our Twilio values get automagically replaced with their secret equivalents at runtime.

Phone number compliance stuff!

Before we can buy a phone number, for regulatory purposes Twilio needs us to create an address that will be associated with that number. The code below shows how we can create a new address, if one doesn't already exist.

private static async Task<string> GetOrCreateAddress()
{
    var existingAddresses = await AddressResource.ReadAsync(limit: 20);

    if (existingAddresses.Any())
    {
        Console.WriteLine($"No address created, existing address found with Sid: {existingAddresses.First().Sid}");

        return existingAddresses.First().Sid;
    }

    var createdAddress = await AddressResource.CreateAsync(
        friendlyName: "My IVR Address",
        customerName: "Mr E Guest",
        street: "1 Guest Road",
        city: "Guesttown",
        region: "Guestshire",
        postalCode: "GS1 1GS",
        isoCountry: "GB");

    Console.WriteLine($"Address created, with Sid: {createdAddress.Sid}");

    return createdAddress.Sid;
}

We can see that the Twilio API has the concept of an AddressResource which they use to represent an address. We use the ReadAsync method to fetch up to a number (twenty in this case) of addresses that may exist in our Twilio account. If we haven't yet created an address on our account, we use the CreateAsync method to create a new address.

NOTE: I have used dummy address values here so substitute these as you see fit :-)

Now we can use the address details to buy our phone number.

Buying a local phone number

Twilio provide the ability to search for locally available phone numbers using their REST API (via LocalResource requests). You can search for numbers that match several criteria, including area code, country code and the features the number supports (e.g. SMS). Once you have identified the number you want, you can use the IncomingPhoneNumberResource API to buy a specific number - supplying an appropriate address (which we created earlier).

The method below finds a local phone number and provisions the first in the list:

private static async Task<string> BuyLocalPhoneNumber(string addressSid)
{
    // Find local phone numbers for the Macclesfield area
    var availableLocalPhoneNumbers = await LocalResource.ReadAsync(
        contains: "+441625",
        pathCountryCode: "GB",
        voiceEnabled: true,
        smsEnabled: true,
        limit: 50);

    var numberToBuy = availableLocalPhoneNumbers.First();

    var boughtNumber = await IncomingPhoneNumberResource.CreateAsync(phoneNumber: numberToBuy.FriendlyName,
        addressSid: addressSid);

    return boughtNumber.Sid;
}

If we were to run this method as-is it would buy a new phone number every time. As we're on a limited budget it probably makes sense to only create a number if one doesn't already exist - so we wrap the BuyLocalPhoneNumber method in a method that first checks for an existing number on the account, using the IncomingPhoneNumberResource API:

private static async Task<string> GetOrProvisionIncomingPhoneNumber(string addressSid)
{
    var incomingPhoneNumbers = await IncomingPhoneNumberResource.ReadAsync(limit: 20);
    if (incomingPhoneNumbers.Any())
    {
        var existingIncomingPhoneNumber = incomingPhoneNumbers.First();

        Console.WriteLine($"No phone number bought, existing incoming phone number found with friendly name: {existingIncomingPhoneNumber.FriendlyName} " +
                            $"and Sid: {existingIncomingPhoneNumber.Sid}");

        return existingIncomingPhoneNumber.Sid;
    }

    var incomingPhoneNumberSid = await BuyLocalPhoneNumber(addressSid);
    Console.WriteLine($"phone number bought with Sid: {incomingPhoneNumberSid}");
    
    return incomingPhoneNumberSid;
}

Put it all together!

We now have all the building blocks needed to buy a phone number - so we can update our console app to call the building blocks and actually get a phone number!

static async Task Main(string[] args)
{
    var config = BuildApplicationConfiguration();

    TwilioClient.Init(config["Twilio:AccountSid"], config["Twilio:AuthToken"]);

    string addressSid = await GetOrCreateAddress();
    string phoneNumberSid = await GetOrProvisionIncomingPhoneNumber(addressSid);
}

Once we run this we should see something similar to the following:

twilio-buy-phone-number1

Now we can actually call the phone number to hear a pleasant voice telling us we have a trial account and that we should press any key to execute our code! If we do press a key we then get a standard Twilio demo voice message which shows us everything is working fine!

Homer Woohoo

Summary

In this post we setup our Twilio trial account and bought a phone number that will be the entry point to our IVR.

In the next post we'll look at how to create the API that will power our IVR, instead of just playing a slightly robotic message!

Building an IVR using Twilio
Share this