Rate this page:

Automated Survey with Python and Flask

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.

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.

Here's how it works at a high level

  1. The end user calls or sends an SMS to the survey phone number.
  2. Twilio gets the call or SMS and makes an HTTP request to your application asking for instructions on how to respond.
  3. Your web application instructs Twilio (using TwiML) to Gather or Record if it receives voice input. If using SMS it will prompt for text input with Message.
  4. 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.
  5. After storing the answer, our server will instruct Twilio to Redirect the 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.

Click here to get started!

Creating a Survey

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 dbseed command:

$ python dbseed

Loading Code Sample...

        Load a survey and its questions from a JSON file


        Let's take a moment to understand the flow of a Twilio-powered survey as an interview loop.

        What is an interview loop?

        The 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.

        Configure your Twilio number

        Configuring a Twilio Number

        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.

        Automated Survey Webhook Setup

        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.

        Respond to a Twilio request

        Responding to a Twilio Request

        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 <Redirect> verb.

        The /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.

        Loading Code Sample...

              Route incoming SMS and voice calls with a survey controller


              Let's see how we can use a Controller to handle and maintain the state of the survey.

              Build the question controller

              Question Controller

              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.

              Loading Code Sample...

                    Build the appropriate question for SMS or voice


                    Let's see how the response is built.

                    Build your TwiML response

                    Building Our TwiML Verbs

                    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 action attribute.

                    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.

                    During the 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.

                    Loading Code Sample...

                          Build TwiML responses for survey questions


                          Now let's see what to do once we receive a response.

                          Handle survey responses

                          Handling Responses

                          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:

                          • Body contains the text message from an answer sent via SMS.
                          • Digits contains the keys pressed for a numeric question.
                          • RecodingUrl contains the URL for a recorded message.
                          • TranscriptionText contains 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 redirect_twiml function.

                          Loading Code Sample...

                                Methods for handling survey responses


                                Finally, let's see how to visualize the results.

                                Display survey results

                                Displaying the Survey 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.

                                Loading Code Sample...

                                      Display the survey results


                                      That's it!

                                      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.

                                      What's next?

                                      Where to Next?

                                      If you're a Python developer working with Twilio, you might enjoy these other tutorials:

                                      Gather User Input via Keypad (DTMF Tones)

                                      Gather user input during a phone call through the phone's keypad (using DTMF tones).

                                      Receive and Reply to SMS and MMS Messages

                                      Use Programmable SMS to respond to incoming SMS messages in your web application.

                                      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!

                                      Orlando Hidalgo Kat King Samuel Mendes Andrew Baker Jose Oliveros Brianna DelValle
                                      Rate this page:

                                      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.

                                      Loading Code Sample...

                                            Thank you for your feedback!

                                            Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

                                            Sending your feedback...
                                            🎉 Thank you for your feedback!
                                            Something went wrong. Please try again.

                                            Thanks for your feedback!