TwiML™ for Programmable Voice

What is TwiML?

TwiML is a set of instructions you can use to tell Twilio what to do when you receive an incoming call, SMS, or fax.

When someone makes a call to one of your Twilio numbers, Twilio looks up the URL associated with that phone number and makes a request to that URL. Twilio then reads TwiML instructions at that URL to determine what to do: record the call, play a message for the caller, prompt the caller to press digits on their keypad, etc.

For example, the following will say a short message, and then record the caller's voice:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="woman">Please leave a message after the tone.</Say>
    <Record maxLength="20" />
</Response>

TwiML is similar to HTML. Only one TwiML document is rendered to the caller at once, but many documents can be linked together to build complex interactive voice applications.

Calls from a Twilio number to an outside number are controlled using TwiML in the same manner as incoming calls. The initial URL for the call is provided as a parameter to the Twilio REST API request you make to initiate the call.

The TwiML verbs for Programmable Voice

You can use different combinations of the verbs below to create all kinds of interactive voice applications.

  • Say - Read text to the caller
  • Play - Play an audio file for the caller
  • Dial - Add another party to the call
  • Record - Record the caller's voice
  • Gather - Collect digits the caller types on their keypad
  • Hangup - Hang up the call
  • Enqueue - Add the caller to a queue of callers.
  • Leave - Remove a caller from a queue of callers.
  • Redirect - Redirect call flow to a different TwiML document.
  • Pause - Wait before executing more instructions
  • Reject - Decline an incoming call without being billed.

Twilio's request to your application

Twilio makes HTTP requests to your application just like a regular web browser, in the format application/x-www-form-urlencoded. By including parameters and values in its requests, Twilio sends data to your application that you can act upon before responding.

Twilio cannot cache POSTs. If you want Twilio to cache static TwiML pages, then have Twilio make requests to your application using GET.

When Twilio receives a call for one of your Twilio numbers it makes a synchronous HTTP request to the Voice URL configured for that number, and expects to receive TwiML in response.

Twilio sends the following parameters with its request as POST parameters or URL query parameters, depending on which HTTP method you've configured:

Request parameters

PARAMETER DESCRIPTION
CallSid A unique identifier for this call, generated by Twilio.
AccountSid Your Twilio account id. It is 34 characters long, and always starts with the letters AC.
From The phone number or client identifier of the party that initiated the call. Phone numbers are formatted with a '+' and country code, e.g. +16175551212 (E.164 format). Client identifiers begin with the client: URI scheme; for example, for a call from a client named 'tommy', the From parameter will be client:tommy.
To The phone number or client identifier of the called party. Phone numbers are formatted with a '+' and country code, e.g. +16175551212(E.164 format). Client identifiers begin with the client: URI scheme; for example, for a call to a client named 'joey', the To parameter will be client:joey.
CallStatus A descriptive status for the call. The value is one of queuedringingin-progresscompletedbusyfailed or no-answer. See the CallStatus section below for more details.
ApiVersion The version of the Twilio API used to handle this call. For incoming calls, this is determined by the API version set on the called number. For outgoing calls, this is the API version used by the outgoing call's REST API request.
Direction A string describing the direction of the call. inbound for inbound calls, outbound-api for calls initiated via the REST API or outbound-dial for calls initiated by a <Dial> verb.
ForwardedFrom This parameter is set only when Twilio receives a forwarded call, but its value depends on the caller's carrier including information when forwarding. Not all carriers support passing this information.
CallerName This parameter is set when the IncomingPhoneNumber that received the call has had its VoiceCallerIdLookup value set to true ($0.01 per look up).
ParentCallSid A unique identifier for the call that created this leg. This parameter is not passed if this is the first leg of a call.

Twilio also attempts to look up geographic data based on the 'To' and 'From' phone numbers. The following parameters are sent, if available:

PARAMETER DESCRIPTION
FromCity The city of the caller.
FromState The state or province of the caller.
FromZip The postal code of the caller.
FromCountry The country of the caller.
ToCity The city of the called party.
ToState The state or province of the called party.
ToZip The postal code of the called party.
ToCountry The country of the called party.

Depending on what is happening on a call, other variables may also be sent.

When SIP is received by Twilio, the following parameters are also sent to your web application.

