Automated Survey with Node.js and Express
Twilio is launching a new Console. Some screenshots on this page may show the Legacy Console and therefore may no longer be accurate. We are working to update all screenshots to reflect the new Console experience. Learn more about the new Console.
Implementing your own automated survey with Twilio Voice and SMS can be a huge time saver when you need to collect feedback from a group of people. Whether it's participants in a social services program or a field service organization, you can quickly set up your own survey to collect structured data over the phone or via text message. Here's how it works at a high level:
- The end user calls or texts the survey phone number.
- Twilio gets the call or text and makes an HTTP request to your application for instructions on how to respond.
- Your web application serves up TwiML instructions to
Recordthe user input over the phone, or prompts for text input with
- After each question, Twilio makes another request to your server with the user's input, which your application stores in its database.
- Your application returns a TwiML response to Twilio with instructions to either ask the next question or end the survey.
What We Will Learn
This How-To demonstrates how to use TwiML to deliver a survey that can be completed via voice call. The survey actually works via SMS text messages, too, but we're going to focus on the looping logic necessary to conduct an interview over the phone. We will create a voice call flow using the
Gather TwiML verbs.
You will also learn how to maintain conversation state in a database that spans multiple webhook requests. Beyond an automated survey, these techniques can be applied to implement more complex IVR systems or text message interfaces.
Instacart uses Twilio to power their customer service surveys and integrate that feedback into their customer database. Read more here.
About This Application
Like most Node.js web applications, this one relies on a number of smaller modules installed via npm to handle HTTP requests and store data. The key modules for this application are:
Bootstrapping the Application
This is the Node file we will execute to serve up our web application. We load our app's configuration from an external file containing the HTTP port we want to run on and the MongoDB database connection string we need to store data using Mongoose.
We also define four routes to be handled by our web application. Three of the routes are webhooks that will be requested by Twilio when your Twilio survey number receives an incoming call or text, or when the results of a transcription job are ready. The fourth route will be used by our reporting UI to get the results of the survey from the database.
Now that our application is all set up, let's look at the high level steps necessary to implement a voice interview via Twilio and TwiML.
The Voice Interview Loop
The user can enter input for your survey over the phone using either their phone's keypad or by speaking. After each interaction Twilio will make an HTTP request to your web application with either the string of keys the user pressed or a URL to a recording of their voice input.
It's up to our application to process and store the user's input, maintain the current state of the conversation, and respond back to the user. Let's dive into this flow to see how it actually works.
Responding to a Phone Call
To initiate the interview process, we need to configure one of our Twilio numbers to send our web application an HTTP request when we get an incoming call or text (remember, our app accepts text messages as well, even though we're focusing on the voice side).
Click on one of your numbers and configure Voice and Message URLs that point to your server. In our code, the routes are
If you don't already have a server configured to use as your webhook, ngrok is a great tool for testing webhooks locally.
We've configured our webhooks in the Twilio Console. Next let's see how to respond to requests.
Responding to a Phone Call
voice route maps to this Express handler function, which takes an HTTP request and HTTP response as arguments. From the request, we can access the phone number of the person calling in by the
From POST parameter - we can use this to uniquely identify a person taking the survey.
We can also access the
RecordingUrl, which contains any voice input from the user.
Digits may also be present, which contains the string of keys entered by the user on their keypad. If this is the user's first call to our system or they failed to enter any input to the previous question, these values might be blank Strings.
We also create a
We've seen how to handle requests to our webhooks. Now lets go deeper into how to generate TwiML to redirect our users to the next question and generate speech from text.
Asking a Question
If either the user did not enter any input, it's the first question in the survey, or there's still another question after the current one, we will build a TwiML response that will ask the next question.
We define a few inner functions here to help us build our response.
respond completes our TwiML response and sends XML content back to Twilio.
say is shorthand for appending a string of text that will be read back to the user with Twilio TTS (text-to-speech) engine.
Asking a Question
In our TwiML response, we need to include either a
Gather tag or a
Record tag to collect input from the user. Which tag we use depends on the question type in the survey.
Record use case, we also provide a transcription callback URL. Unlike
Record, the transcription callback happens outside the loop of the call, sometime in the very near future (several seconds rather than minutes). So while you can't count on having the transcript results during the flow of the call, you can add the transcript to your database record to give the response a chance to help enrich that data a bit.
Another caveat here is that Twilio's transcription service is automated and not always super accurate. If transcription accuracy is critical for you, you might consider using a service with human translators like Rev.com.
Now that we know how to ask questions and gather user input. Lets see how to store the survey state.
Updating Conversation State
Saving the user's response and maintaining the state of our conversation with the user is a concern best handled at the model layer of our server-side application, so we use our MongoDB-backed Mongoose model to handle this for us.
Abstracting the survey state from the controller also has the benefit of letting us re-use it for handling survey inputs from text messages. #winning!
We have a general idea of how we want to persist survey state. Lets go into more details and have a look at what our schema looks like.
The Mongoose Schema
In our model, we create a schema that allows us to constrain and validate the form of documents that we insert into MongoDB. However, the
responses array, so we have some flexibility there with the objects we use to store our user's answers.
Next, lets see how we will keep track of our data by using the type property on our models.
Prior to saving a response from the user, we convert the raw string values submitted into data types that will be easier for us to work with as we analyze and visualize our survey data.
We've seen how to persist answers to our questions. Next we'll see how guide the user to the next question.
Queueing Up the Next Question
After the current response is saved, we invoke the callback to our controller with the index of the next question in the survey. This may be longer than the actual length of the survey, which means we're finished asking questions!
After you've finished asking questions, you might be interested in seeing how people responded to your survey. We won't spend too much time on visualizing the data, but we've included a bit of code in this sample app that shows you how you might approach that.
Checking Out The Results
In this app's static asset directory, you'll find an
index.html file that contains some markup for displaying the results of our survey questions. It makes an Ajax request to our Express application to get the last 100 results from our survey to display here.
For phone survey responses, there is a link to listen to the recording in addition to the transcribed text sent to our application.
Sometimes, the results aren't enough. So lets see how we can drill deeper and hear the actual recordings.
Playing Twilio Recordings
RecordingUrl sent to us from Twilio in a new window. This works well enough, but you might consider playing them directly on the page with a more robust audio library like Buzz. When preparing the recordings to be played via HTML 5 audio, note that each recording is made available in either WAV or MP3 formats.
That's All Folks
And that's it! You can reload the page on your localhost in your browser and watch as the results fly in from your users.
Where to next?
If you're a Node.js developer working with Twilio, you might also enjoy these tutorials:
Learn how to use Twilio Client to convert web traffic into phone calls with the click of a button.
Learn to implement two-factor authentication (2FA) in your web app with Twilio-powered Authy.
Did this help?
Thanks for checking this tutorial out! If you have any feedback to share with us, we'd love to hear it. Connect with us on Twitter and let us know what you build!
Need some help?
We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.