Collect
Data collection is one of the most common tasks that virtual assistants perform. The Collect
action enables you to ask questions to users and efficiently collect answers.
Collect
is similar to using web forms. You define the field name and the type for each question and Collect
will render conversational flow to ask the user the questions.
The answers are collected in the Assistant's memory and sent in the request parameters on every Autopilot webhook.
- Collect Properties
- Questions Properties
- Processing Answers
- Questions
- Validate
Collect properties
Property | Description | Required |
name |
A unique string that describes the data being collected. No spaces are allowed. |
Yes |
questions |
An array of questions to ask the user. |
Yes |
on_complete |
The action to execute when the questionnaire is successfully completed. It can take a redirect action with a task or a URL (see request params). |
Yes |
Question properties
Property | Description | Required |
question |
The question to ask. It can be a { say } action. |
Yes |
name |
The name of the field collected. This must be unique within the questionnaire. No spaces are allowed. |
Yes |
type |
The field type of the questions being collected. This can be a built-in field type or a custom field type. See built-in field types for more information.
|
No. If no type is provided free-form input will be collected. |
voice_digits |
Provide the option to use DTMF in addition to speech for the input if they are interacting with your bot over the voice channel. This property will be ignored on other channels. More details below. |
No. |
Processing Answers
You can consume Collect's answers from your application using Functions or your own backend. The video below describes how to build actions dynamically and consume the user's answers using Twilio Functions.
Questions
Example 1: Single free-form question
Free-form questions that are going to receive any type of input, do not need to specify any Type
.
{
"actions": [
{
"collect": {
"name": "collect_comments",
"questions": [
{
"question": "Please let us know any comments or feedback on your recent purchase",
"name": "comments"
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
The answers come in the Memory parameter with a JSON containing:
{
"twilio": {
"collected_data": {
"collect_comments": {
"status": "complete",
"date_started": "2018-09-10T10:42:00Z",
"date_completed": "2018-09-10T10:42:00Z",
"answers": {
"comments": {
"answer": "I love the product! thanks so much!",
"filled": true,
"attempts": 1,
"confirmed": false
}
}
}
}
}
}
Example 2: Single question: what's your name?
{
"actions": [
{
"collect": {
"name": "ask_first_name",
"questions": [
{
"question": "Hi! What's your name",
"name": "first_name",
"type": "Twilio.FIRST_NAME"
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
The answers come in the Memory parameter with a JSON containing:
{
"twilio": {
"collected_data": {
"ask_first_name": {
"status": "complete",
"date_started": "2018-09-10T10:42:00Z",
"date_completed": "2018-09-10T10:42:00Z",
"answers": {
"first_name": {
"answer": "Robert",
"type": "Twilio.FIRST_NAME",
"filled": true,
"attempts": 1,
"confirmed": false
}
}
}
}
}
}
Example 3: Multiple questions
The questions will be asked sequentially.
{
"actions": [
{
"collect": {
"name": "travel_questions",
"questions": [
{
"question": "Do you like traveling?",
"name": "likes_traveling",
"type": "Twilio.YES_NO"
},
{
"question":"What's your favorite city?",
"name": "favorite_city",
"type": "Twilio.CITY"
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
The answers come in the Memory parameter with a JSON containing:
{
"twilio": {
"collected_data": {
"travel_questions": {
"status": "complete",
"date_started": "2018-09-10T10:42:00Z",
"date_completed": "2018-09-10T10:42:00Z",
"answers": {
"likes_traveling": {
"type": "Twilio.YES_NO",
"answer": "Yes",
"filled": true,
"attempts": 1,
"confirmed": false
},
"favorite_city": {
"answer": "San Francisco",
"type": "Twilio.CITY",
"filled": true,
"attempts": 1,
"confirmed": false
}
}
}
}
}
}
Example 4: Collect Images
Collects an image
{
"actions":[
{
"say":"Great! I can help you make a reservation."
},
{
"collect":{
"name":"reservation_questions",
"questions":[
{
"question":"What is your insurance company name?",
"name":"insurance_name"
},
{
"question":"Please take a picture of insurance card?",
"name":"insurance_card",
"type":"Twilio.MEDIA",
"validate":{
"allowed_types":{
"list":[
"image/jpeg",
"image/gif",
"image/png",
"image/bmp",
"application/pdf"
]
},
"on_failure":{
"messages":[
{
"say":"We do not accept this format. Please send image of insurance card"
},
{
"say":"Lets try one more time to get the insurance card picture."
}
],
"repeat_question":true
},
"on_success":{
"say":"Great, we've got your insurance card."
},
"max_attempts":{
"redirect":"task://redirect",
"num_attempts":3
}
}
}
],
"on_complete":{
"redirect":"https://www.example.com/end_collect"
}
}
}
]
}
The answers come in the Memory parameter with a JSON containing:
{
"twilio":{
"collected_data":{
"reservation_questions":{
"status":"completed",
"date_started":"2019-12-20T20:45:19Z",
"date_completed":"",
"answers":{
"insurance_name":{
"answer":"HealthCo",
"type":"",
"filled":true,
"attempts":1,
"validate_attempts":0,
"confirm_attempts":0,
"confirmed":false
},
"insurance_card":{
"answer":"",
"type":"Twilio.MEDIA",
"filled":true,
"attempts":2,
"validate_attempts":1,
"confirm_attempts":0,
"confirmed":false,
"media":{
"type":"image/jpeg",
"url":"http://XXXXXXX"
}
}
}
}
}
}
}
Validate
Collect enables you to validate the user input with the Validate
instruction.
Validate property | Description | Default |
allowed_values | A list of allowed values |
If no list of values is specified, Validate will verify that the value is a valid type for the specified field type. For example, if the field is Twilio.DATE, Validate will verify that the provided value is a valid date. |
webhook | A webhook to validate the value being collected. Useful when validating fields like order numbers or available dates that can only be validated against your business logic |
If no webhook is specified, Validate will verify that the value is a valid type for the specified field type. If both allowed values and webhook are specified, only allowed values is considered. See Example 6. |
on_failure | The messages when the input is invalid. |
By default, the on_failure messages are defined in the Stylesheet. It can be overridden by defining on_failure messages in the collect. The on_failure action is defined in the Defaults. It can be overridden by defining the on_failure message in the collect. |
on_success | The message when the question is answered successfully. | By default, the on_success message is defined in the Stylesheet. It can be overridden by defining on_success message in the collect. |
max_attempts | How many times it should ask the user the question | By default, max_attempts is defined in the Stylesheet. It can be overridden by defining max_attempts in the collect. |
Collect Validate Webhook
The request parameters
The Collect Validate webhook has all the Standard Autopilot Parameters except the two parameters Field_{field-name}_Value and Field_{field-name}_Type and the Collect Validate specific parameters defined below:
Parameter |
Description |
Example |
CollectName |
The name of the Collect |
CollectName=travel_questions |
CollectDateStarted |
The timestamp when the Collect was started. |
CollectDateStarted=2018-09-10T10:42:00Z |
CollectStatus |
The status of the Collect |
CollectStatus=in-progress |
ValidateFieldName |
The name of the field to be validated |
ValidateFieldName=trip_start |
ValidateFieldType |
The type of the field to be validated |
ValidateFieldType=Twilio.CITY |
ValidateFieldAnswer |
The value to be validated |
ValidateFieldAnswer=San Francisco |
Expected response
Autopilot expects the following JSON response when the value is valid:
{
"valid" : true
}
Autopilot expects the following JSON response when the value is not valid:
{
"valid" : false
}
Example 5: Multiple questions with validate
{
"actions": [
{
"collect": {
"name": "travel_questions",
"questions": [
{
"question": "Where would you like to start your Trip? New York or San Francisco",
"name": "trip_start",
"type": "Twilio.CITY",
"validate": {
"allowed_values": {
"list": [
"New York",
"San Francisco"
]
},
"on_failure": {
"messages": [
{
"say": "Sorry, that's not a valid start city."
},
{
"say": "Hmm, I'm not understanding. It can be San Francisco or New york."
}
],
"repeat_question": true
},
"on_success": {
"say": "Great, we've got your starting city"
},
"max_attempts": {
"redirect": "task://having-trouble",
"num_attempts": 3
}
}
},
{
"question": "Would you like to be on our January, June or September trip?",
"name": "trip_month",
"type": "Twilio.Month",
"validate": {
"allowed_values": {
"list": [
"January",
"June",
"September"
]
},
"on_failure": {
"messages": [
{
"say": "Sorry, that's valid month"
},
{
"say": "Hmm, I'm not understanding. It can be January, June, and September"
}
],
"repeat_question": true
},
"on_success": {
"say": "Great, we've got your month"
},
"max_attempts": {
"redirect": "task://having-trouble",
"num_attempts": 3
}
}
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
Valid answers come in the Memory parameter with a JSON containing:
{
"twilio": {
"collected_data": {
"travel_questions": {
"status": "complete",
"date_started": "2018-09-10T10:42:00Z",
"date_completed": "2018-09-10T10:42:00Z",
"answers": {
"trip_start": {
"type": "Twilio.CITY",
"answer": "San Francisco",
"filled": true,
"attempts": 1,
"confirmed": false
},
"trip_month": {
"answer": "January",
"type": "Twilio.MONTH",
"filled": true,
"attempts": 1,
"confirmed": false
}
}
}
}
}
}
Invalid answers would look like this:
{
"twilio": {
"collected_data": {
"travel_questions": {
"status": "complete",
"date_started": "2018-09-10T10:42:00Z",
"date_completed": "2018-09-10T10:42:00Z",
"answers": {
"trip_start": {
"type": "Twilio.CITY",
"error":{
"error_message":"Invalid Value",
"value":"donuts"
}
,
"filled": true,
"attempts": 1,
"confirmed": false
},
"trip_month": {
"error":{
"error_message":"Invalid Value",
"value":"I habe no idea"
},
"type": "Twilio.MONTH",
"filled": true,
"attempts": 1,
"confirmed": false
}
}
}
}
}
}
Example 6: Single question without validation or optional
{
"actions": [
{
"collect": {
"name": "ask_first_name",
"questions": [
{
"question": "Hi! What's your name",
"name": "first_name",
"type": "Twilio.FIRST_NAME",
"validate": false,
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
Example 7: Single question validation webhook
{
"actions": [
{
"collect": {
"name": "travel_questions",
"questions": [
{
"question": "Where would you like to start your Trip?",
"name": "trip_start",
"type": "Twilio.CITY",
"validate": {
"on_failure": {
"messages": [
{
"say": "Sorry, that's not a valid start city."
},
{
"say": "Hmm, I'm not understanding. It can be San Francisco or New york."
}
],
"repeat_question": true
},
"webhook": {
"url":"https://example.com/validateTripStart",
"method":"POST"
},
"on_success": {
"say": "Great, we've got your starting city"
},
"max_attempts": {
"redirect": "task://say",
"num_attempts": 3
}
}
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
DTMF
Collect
also lets you provide users the option to answer questions with DTMF inputs using the voice_digits
property if they are using your bot on the voice channel. This can be useful if you're building an IVR with Autopilot.
voice_digits has the following properties:
Property name | Description | Optional | Default value | Allowed values |
num_digits | The number of DTMF inputs you expect from the user. | Yes | unlimited | positive integers |
finish_on_key | The key the user should press to submit their input. If not provided, Autopilot will automatically move to the next Action after 5 seconds | Yes | # | 0-9, #, *, and '' (the empty string) |
mapping | The mapping of DTMF inputs to field values. | If not used, Autopilot will provide the raw DTMF input to your application. | N/A | A JSON object mapping DTMF values to expected inputs. |
Input validation when using mapping
and type
properties at the same time
Autopilot will only use type
to validate speech inputs and not DTMF inputs. If a valid mapping
exists for the DTMF input, it will send back the associated value to your application. If no mapping exists, it will send back the raw DTMF input instead.
Example 7: Collect a yes or no response
{
"actions": [
{
"collect": {
"name": "collect_dtmf_yes_no",
"questions": [
{
"question": "Are you a customer? Press or say 1 if you are and 2 if you are not a customer.",
"name": "customer",
"type": "Twilio.YES_NO",
"voice_digits": {
"num_digits": 1,
"finish_on_key": "#",
"mapping": {
"1": "Yes",
"2": "No"
}
}
}
],
"on_complete": {
"redirect": {
"uri": "https://example.com/collect",
"method": "POST"
}
}
}
}
]
}
Example 8: Collect a number by limiting the number of digits
{
"actions": [
{
"collect": {
"name": "collect_zip_5_digits",
"questions": [
{
"question": "Hi! Please say or type your zip code.",
"name": "zip_code",
"type": "Twilio.NUMBER",
"voice_digits": {
"num_digits": 5
}
}
],
"on_complete": {
"redirect": {
"uri": "https://example.com/collect",
"method": "POST"
}
}
}
}
]
}
Example 9: Collect a number with the finishOnKey
{
"actions": [
{
"collect": {
"name": "collect_zip_finish_on_key",
"questions": [
{
"question": "Hi! Please say or type in your zip code.",
"name": "zip_code",
"type": "Twilio.NUMBER",
"voice_digits": {
"finish_on_key": "#"
}
}
],
"on_complete": {
"redirect": {
"uri": "https://example.com/collect",
"method": "POST"
}
}
}
}
]
}
Prefill
Collect's Prefill feature intelligently skips questions asking for information the user has already provided in the utterance invoking the task. Prefill can be enabled separately on each question by setting the prefill
attribute to the Field Name expected. Note that the Task being triggered should be trained with adequate samples containing the Field Names for Prefill to work.
Example 10: Prefill
{
"actions": [
{
"collect": {
"name": "book_appointment",
"questions": [
{
"question": "What date?",
"name": "appt_date",
"type": "Twilio.DATE",
"prefill": "Date"
},
{
"question":"What time?",
"name": "appt_time",
"type": "Twilio.TIME",
"prefill": "Time"
}
],
"on_complete": {
"redirect": "https://example.com/collect"
}
}
}
]
}
Scenarios
1. I would like to book an appointment on {Date} at {Time}
Since both Fields are present in the initial utterance, Collect will skip both questions and move to on_complete
.
2. I would like to book an appointment on {Date}.
Collect will ask the first question but skip the second question before moving to on_complete
.
3. I would like to book an appointment.
Collect will ask both questions before moving to on_complete
.
Confirm
Collect's Confirm feature simplifies programming your bot to confirm the answers it receives from the user during a question answer sequence. Confirm can be enabled separately on each question using the confirm
attribute as shown in Example 11 below. The name of the question that requires confirmation must be included in { } in the confirmation messages, and Autopilot will loop through the confirmation messages provided. If the user responds with yes, yeah, sure or any other synonym, Autopilot will proceed to the next question. If the user responds with no or one of its synonyms, Autopilot will pick a response from the reject
attribute and repeat the question. If the user does not confirm their answer after max_attempts
, Autopilot will redirect to the task specified in max_attempts
.
{
"actions": [
{
"say": "Okay lets schedule a viewing for you. I just need you to answer a few questions."
},
{
"collect": {
"name": "schedule_open_house",
"questions": [
{
"question": "What date do you want to come visit?",
"prefill": "schedule_open_house_date",
"name": "schedule_open_house_date",
"type": "Twilio.DATE",
"confirm": {
"messages": [
{
"say": "I heard you want to come in on {schedule_open_house_date}. Is that correct?"
},
{
"say": "Did you mean to say you want to come in on {schedule_open_house_date}?"
}
],
"on_confirm": {
"say": "Perfect, let's continue"
},
"on_reject": {
"messages": [
{
"say": "I'm sorry, let's try again. When do you want to come in?"
},
{
"say": "My bad, can you tell me again when you want to come in?"
}
]
},
"max_attempts": {
"num_attempts": 2,
"redirect": "task://fallback"
}
}
},
{
"question": "Thanks, what time would you like to come in?",
"prefill": "schedule_open_house_time",
"name": "schedule_open_house_time",
"type": "Twilio.TIME"
},
{
"question": "Thanks, what's the name of the person attending?",
"prefill": "schedule_open_house_name",
"name": "schedule_open_house_name",
"type": "Twilio.FIRST_NAME"
},
{
"question": "Awesome, last question. What is the best number to reach you at?",
"prefill": "schedule_open_house_number",
"name": "schedule_open_house_number",
"type": "Twilio.PHONE_NUMBER"
}
],
"on_complete": {
"redirect": "task://complete_apt_scheduling"
}
}
}
]
}
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.