PARAMETER NAME DESCRIPTION
SipDomain The Twilio SIP Domain to which the INVITE was sent
SipUsername The username given when authenticating the request, if Credential List is the authentication method.
SipCallId The Call-Id of the incoming INVITE
SipSourceIp The IP Address the incoming INVITE came from.
SipHeader_<name> X- headers in the incoming INVITE are passed as SipHeader_<name> params, where <name> is the header key. You can receive multiple of these.

CallStatus values

The following are the possible values for the 'CallStatus' parameter. These values also apply to the 'DialCallStatus' parameter, which is sent with requests to a <Dial> Verb's action URL.

STATUS DESCRIPTION
queued The call is ready and waiting in line before going out.
ringing The call is currently ringing.
in-progress The call was answered and is currently in progress.
completed The call was answered and has ended normally.
busy The caller received a busy signal.
failed The call could not be completed as dialed, most likely because the phone number was non-existent.
no-answer The call ended without being answered.
canceled The call was canceled via the REST API while queued or ringing.

Call end callback (StatusCallback) requests

After receiving a call, requesting TwiML from your app, processing it, and finally ending the call, Twilio will make an asynchronous HTTP request to the StatusCallback URL configured for the called Twilio number (if there is one).

By providing a StatusCallback URL for your Twilio number and capturing this request you can determine when a call ends and receive information about the call. Non-relative URLs must contain a valid hostname (underscores are not permitted).

Request parameters

The parameters Twilio passes to your application in an asynchronous request to the StatusCallback URL include all those passed in a synchronous TwiML request.

The Status Callback request also passes these additional parameters:

PARAMETER DESCRIPTION
CallDuration The duration in seconds of the just-completed call.
RecordingUrl The URL of the phone call's recorded audio. This parameter is included only if Record=true is set on the REST API request, and does not include recordings from <Dial> or <Record>.
RecordingSid The unique id of the Recording from this call.
RecordingDuration The duration of the recorded audio (in seconds).

Data formats

Phone numbers

All phone numbers in requests from Twilio are in E.164 format if possible. For example, (415) 555-4345 would come through as '+14155554345'. However, there are occasionally cases where Twilio cannot normalize an incoming caller ID to E.164. In these situations Twilio will report the raw caller ID string.

Dates and times

All dates and times in requests from Twilio are GMT in RFC 2822 format. For example, 6:13 PM PDT on August 19th, 2010 would be 'Fri, 20 Aug 2010 01:13:42 +0000'

Responding to Twilio

When a phone call comes in to one of your Twilio numbers, Twilio makes an HTTP request to the URL configured for that number. In your response to that request, you can tell Twilio what to do on the call.

Twilio is a well-behaved HTTP client

Twilio behaves just like a web browser when making HTTP requests to URLs:

  • Cookies: Twilio accepts HTTP cookies and will include them in each request, just like a normal web browser.
  • Redirects: Twilio follows HTTP Redirects (HTTP status codes 301, 307, etc.), just like a normal web browser.
  • Caching: Twilio will cache files when HTTP headers allow it (via ETag and Last-Modified headers) and when the HTTP method is GET, just like a normal web browser.

Twilio understands MIME types

Twilio does the right thing when your application responds with different MIME types:

MIME Type Behavior
text/xml, application/xml, text/html Twilio interprets the returned document as an XML Instruction Set (which we like to call TwiML). This is the most commonly used response.
various audio types Twilio plays the audio file to the caller, and then hangs up. See the <Play> documentation for supported MIME types.
text/plain Twilio reads the content of the text out loud to the caller, and then hangs up.

How Twilio's TwiML interpreter works

When your application responds to a Twilio request with XML, Twilio runs your document through the TwiML interpreter. To keep things simple, the TwiML interpreter only understands a few specially-named XML elements.

In TwiML parlance, these are divided into three groups: the root <Response> element, "verbs" and "nouns." Each group is discussed below.

The interpreter starts at the top of your TwiML document and executes instructions ("verbs") in order from top to bottom. As an example, the following TwiML snippet reads "Hello World" to the caller before playing Cowbell.mp3 for the caller and then hanging up.

<?xml version="1.0" encoding="UTF-8" ?>  
<Response> 
    <Say>Hello World</Say>
    <Play>https://api.twilio.com/Cowbell.mp3</Play>
</Response>

TwiML elements ("verbs" and "nouns") have case-sensitive names. For example, using <say> instead of <Say> will result in an error. Attribute names are also case sensitive and "camelCased." And you can use XML comments freely; the interpreter ignores them.

The <Response> element

