Introduction
Twilio refers to a set of web services, APIs and tools provided by the Twilio company. These are used to send and receive text/picture messages, make and receive phone calls, and embed VoIP calling into web and native mobile applications. They provided many helper libraries, available for various server-side programming environments like Node.js, PHP, .NET, Java, Ruby, Python, Apex etc.
In this blog, we will use the Twilio node module to implement inbound and outbound voice calls in Node.js.
Prerequisites
The code snippets shown in this blog are written using Node.js version 8.9.4.
Create a Twilio account
Before you can make a call from Node.js, you’ll need to sign up for a Twilio account or log in to an account you already have. Head over to Twilio.com/try-twilio to sign up.
Buy a Twilio Number
The next thing you’ll need is a voice-capable Twilio phone number. If you currently do not own a Twilio phone number with voice call functionality, you’ll need to purchase one.
Follow the steps below,
- Go to the Phone Numbers page of Twilio Developer Console.
- Search for the number you want to buy by selecting Voice in the CAPABILITIES section.
Note: Buy a phone number with voice capabilities to make and receive calls
- You’ll then see a list of available phone numbers and their capabilities. Find a number that suits your fancy and click Buy to add it to your account.
All phone numbers in Twilio’s API require E.164 formatting i.e. the number should include “+”, then country code, then a number with no dashes or spacing in between. For example, +918545658754.
Now that you have a Twilio account and a programmable phone number, you have the basic tools you need to make a phone call.
Get Twilio Credentials
Twilio credentials are required to authenticate and access Twilio APIs,
- Go to Twilio homepage.
- Go to Account Details.
- Retrieve ACCOUNT SID and AUTH TOKEN.
Install Twilio node module
You could use Twilio’s HTTP API to make phone calls, but we’ll make things even simpler by using the Twilio module for Node.js.
Let’s use npm to install the required library. Simply fire up a terminal or command line interface on your machine that already has Node and npm installed, and run the following command in your project directory.
1 |
npm install twilio |
The code snippets uses Twilio SDK v3.17.2. Please check the migration guide while migrating from one version to other.
TwiML
TwiML (the Twilio Markup Language) is a set of instructions you can use to tell Twilio what to do when you receive an incoming call, SMS, or fax.
At its core, TwiML is an XML document with special tags defined by Twilio to help you build your Programmable Voice application.
The following will say Hello, world! when someone dials a Twilio number configured with this TwiML:
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>Hello, world!</Say> </Response> |
You can read more about TwiML here.
TwiML is case sensitive, so make sure to capitalize the first letter of your elements.
Make a Voice call
It is interesting to see how with just a few lines of code, your Node.js application can make and receive phone calls with Twilio Programmable Voice.
Let us try to understand what exactly happens using a block diagram,
From our application we make an HTTP request to the Twilio server with the following details:
- From number (Twilio number)
- To number (Callee’s number)
- URL (TwiML)
The URL argument points to some TwiML, which tells Twilio what to do next when our recipient answers their phone.
When our recipient answers their phone the Twilio server will make an HTTP request to our application to fetch the TwiML.
Note that you can also provide a link to TwiML file, such as
Twilio then reads the TwiML instructions to determine what to do, whether it’s recording the call, playing a message for the caller, or prompting the caller to press digits on their keypad.
Sample code
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const accountSid = 'your_account_sid'; const authToken = 'your_auth_token'; const client = require('twilio')(accountSid, authToken); client.calls .create({ url: 'http://notification.voice.com/voice-call', method: 'GET', to: '+123456789', from: 'your_twilio_number' }) .then(call => console.log(call.sid)) .done(); |
This code starts a phone call between the two phone numbers that we pass as arguments. The from number is our Twilio number, and the to number is who we want to call.
The URL argument points to an API which will respond with TwiML.
Before using this code though, we need to edit it a little to work with your Twilio account. Swap the placeholder values for accountSid and authToken with your personal Twilio credentials.
Please note that it’s okay to hardcode your credentials when getting started, but you should use environment variables to keep them secret before deploying to production. We use nconf to do this.
Above code snippet can also be implemented using callbacks.
API code
Here is a sample API which will generate TwiML. The /voice-call API will be called by the Twilio server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const express = require('express'); const VoiceResponse = require('twilio').twiml.VoiceResponse; const app = express(); // HTTP GET to /voice-call in our application app.get('/voice-call', (request, response) => { // Use the Twilio Node.js SDK to build an XML response const twiml = new VoiceResponse(); twiml.say({ voice: 'alice' }, 'Hello, how are you?'); // Render the response as XML response.type('text/xml'); response.send(twiml.toString()); }); // Create an HTTP server and listen for requests on port 3000 app.listen(3000); |
This ( /voice-call) API will return a XML response i.e.
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8"?> <Response> <Say voice="alice">Hello, how are you?</Say> </Response> |
This TwiML instructs Twilio to play a message Hello, how are you? to the recipient (callee).
Receive a Voice call
Webhooks
A webhook is a user-defined callback mechanism that is triggered when an event occurs. When that event occurs, the source site makes an HTTP request to the URL configured for that webhook.
Twilio provides allows us to configure webhooks for various events like Incoming Call (A call comes in), Call Status Changes, Primary Handler Fails etc. When each of these events occur our webhook will be triggered via an HTTP request.
To define your webhook, go to your number’s configuration page.
Select the number for which you would like to add the webhook.
Scroll down to Voice & Fax section. Here you need to provide 3 things,
- Webhook event
- HTTP request URL
- HTTP request method
In the above screenshot the webhook event is A CALL COMES IN.This event is triggered when you receive a call on your Twilio number. When this happens Twilio makes an HTTP request (usually a POST or a GET) to the URL you configured for the webhook.
The application should return TwiML response back to Twilio server.
The Twilio server will then parse that TwiML response and respond to the caller based on that response.
API Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const express = require('express'); const VoiceResponse = require('twilio').twiml.VoiceResponse; const app = express(); // Create a route that will handle Twilio webhook requests, sent as an // HTTP GET to /v1/greeting in our application app.get('/v1/greeting', (request, response) => { // Use the Twilio Node.js SDK to build an XML response const twiml = new VoiceResponse(); twiml.say({ voice: 'alice' }, 'Hello'); // Render the response as XML in reply to the webhook request response.type('text/xml'); response.send(twiml.toString()); }); // Create an HTTP server and listen for requests on port 3000 app.listen(3000); |
The above API will return the following TwiML,
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8"?> <Response> <Say voice="alice">Hello</Say> </Response> |
How we used Twilio’s voice call APIs
We would like to share how we structured our application to handle different voice API calls.
Architecture
We have an alert notification system which sends notification through different mediums such as,
- SMS
- Voice call
- Pager
For sending SMS and making voice calls we used the Twilio API.
The system architecture is very simple to understand.
We wanted to separate the code into various modules based on the role,
Application
This contains the core logic of our application. This is a REST API server developed using NodeJs which handles all the requests from our front end applications (Web portal, mobile app and IoT devices).
Microservice
This micro-service will handle the sending of notifications. This will send SMS, Email, Pager and Voice calls by using the Twilio and Mailgun APIs.
Voice API server
We developed a different server specially to handle all the voice call related routes. This server generates TwiML which gathers user input and submits the user’s response back to one of its own route, which then does further processing.
There is a two way communication between this component and the Twilio API. For example,
- Twilio makes a request to this component to get the TwiML to play a message and ask for user input.
- The Voice API server will return TwiML which will contain that message.
- Twilio server will play that message to the user.
- After user gives feedback by pressing a digit, the Twilio server will make another request to Voice call API with the user’s entered digit.
- Voice call API will then take the user input, do some processing and respond back with another TwiML. (“Thank you for giving feedback” message)
- Twilio server will play the thank you message to the user and the transaction ends here.
Advantages
Following are some of the major advantage,
Well maintained code
Every component does what it needs to do. The core application will have the APIs which are required for the application to run. The micro-service (Notification processor as we named it) will take care of sending different notifications. Finally all the HTTP requests made by Twilio will land on Voice API server which will serve the TwiML.
Quick problem identification
It is very easy to identify an issue or bug, since we have different components that perform specific jobs. If there is an issue receiving notifications then we check only the microservice. If there is an issue with voice calls not being parsed properly then we know that we need to check Voice API server.
Ease of adding new notification type
Adding a new notification type is easy. If tomorrow we decide to add webhook notifications then we need to update the microservice and integrate webhook. No changes are required in the core application and the Voice API server.
Reduced testing scope
The scope of testing and causing a bug is limited to a particular component. If we do code modifications in a component then we are confident that it will not cause any issues in other components.
Use cases
Problem
We had to notify the user for different type of actions through voice call, then take input from the user and update records in our database based on the user’s input.
Goal
To keep it simple lets assume we have 3 different type of actions
- Action I
- Action II
- Action III
For each action we want to take user’s feedback, assume the feedback to be,
- Yes (Press 1)
- No (Press 2)
Based on the input we want to update the database records. We wanted the system to easily handle any new actions as well.
Solution
Lets try to understand the solution using a block diagram
Microservice will make HTTP request to Twilio server for different actions. Each action will have a corresponding REST API in our Voice API server which will return the TwiML.
Twilio server will then parse this TwiML and make a call to the user.
Once the user enter digits on their keypad, Twilio will submit those digits in a POST request back to our Voice API server.
Voice API server will then update database based on user input.
Code
Let us try to understand the code sample,
Microservice sending notifications
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const accountSid = 'your_account_sid'; const authToken = 'your_auth_token'; const client = require('twilio')(accountSid, authToken); // Method to make a twilio call for different action type with data function sendVoiceNotification (actionType, id) { client.calls .create({ url: `http://notification.voice.com/${actionType}/id/${id}`, method: 'GET', to: '+123456789', from: '+987654321' }) .then(call => console.log(call.sid)) .done(); } // Make a twilio call for action type 1 with id 3 (Some data which will be used later for processing) sendVoiceNotification('action1', 3); // Similarly make calls for different action type // sendVoiceNotification('action2', 15); // sendVoiceNotification('action3', 16); |
To keep things simple we wrote a function sendVoiceNotification() which takes actionType and id. When the callee answers the call, Twilio will make a HTTP GET request to our application.
For example if actionType is action1 and id is 3 then the request URL will be http://notification.voice.com/action/action1/id/3.
TwiML response
These route are defined in our Voice API server which will generate the TwiML based on the actionType and data i.e id.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
const express = require('express'); const VoiceResponse = require('twilio').twiml.VoiceResponse; const app = express(); // HTTP GET to generate the TwiML based on action type and id app.get('/action/:action/id/:id', (request, response) => { const data = request.params; const actionType = data.action; const id = data.id; // Get details from database based on action type and id // Assume that this will query the database and fetch the needed // data which can be used to generate the required TwiML const callDetails = getDataByActionType(actionType, id); const twiml = new VoiceResponse(); // Use gather to record user input const gather = twiml.gather({ action: 'http://notification.voice.com/action/${actionType}/id/${id}', input: 'dtmf', timeout: 15, numDigits: 1, method: 'POST' }); // Generate the message for the receiver // getCallText(), this function will generate the needed call text gather.say(getCallText(callDetails)); // If the user doesn't enter input (any digit) within the timeout (15 sec) duration, // then repeate the same message again twiml.redirect({ method: 'GET' }, 'http://notification.voice.com/action/${actionType}/id/${id}'); // Render the response as XML in reply response.set('Content-Type', 'text/xml'); response.send(twiml.toString()); }); // Create an HTTP server and listen for requests on port 3000 app.listen(3000); |
Sample TwiML
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="UTF-8"?> <Response> <Gather action="http://notification.voice.com/action/action1/id/3" input="dtmf" timeout="15" numDigits="1" method="POST"> <Say>Action 1 is triggered with id 3, please press 1 for "Yes" and 2 for "No".</Say> </Gather> <Redirect method="GET">http://notification.voice.com/action/action1/id/3</Redirect> </Response> |
You can learn more about the Gather tag here.
Handling user input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const express = require('express'); const VoiceResponse = require('twilio').twiml.VoiceResponse; const urlencoded = require('body-parser').urlencoded; const app = express(); // Parse incoming POST params with Express middleware app.use(urlencoded({ extended: false })); // HTTP POST to handle user input app.post('/action/:action/id/:id', (request, response) => { // Use the Twilio Node.js SDK to build an XML response const data = request.params; const actionType = data.action; const id = data.id; const digit = request.body.Digits; // Update database based on the user input updateDatabase(actionType, id, digit); const twiml = new VoiceResponse(); twiml.say({ voice: 'alice' }, 'Thank you for your feedback.'); // Render the response as XML in reply to the webhook request response.type('text/xml'); response.send(twiml.toString()); }); // Create an HTTP server and listen for requests on port 3000 app.listen(3000); |
The user entered digits are available in the request body i.e. request.body.Digits. You can update the database or take further action based on the entered digit.
Note that the call will end with an error if your server doesn’t respond back with some TwiML.
Alternative – Call SID
For every call which is initiated by Twilio there is a unique ID associated with it. client.calls.create() will return a call object which contains a call.sid. This SID can be used to uniquely identify a call.
Alternatively instead of passing data as part of URL like we did i.e. http://notification.voice.com/action/${actionType}/id/${id}, we could store this info in database and use call SID.
After initiating the Twilio call, we will store the details into database as shown below,
call_sid | action_type | id |
CAed0ea7ab63d425e987cb24eXXXX | action1 | 3 |
CAed0ea7bb63d425e987cb24eXXXX | action2 | 15 |
Instead of using http://notification.voice.com/action/${actionType}/id/${id} we could simply use http://notification.voice.com/action
1 2 3 4 |
// HTTP GET const callSID = request.params.CallSid; // HTTP POST const callSID = request.body.CallSid; |
Once you have the call SID further processing can be carried out by fetching the additional details from database using the call SID.
Twilio logs
Twilio provides logs for each call made. This is the first place you should check in case your users are complaining about not receiving phone calls, abruptly ending phone calls or any other issue.
You can find voice call logs here.
Debugging
To debug an issue, click on any call log which failed. Failed calls are highlighted with red color.
Log summary
This gives a brief detail about the call.
Error and Warning
This section will list out the error or the warning that occurred while processing the call.
For example, in the above image we gave an API route which does not exist.
Request Inspector
This section contains all the requests which were made during the call.
Twilio initially requested for the TwiML using an HTTP GET request.
You can see the TwiML being sent back in response body.
This TwiML is used to take the user’s input ( Gather)and then POST that input to another URL which is specified in the action attribute.
To demonstrate an error condition, we’ve specified an invalid URL in the action attribute.
When the user presses 2 you can see that a request is made by Twilio to the specified API for the next TwiML. The Digits parameter in the request contains the number that the user dialed.
Note that this request failed with status 404 since the route does not exist.
Summary
Rather than building an app’s Voice and SMS functionality from scratch, developers can make use of Twilio APIs which will be a huge time saver. Here are some other interesting use cases that we found that leverage the power of Twilio to automate things,
- Automated survey with Nodejs and Express
- Automate Wedding
- US Democratic National Committee voter hotline
References
- Twilio document on how to make outbound phone calls in Node.js
- Twilio document on how to make inbound phone calls in Node.js
- Twilio document on how to know the input provided by the user
Posted By: Vishnu Kyatannawar and Sindhusha Balla, Osmosees