Have you ever wondered how to create an automated survey that can be answered over phone or SMS?
This tutorial will show how to do it using Twilio's API.
- The end user calls or sends an SMS to the survey phone number.
- Twilio gets the call or SMS and makes an HTTP request to your application asking for instructions on how to respond.
- Your web application instructs Twilio (using TwiML) to
Recordif it receives voice input. If using SMS it will prompt for text input with
- After each question is answered, Twilio makes another request to your server with the user's input. That input is stored in the application's database.
- After storing the answer, our server will instruct Twilio to
Redirectthe user to the next question or finish the survey.
Instacart uses Twilio to power their customer service surveys and integrate that feedback into their customer database. Read more here.
For your convenience, the application's repository already includes one survey that can be loaded into the database.
You can modify the survey questions by editing the survey.json file located in the root of the repository and re-running the app's
$ python manage.py dbseed
Let's take a moment to understand the flow of a Twilio-powered survey as an interview loop.
The user can answer a question for your survey over the phone by either their phone's keypad or by voice. 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.
For SMS surveys the user will answer questions by replying with another SMS to the Twilio number that sent the question.
It's up to the application to process, store and respond to the user's input.
Let's dive into this flow to see how it actually works.
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 SMS.
Click on one of your numbers and configure Voice and Message URLs that point to your server. In our code, the route is
/voice for Voice and
/message for Messaging.
If you don't already have a server configured to use as your webhook, ngrok is a great tool for testing webhooks locally.
Next, we will see how to handle requests to our webhooks.
Right after receiving a call or SMS, Twilio will send a request to the URL specified in that phone number's configuration.
The endpoint used for a call is the
/voice endpoint. It will check to see if we have a survey with questions to be answered, welcome the user, and redirect them to the first question using Twilio's
/message endpoint will receive each SMS, welcome the user, and redirect them to the proper question. If the SMS request has a
question_id variable on its session then we redirect to our answer endpoint to store the answer. We'll dive into this routing more thoroughly later on. For now, imagine that we are going to the first survey question after welcoming the user.
Let's see how we can use a Controller to handle and maintain the state of the survey.
This endpoint checks if the incoming request is from an SMS or a Call and builds a survey question as a TwiML response. Each type of question and interaction (Call or SMS) will produce different instructions on how to proceed. For instance, we can record a voice message or gather a key press during a call, but we can't do the same for text messages.
When the user interacts with our survey over SMS we don't have something like an ongoing call session with a well defined state. Since all SMS requests will be sent to the
/message main endpoint, it becomes harder to know if an SMS is answering question 2 or 20. To solve that, we can use Twilio Cookies to keep track of what question is being answered at the moment. This is done by setting a
question_id session key, leaving Flask to handle cookie management.
Let's see how the response is built.
If the survey question is "numeric" or "boolean" (yes/no) in nature, then we use the
<Gather> verb. However, if we expect the user to record a free-form answer we use the
<Record> verb. Both verbs take an
Twilio will use this attribute to define our answer endpoint to use as a callback. That endpoint will be responsible for receiving and storing the caller's answer.
Record verb creation, we also ask for a Transcription. Twilio will process the recording and extract useful text, making a request to our transcription endpoint when the transcription is complete.
Now let's see what to do once we receive a response.
After the user has finished submitting their answers, Twilio sends us a request explaining what happened and asking for further instructions.
At this point, we need to recover data from Twilio's request parameters (
extract_content handles this) and store it in the database.
Recovered parameters vary according to what we asked in our questions:
Bodycontains the text message from an answer sent via SMS.
Digitscontains the keys pressed for a numeric question.
RecodingUrlcontains the URL for a recorded message.
TranscriptionTextcontains the text of a voice recording transcription.
Finally we redirect to our Question controller, which will ask the next question in the loop. This is done in the
Finally, let's see how to visualize the results.
For this route we simply query the database for our survey answers using SQLAlchemy and then display the information within a template. We show a panel for every question from the survey, and inside each panel we list the responses from the different calls.
You can access this page in the applications root route.
If you have configured one of your Twilio numbers to the application built in this tutorial, you should be able to take the survey and see the results under the root route of the application. We hope you found this sample application useful.
If you're a Python developer working with Twilio, you might enjoy these other tutorials:
Gather user input during a phone call through the phone's keypad (using DTMF tones).
Use Programmable SMS to respond to incoming SMS messages in your web application.
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!