The root element of Twilio's XML Markup is the <Response> element. In any TwiML response to a Twilio request, all verb elements must be nested within this element. Any other structure is considered invalid.

Example:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say>Hello</Say>
</Response>

TwiML verbs

Most XML elements in a TwiML document are TwiML verbs. Verb names are case sensitive, as are their attribute names. The core TwiML verbs for Programmable Voice are:

  • <Say>: Read some text to the caller.
  • <Play>: Play an audio file to the caller.
  • <Record>: Record a call or part of a call.
  • <Gather>: Get the digits a caller presses.
  • <Dial>: Call another phone number or conference and connect the current caller.

The following verbs may impact control flow: <Gather><Record><Dial><Redirect><Hangup> and <Reject>.

There are certain situations when the TwiML interpreter may not reach verbs in a TwiML document because control flow has passed to a different document. This usually happens when a verb's 'action' attribute is set. For example, if a <Say> verb is followed by a <Redirect> and then another <Say>, the 2nd <Say> is unreachable because <Redirect> transfers full control of a call to the TwiML at a different URL.

TwiML nouns

A Noun in TwiML is anything nested inside a verb that is not itself a verb. It's whatever the verb is acting on. This is usually just text. But sometimes, as in the case of <Dial> with its <Number> and <Conference> nouns, there are nested XML elements that are nouns.

Status callbacks

Status callbacks do not control call flow, so TwiML does not need to be returned. If you do respond, use status code 204 No Content or 200 OK with Content-Type: text/xml and an empty <Response/> in the body. Not responding properly will result in warnings in Debugger.

Generating TwiML with the Twilio helper libraries

Twilio provides helper libraries to let you easily generate those TwiML in your favorite language.

Loading Code Samples...
Language
SDK Version:
  • 5.x
SDK Version:
  • 7.x
SDK Version:
  • 3.x
SDK Version:
  • 5.x
SDK Version:
  • 6.x
SDK Version:
  • 5.x
Format:
  • TwiML
const VoiceResponse = require('twilio').twiml.VoiceResponse;

const response = new VoiceResponse();
response.say('Hello World');
response.play('https://api.twilio.com/Cowbell.mp3');

console.log(response.toString());
using Twilio.TwiML;
using System;

class Example
{
    static void Main()
    {
        var response = new VoiceResponse();
        response.Say("Hello World");
        response.Play(new Uri("https://api.twilio.com/Cowbell.mp3"));

        Console.WriteLine(response.ToString());;
    }
}
<?php
require_once './vendor/autoload.php';
use Twilio\Twiml;

$response = new Twiml();
$response->say('Hello World');
$response->play('https://api.twilio.com/Cowbell.mp3');

echo $response;
require 'twilio-ruby'

response = Twilio::TwiML::VoiceResponse.new
response.say('Hello World')
response.play(url: 'https://api.twilio.com/Cowbell.mp3')

puts response
from twilio.twiml.voice_response import Play, VoiceResponse, Say

response = VoiceResponse()
response.say('Hello World')
response.play('https://api.twilio.com/Cowbell.mp3')

print(response)
import com.twilio.twiml.voice.Play;
import com.twilio.twiml.VoiceResponse;
import com.twilio.twiml.voice.Say;
import com.twilio.twiml.TwiMLException;


