Skip to main content
This page covers the XML elements for call routing: connecting to other parties, transferring call flow, ending calls, and pausing execution.

Dial

The <Dial> element connects the current call to another phone number, SIP endpoint, or Plivo user. When the dialed party answers, both parties are connected. When either party hangs up, the connection ends.

Basic Usage

<Response>
    <Dial>
        <Number>+14155551234</Number>
    </Dial>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
dial = plivoxml.DialElement()
dial.add(plivoxml.NumberElement('+14155551234'))
response.add(dial)
print(response.to_string())

Dial Attributes

AttributeTypeDefaultDescription
actionURL-URL to receive dial completion status
methodstringPOSTHTTP method for action URL (GET, POST)
timeoutinteger-Seconds to wait for answer
timeLimitinteger14400Maximum call duration in seconds
callerIdstringcaller’s IDCaller ID to display
callerNamestringcaller’s nameCaller name (max 50 chars)
hangupOnStarbooleanfalseLet caller hang up B-leg by pressing *
redirectbooleantrueRedirect to action URL when complete

Callback Attributes

AttributeTypeDescription
callbackUrlURLURL for real-time dial events
callbackMethodstringHTTP method for callback (GET, POST)
confirmSoundURLURL returning XML to play when B-leg answers
confirmKeystringKey B-leg must press to accept call
confirmTimeoutintegerSeconds to wait for confirm key
dialMusicURLURL returning XML for ringback, or real
digitsMatchstringDTMF patterns to report (A-leg)
digitsMatchBLegstringDTMF patterns to report (B-leg)
sipHeadersstringCustom SIP headers (key=value,key2=value2)

Nested Elements

<Dial> must contain at least one nested element:

Number Element

Dial a phone number:
<Dial>
    <Number>+14155551234</Number>
</Dial>
Number Attributes:
AttributeTypeDefaultDescription
sendDigitsstring-DTMF digits to send after answer. Use w for 0.5s pause
sendOnPreanswerbooleanfalseSend digits during early media

User Element

Dial a SIP endpoint:
<Dial>
    <User>sip:[email protected]</User>
</Dial>

Simultaneous Dialing

Ring multiple numbers at once. First to answer is connected:
<Response>
    <Dial>
        <Number>+14155551111</Number>
        <Number>+14155552222</Number>
        <Number>+14155553333</Number>
    </Dial>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
dial = plivoxml.DialElement()
dial.add(plivoxml.NumberElement('+14155551111'))
dial.add(plivoxml.NumberElement('+14155552222'))
dial.add(plivoxml.NumberElement('+14155553333'))
response.add(dial)
print(response.to_string())

Sequential Dialing

Try numbers one at a time with timeouts:
<Response>
    <Dial timeout="15" action="/dial-status/">
        <Number>+14155551111</Number>
    </Dial>
    <Dial timeout="15" action="/dial-status/">
        <Number>+14155552222</Number>
    </Dial>
    <Speak>Sorry, no one is available. Please try again later.</Speak>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()

# First attempt
dial1 = plivoxml.DialElement(timeout=15, action='/dial-status/')
dial1.add(plivoxml.NumberElement('+14155551111'))
response.add(dial1)

# Second attempt
dial2 = plivoxml.DialElement(timeout=15, action='/dial-status/')
dial2.add(plivoxml.NumberElement('+14155552222'))
response.add(dial2)

response.add(plivoxml.SpeakElement('Sorry, no one is available.'))
print(response.to_string())

Dial with Confirmation

Require the called party to press a key to accept:
<Response>
    <Dial confirmSound="https://example.com/confirm.xml" confirmKey="1">
        <Number>+14155551234</Number>
    </Dial>
</Response>
The confirmSound URL should return XML like:
<Response>
    <Speak>Press 1 to accept this call.</Speak>
</Response>

Custom Caller ID

<Response>
    <Dial callerId="+14155559999" callerName="Support Team">
        <Number>+14155551234</Number>
    </Dial>