public class Example {
    public static void main(String[] args) {
        Say say = new Say.Builder("Hello World").build();
        Play play = new Play.Builder("https://api.twilio.com/Cowbell.mp3")
            .build();
        VoiceResponse response = new VoiceResponse.Builder().say(say)
            .play(play).build();

        try {
            System.out.println(response.toXml());
        } catch (TwiMLException e) {
            e.printStackTrace();
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<Response> 
    <Say>Hello World</Say>
    <Play>https://api.twilio.com/Cowbell.mp3</Play>
</Response>
Hello World with music
Loading Code Samples...
Language
SDK Version:
  • 5.x
SDK Version:
  • 7.x
SDK Version:
  • 3.x
SDK Version:
  • 5.x
SDK Version:
  • 6.x
SDK Version:
  • 5.x
Format:
  • TwiML
const VoiceResponse = require('twilio').twiml.VoiceResponse;

const response = new VoiceResponse();
response.say('Hello');

console.log(response.toString());
using Twilio.TwiML;
using System;


class Example
{
    static void Main()
    {
        var response = new VoiceResponse();
        response.Say("Hello");

        Console.WriteLine(response.ToString());;
    }
}
<?php
require_once './vendor/autoload.php';
use Twilio\Twiml;

$response = new Twiml();
$response->say('Hello');

echo $response;
require 'twilio-ruby'

response = Twilio::TwiML::VoiceResponse.new
response.say('Hello')

puts response
from twilio.twiml.voice_response import VoiceResponse, Say

response = VoiceResponse()
response.say('Hello')

print(response)
import com.twilio.twiml.VoiceResponse;
import com.twilio.twiml.voice.Say;
import com.twilio.twiml.TwiMLException;


public class Example {
    public static void main(String[] args) {
        Say say = new Say.Builder("Hello").build();
        VoiceResponse response = new VoiceResponse.Builder().say(say).build();

        try {
            System.out.println(response.toXml());
        } catch (TwiMLException e) {
            e.printStackTrace();
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say>Hello</Say>
</Response>
Simple Hello

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 browsing the Twilio tag on Stack Overflow.

Loading Code Samples...
SDK Version:
  • 5.x
SDK Version:
  • 7.x
SDK Version:
  • 3.x
SDK Version:
  • 5.x
SDK Version:
  • 6.x
SDK Version:
  • 5.x
Format:
  • TwiML
const VoiceResponse = require('twilio').twiml.VoiceResponse;

const response = new VoiceResponse();
response.say('Hello World');
response.play('https://api.twilio.com/Cowbell.mp3');

console.log(response.toString());
using Twilio.TwiML;
using System;

class Example
{
    static void Main()
    {
        var response = new VoiceResponse();
        response.Say("Hello World");
        response.Play(new Uri("https://api.twilio.com/Cowbell.mp3"));

        Console.WriteLine(response.ToString());;
    }
}
<?php
require_once './vendor/autoload.php';
use Twilio\Twiml;

$response = new Twiml();
$response->say('Hello World');
$response->play('https://api.twilio.com/Cowbell.mp3');

echo $response;
require 'twilio-ruby'

response = Twilio::TwiML::VoiceResponse.new
response.say('Hello World')
response.play(url: 'https://api.twilio.com/Cowbell.mp3')

puts response
from twilio.twiml.voice_response import Play, VoiceResponse, Say

response = VoiceResponse()
response.say('Hello World')
response.play('https://api.twilio.com/Cowbell.mp3')

print(response)
import com.twilio.twiml.voice.Play;
import com.twilio.twiml.VoiceResponse;
import com.twilio.twiml.voice.Say;
import com.twilio.twiml.TwiMLException;


public class Example {
    public static void main(String[] args) {
        Say say = new Say.Builder("Hello World").build();
        Play play = new Play.Builder("https://api.twilio.com/Cowbell.mp3")
            .build();
        VoiceResponse response = new VoiceResponse.Builder().say(say)
            .play(play).build();

        try {
            System.out.println(response.toXml());
        } catch (TwiMLException e) {
            e.printStackTrace();
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<Response> 
    <Say>Hello World</Say>
    <Play>https://api.twilio.com/Cowbell.mp3</Play>
</Response>
SDK Version:
  • 5.x
SDK Version:
  • 7.x
SDK Version:
  • 3.x
SDK Version:
  • 5.x
SDK Version:
  • 6.x
SDK Version:
  • 5.x
Format:
  • TwiML
const VoiceResponse = require('twilio').twiml.VoiceResponse;

const response = new VoiceResponse();
response.say('Hello');

console.log(response.toString());
using Twilio.TwiML;
using System;


class Example
{
    static void Main()
    {
        var response = new VoiceResponse();
        response.Say("Hello");

        Console.WriteLine(response.ToString());;
    }
}
<?php
require_once './vendor/autoload.php';
use Twilio\Twiml;

$response = new Twiml();
$response->say('Hello');

echo $response;
require 'twilio-ruby'

response = Twilio::TwiML::VoiceResponse.new
response.say('Hello')

puts response
from twilio.twiml.voice_response import VoiceResponse, Say

response = VoiceResponse()
response.say('Hello')

print(response)
import com.twilio.twiml.VoiceResponse;
import com.twilio.twiml.voice.Say;
import com.twilio.twiml.TwiMLException;


public class Example {
    public static void main(String[] args) {
        Say say = new Say.Builder("Hello").build();
        VoiceResponse response = new VoiceResponse.Builder().say(say).build();

        try {
            System.out.println(response.toXml());
        } catch (TwiMLException e) {
            e.printStackTrace();
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say>Hello</Say>
</Response>