</Response>

Custom Ringback

Play custom audio instead of the standard ring:
<Response>
    <Dial dialMusic="https://example.com/ringback.xml">
        <Number>+14155551234</Number>
    </Dial>
</Response>
Use dialMusic="real" to play the actual ringtone from the carrier.

Dial Extensions

Send DTMF tones after the call connects (useful for extensions):
<Response>
    <Dial>
        <Number sendDigits="wwww1234">+14155551234</Number>
    </Dial>
</Response>
Each w adds a 0.5-second pause. This example waits 2 seconds, then dials extension 1234.

Dial Action URL Parameters

When the dial completes, these parameters are sent to the action URL:
ParameterDescription
DialStatuscompleted, busy, failed, cancel, timeout, no-answer
DialRingStatustrue or false - whether the call rang
DialHangupCauseStandard telephony hangup cause
DialALegUUIDCall UUID of the A-leg (original caller)
DialBLegUUIDCall UUID of the B-leg (empty if not answered)

Dial Callback URL Parameters

Real-time events sent to callbackUrl:
ParameterDescription
DialActionanswer, connected, hangup, digits
DialBLegStatusB-leg status
DialALegUUIDA-leg call UUID
DialBLegUUIDB-leg call UUID
DialBLegDurationCall duration (on hangup)
DialBLegBillDurationBilled duration (on hangup)
DialBLegFromB-leg caller number
DialBLegToB-leg destination
DialDigitsMatchMatched DTMF digits
DialDigitsPressedByALeg or BLeg
DialBLegHangupCauseNameHangup reason
DialBLegHangupCauseCodeHangup code
DialBLegHangupSourceWho hung up
STIRVerificationSTIR/SHAKEN attestation

Dial to SIP

<Response>
    <Dial>
        <User>sip:[email protected]</User>
    </Dial>
</Response>

Redirect

The <Redirect> element transfers call execution to a different URL. Plivo fetches new XML instructions from the specified URL and continues the call.

Basic Usage

<Response>
    <Redirect>https://example.com/new-flow/</Redirect>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
response.add(plivoxml.RedirectElement('https://example.com/new-flow/'))
print(response.to_string())

Redirect Attributes

AttributeTypeDefaultDescription
methodstringPOSTHTTP method to use (GET, POST)

Dynamic Routing

Redirect based on conditions:
@app.route('/route-call/', methods=['POST'])
def route_call():
    caller = request.form.get('From')
    response = plivoxml.ResponseElement()

    # VIP callers get priority queue
    if is_vip(caller):
        response.add(plivoxml.RedirectElement('https://example.com/vip-queue/'))
    else:
        response.add(plivoxml.RedirectElement('https://example.com/standard-queue/'))

    return Response(response.to_string(), mimetype='application/xml')

After Dial Failure

Redirect when a dial attempt fails:
<Response>
    <Dial timeout="20">
        <Number>+14155551234</Number>
    </Dial>
    <Redirect>https://example.com/voicemail/</Redirect>
</Response>
If the dial fails or times out, the call redirects to voicemail.

Conditional IVR Flow

<Response>
    <GetDigits action="https://example.com/ivr-choice/" numDigits="1">
        <Speak>Press 1 for English, press 2 for Spanish.</Speak>
    </GetDigits>
    <Redirect>https://example.com/ivr-timeout/</Redirect>
</Response>
Create a menu that returns to itself:
@app.route('/main-menu/', methods=['POST'])
def main_menu():
    digits = request.form.get('Digits', '')
    response = plivoxml.ResponseElement()

    if digits == '1':
        response.add(plivoxml.RedirectElement('https://example.com/sales/'))
    elif digits == '2':
        response.add(plivoxml.RedirectElement('https://example.com/support/'))
    elif digits == '9':
        # Repeat menu
        getdigits = plivoxml.GetDigitsElement(
            action='https://example.com/main-menu/',
            numDigits=1
        )
        getdigits.add(plivoxml.SpeakElement('Press 1 for sales, 2 for support, 9 to repeat.'))
        response.add(getdigits)
        response.add(plivoxml.RedirectElement('https://example.com/main-menu/'))
    else:
        response.add(plivoxml.SpeakElement('Invalid option.'))
        response.add(plivoxml.RedirectElement('https://example.com/main-menu/'))

    return Response(response.to_string(), mimetype='application/xml')

Redirect with GET Method

<Response>
    <Redirect method="GET">https://example.com/next-step/?lang=en</Redirect>
</Response>

Redirect Request Parameters

When Plivo calls the redirect URL, it includes all standard request parameters:
ParameterDescription
CallUUIDUnique call identifier
FromCaller’s number
ToCalled number
CallStatusCurrent call status
Directioninbound or outbound

Redirect Best Practices

  1. Avoid infinite loops - Ensure redirects eventually lead to an endpoint that doesn’t redirect
  2. Handle errors - Your redirect URL should always return valid XML
  3. Use HTTPS - All URLs should use HTTPS
  4. Pass context - Use query parameters to pass state between endpoints

Hangup

The <Hangup> element terminates the current call. Use it to gracefully end calls after completing a flow.

Basic Usage

<Response>
    <Speak>Thank you for calling. Goodbye!</Speak>
    <Hangup/>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
response.add(plivoxml.SpeakElement('Thank you for calling. Goodbye!'))
response.add(plivoxml.HangupElement())
print(response.to_string())

Hangup Attributes

AttributeTypeDefaultDescription
reasonstring-Hangup reason: rejected, busy
scheduleinteger-Seconds to wait before hanging up

Scheduled Hangup

End the call after a delay:
<Response>
    <Speak>This call will end in 60 seconds.</Speak>
    <Hangup schedule="60"/>
    <Play loop="0">https://example.com/hold-music.mp3</Play>
</Response>
This schedules a hangup while continuing to execute subsequent elements.

Reject with Reason

Provide a hangup reason to simulate different call states:
<Response>
    <Hangup reason="busy"/>
</Response>
ReasonEffect
rejectedCaller hears rejection tone
busyCaller hears busy signal

After IVR Timeout

<Response>
    <GetDigits action="/handle-input/" numDigits="1" timeout="10" retries="2">
        <Speak>Press 1 to continue.</Speak>
    </GetDigits>
    <Speak>We didn't receive any input. Goodbye.</Speak>
    <Hangup/>
</Response>

After Business Hours

@app.route('/answer/', methods=['POST'])
def answer():
    response = plivoxml.ResponseElement()

    if not is_business_hours():
        response.add(plivoxml.SpeakElement(
            'Our office is currently closed. Please call back during business hours.'
        ))
        response.add(plivoxml.HangupElement())
    else:
        response.add(plivoxml.SpeakElement('Welcome! Please hold.'))
        dial = plivoxml.DialElement()
        dial.add(plivoxml.NumberElement('+14155551234'))
        response.add(dial)

    return Response(response.to_string(), mimetype='application/xml')

Block Spam Callers

@app.route('/answer/', methods=['POST'])
def answer():
    caller = request.form.get('From')
    response = plivoxml.ResponseElement()

    if is_blocked(caller):
        response.add(plivoxml.HangupElement(reason='rejected'))
    else:
        response.add(plivoxml.SpeakElement('Hello! How can I help you?'))
        # Continue with normal flow

    return Response(response.to_string(), mimetype='application/xml')

Implicit Hangup

If your XML doesn’t end with <Hangup>, the call automatically ends when all elements are executed. However, it’s good practice to include it explicitly for clarity.

Wait

The <Wait> element pauses call execution for a specified duration. Use it for hold times, delays, or with answering machine detection.

Basic Usage

<Response>
    <Speak>Please hold while we connect you.</Speak>
    <Wait length="5"/>
    <Speak>Thank you for waiting.</Speak>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
response.add(plivoxml.SpeakElement('Please hold while we connect you.'))
response.add(plivoxml.WaitElement(length=5))
response.add(plivoxml.SpeakElement('Thank you for waiting.'))
print(response.to_string())

Wait Attributes

AttributeTypeDefaultDescription
lengthinteger1Seconds to wait
silencebooleanfalsePlay silence (vs default hold music)
minSilenceinteger-Minimum silence milliseconds to detect
beepstring-Detect beeps: true or beep parameters

Silent Wait

By default, <Wait> plays hold music. For silence:
<Response>
    <Wait length="10" silence="true"/>
</Response>

Delayed Call Answer

Use <Wait> to delay answering (useful for screening):
<Response>
    <Wait length="3"/>
    <Speak>Hello, you've reached Acme Corp.</Speak>
</Response>

Answering Machine Detection

Detect answering machines by listening for beeps:
<Response>
    <Wait length="10" beep="true"/>
    <Speak>Hello, this is an automated message from Acme Corp.</Speak>
</Response>

Beep Detection Parameters

For fine-grained control, pass beep parameters as a comma-separated string:
<Wait beep="duration=300,inter_silence=50,intra_silence=500,threshold=256"/>
ParameterDefaultDescription
duration300Beep duration in ms to match
inter_silence50Silence between beeps (ms)
intra_silence500Silence after beep to confirm (ms)
threshold256Audio level threshold

Silence Detection

Detect when the other party stops speaking:
<Response>
    <Wait length="30" silence="true" minSilence="1000"/>
    <Speak>It seems like you're done speaking.</Speak>
</Response>
minSilence is the minimum silence duration in milliseconds to trigger detection.

Machine Detection Flow

Combine with <Speak> for voicemail drops:
<Response>
    <Wait length="5" beep="true"/>
    <Speak>
        Hello, this is a reminder from Dr. Smith's office
        about your appointment tomorrow at 2 PM.
        Please call us at 555-1234 to confirm.
    </Speak>
</Response>

Use in PreAnswer

Delay before answering the call:
<Response>
    <PreAnswer>
        <Wait length="2"/>
        <Play>https://example.com/ring.mp3</Play>
    </PreAnswer>
    <Speak>Hello, thank you for calling.</Speak>
</Response>

PreAnswer

The <PreAnswer> element plays audio to the caller before the call is answered. This is useful for custom ringback tones or screening calls. The caller is not billed during this phase.

Basic Usage

<Response>
    <PreAnswer>
        <Speak>Please wait while we connect your call.</Speak>
    </PreAnswer>
    <Dial>
        <Number>+14155551234</Number>
    </Dial>
</Response>
from plivo import plivoxml

response = plivoxml.ResponseElement()
preanswer = plivoxml.PreAnswerElement()
preanswer.add(plivoxml.SpeakElement('Please wait while we connect your call.'))
response.add(preanswer)
dial = plivoxml.DialElement()
dial.add(plivoxml.NumberElement('+14155551234'))
response.add(dial)
print(response.to_string())

Nested Elements

<PreAnswer> can contain:
  • <Speak> - Text-to-speech messages
  • <Play> - Audio files
  • <Wait> - Pauses

Custom Ringback

Play music while connecting:
<Response>
    <PreAnswer>
        <Play loop="0">https://example.com/custom-ringback.mp3</Play>
    </PreAnswer>
    <Dial>
        <Number>+14155551234</Number>
    </Dial>
</Response>

Use Cases

Use CaseDescription
Custom ringbackReplace standard ring with music or branding
Legal notices”This call may be recorded” disclaimers
Spam screeningDelay answering to deter robocalls
Queue position”You are caller number 3” before answering

Limitations

  • Only Speak, Play, and Wait elements are allowed
  • Call is not “answered” during PreAnswer, so some carriers may timeout
  • Recommended to keep PreAnswer duration under 30 seconds