0
# TwiML Generation
1
2
XML markup language generation for controlling voice calls and messaging flows. Provides Python classes that generate TwiML XML for call routing, text-to-speech, user input collection, and message handling.
3
4
## Capabilities
5
6
### Voice Response Generation
7
8
Create TwiML responses for voice calls with support for speech synthesis, call routing, user input collection, and call control.
9
10
```python { .api }
11
class VoiceResponse:
12
"""Main class for generating voice TwiML responses"""
13
14
def __init__(self):
15
"""Initialize a new voice response"""
16
17
def say(
18
self,
19
message: str = None,
20
voice: str = None,
21
language: str = None,
22
loop: int = None
23
) -> Say:
24
"""
25
Text-to-speech synthesis.
26
27
Args:
28
message (str): Text to speak
29
voice (str): Voice to use ('man', 'woman', 'alice')
30
language (str): Language code (e.g., 'en-US', 'es-ES')
31
loop (int): Number of times to repeat
32
33
Returns:
34
Say: Say verb element
35
"""
36
37
def play(
38
self,
39
url: str = None,
40
loop: int = None,
41
digits: str = None
42
) -> Play:
43
"""
44
Play audio file from URL.
45
46
Args:
47
url (str): URL of audio file to play
48
loop (int): Number of times to repeat
49
digits (str): DTMF digits to send during playback
50
51
Returns:
52
Play: Play verb element
53
"""
54
55
def dial(
56
self,
57
number: str = None,
58
action: str = None,
59
method: str = None,
60
timeout: int = None,
61
hangup_on_star: bool = None,
62
time_limit: int = None,
63
caller_id: str = None,
64
record: str = None,
65
trim: str = None,
66
recording_status_callback: str = None,
67
recording_status_callback_method: str = None,
68
recording_status_callback_event: list = None,
69
answer_on_bridge: bool = None,
70
ring_tone: str = None,
71
recording_track: str = None,
72
sequential: bool = None
73
) -> Dial:
74
"""
75
Dial phone numbers, SIP addresses, or clients.
76
77
Args:
78
number (str): Phone number to dial
79
action (str): Webhook URL after dial completes
80
method (str): HTTP method for action URL
81
timeout (int): Ring timeout in seconds
82
hangup_on_star (bool): Hang up when caller presses *
83
time_limit (int): Maximum call duration in seconds
84
caller_id (str): Caller ID to display
85
record (str): Record the call ('record-from-answer')
86
87
Returns:
88
Dial: Dial verb element
89
"""
90
91
def gather(
92
self,
93
input: str = None,
94
action: str = None,
95
method: str = None,
96
timeout: int = None,
97
finish_on_key: str = None,
98
num_digits: int = None,
99
partial_result_callback: str = None,
100
partial_result_callback_method: str = None,
101
language: str = None,
102
hints: str = None,
103
barge_in: bool = None,
104
debug: bool = None,
105
action_on_empty_result: bool = None,
106
speech_timeout: str = None,
107
enhanced: bool = None,
108
speech_model: str = None,
109
profanity_filter: bool = None
110
) -> Gather:
111
"""
112
Collect user input via DTMF or speech.
113
114
Args:
115
input (str): Input type ('dtmf', 'speech', 'dtmf speech')
116
action (str): Webhook URL to send results
117
method (str): HTTP method for action URL
118
timeout (int): Input timeout in seconds
119
finish_on_key (str): Key to end input (default: #)
120
num_digits (int): Number of digits to collect
121
language (str): Speech recognition language
122
hints (str): Speech recognition hints
123
barge_in (bool): Allow input during nested verbs
124
125
Returns:
126
Gather: Gather verb element
127
"""
128
129
def record(
130
self,
131
action: str = None,
132
method: str = None,
133
timeout: int = None,
134
finish_on_key: str = None,
135
max_length: int = None,
136
play_beep: bool = None,
137
trim: str = None,
138
recording_status_callback: str = None,
139
recording_status_callback_method: str = None,
140
transcribe: bool = None,
141
transcribe_callback: str = None
142
) -> Record:
143
"""
144
Record caller's voice.
145
146
Args:
147
action (str): Webhook URL after recording
148
method (str): HTTP method for action URL
149
timeout (int): Silence timeout in seconds
150
finish_on_key (str): Key to stop recording
151
max_length (int): Maximum recording length in seconds
152
play_beep (bool): Play beep before recording
153
trim (str): Trim silence ('trim-silence')
154
transcribe (bool): Enable transcription
155
156
Returns:
157
Record: Record verb element
158
"""
159
160
def pause(self, length: int = None) -> Pause:
161
"""
162
Pause execution.
163
164
Args:
165
length (int): Pause duration in seconds
166
167
Returns:
168
Pause: Pause verb element
169
"""
170
171
def redirect(
172
self,
173
url: str = None,
174
method: str = None
175
) -> Redirect:
176
"""
177
Redirect to new TwiML URL.
178
179
Args:
180
url (str): New TwiML URL
181
method (str): HTTP method ('GET' or 'POST')
182
183
Returns:
184
Redirect: Redirect verb element
185
"""
186
187
def hangup(self) -> Hangup:
188
"""
189
End the call.
190
191
Returns:
192
Hangup: Hangup verb element
193
"""
194
195
def reject(self, reason: str = None) -> Reject:
196
"""
197
Reject incoming call.
198
199
Args:
200
reason (str): Rejection reason ('rejected', 'busy')
201
202
Returns:
203
Reject: Reject verb element
204
"""
205
206
def enqueue(
207
self,
208
name: str = None,
209
action: str = None,
210
method: str = None,
211
wait_url: str = None,
212
wait_url_method: str = None,
213
workflow_sid: str = None
214
) -> Enqueue:
215
"""
216
Place caller in queue.
217
218
Args:
219
name (str): Queue name
220
action (str): Webhook URL after dequeue
221
wait_url (str): TwiML URL while waiting
222
workflow_sid (str): TaskRouter workflow SID
223
224
Returns:
225
Enqueue: Enqueue verb element
226
"""
227
228
def leave(self) -> Leave:
229
"""
230
Leave current queue.
231
232
Returns:
233
Leave: Leave verb element
234
"""
235
236
def to_xml(self) -> str:
237
"""
238
Generate XML string.
239
240
Returns:
241
str: Complete TwiML XML document
242
"""
243
244
# Dial sub-elements
245
class Number:
246
"""Phone number to dial within <Dial>"""
247
def __init__(
248
self,
249
number: str,
250
send_digits: str = None,
251
url: str = None,
252
method: str = None,
253
status_callback_event: list = None,
254
status_callback: str = None,
255
status_callback_method: str = None,
256
byoc: str = None,
257
machine_detection: str = None,
258
machine_detection_timeout: int = None,
259
amd_status_callback: str = None,
260
amd_status_callback_method: str = None
261
): ...
262
263
class Conference:
264
"""Conference room within <Dial>"""
265
def __init__(
266
self,
267
name: str,
268
muted: bool = None,
269
beep: str = None,
270
start_conference_on_enter: bool = None,
271
end_conference_on_exit: bool = None,
272
wait_url: str = None,
273
wait_method: str = None,
274
max_participants: int = None,
275
record: str = None,
276
region: str = None,
277
whisper: str = None,
278
trim: str = None,
279
status_callback_event: list = None,
280
status_callback: str = None,
281
status_callback_method: str = None,
282
recording_status_callback: str = None,
283
recording_status_callback_method: str = None,
284
recording_status_callback_event: list = None,
285
event_callback_url: str = None,
286
jitter_buffer_size: str = None,
287
coach: str = None,
288
call_sid_to_coach: str = None
289
): ...
290
291
class Queue:
292
"""Queue within <Dial>"""
293
def __init__(
294
self,
295
name: str,
296
url: str = None,
297
method: str = None,
298
reservation_sid: str = None,
299
post_work_activity_sid: str = None
300
): ...
301
302
class Sip:
303
"""SIP address within <Dial>"""
304
def __init__(
305
self,
306
uri: str,
307
username: str = None,
308
password: str = None
309
): ...
310
311
class Client:
312
"""Twilio Client within <Dial>"""
313
def __init__(
314
self,
315
identity: str,
316
url: str = None,
317
method: str = None,
318
status_callback_event: list = None,
319
status_callback: str = None,
320
status_callback_method: str = None
321
): ...
322
```
323
324
Voice TwiML examples:
325
326
```python
327
from twilio.twiml.voice_response import VoiceResponse
328
329
# Basic text-to-speech
330
response = VoiceResponse()
331
response.say("Hello, welcome to our service!")
332
print(response)
333
# Output: <Response><Say>Hello, welcome to our service!</Say></Response>
334
335
# Play audio with loop
336
response = VoiceResponse()
337
response.play("https://example.com/music.mp3", loop=3)
338
339
# Collect DTMF input
340
response = VoiceResponse()
341
gather = response.gather(num_digits=4, action="/process-input", method="POST")
342
gather.say("Please enter your 4-digit PIN followed by the pound key")
343
response.say("We didn't receive any input. Goodbye!")
344
345
# Make outbound call
346
response = VoiceResponse()
347
dial = response.dial("+15559876543", timeout=30, caller_id="+15551234567")
348
349
# Conference call
350
response = VoiceResponse()
351
dial = response.dial()
352
dial.conference("My Conference Room",
353
start_conference_on_enter=True,
354
end_conference_on_exit=True)
355
356
# Record voicemail
357
response = VoiceResponse()
358
response.say("Please leave a message after the beep")
359
response.record(max_length=30,
360
action="/handle-recording",
361
finish_on_key="#")
362
363
# Call screening with gather
364
response = VoiceResponse()
365
gather = response.gather(num_digits=1, action="/screen-response")
366
gather.say("Press 1 to accept this call, press 2 to send to voicemail")
367
response.hangup()
368
369
# Complex call flow
370
response = VoiceResponse()
371
gather = response.gather(input="speech dtmf",
372
action="/handle-input",
373
speech_timeout="auto")
374
gather.say("Say or press a department: Sales, Support, or Billing")
375
response.redirect("/main-menu")
376
```
377
378
### Messaging Response Generation
379
380
Create TwiML responses for SMS/MMS message handling with support for replies and redirects.
381
382
```python { .api }
383
class MessagingResponse:
384
"""Main class for generating messaging TwiML responses"""
385
386
def __init__(self):
387
"""Initialize a new messaging response"""
388
389
def message(
390
self,
391
body: str = None,
392
to: str = None,
393
from_: str = None,
394
action: str = None,
395
method: str = None,
396
status_callback: str = None
397
) -> Message:
398
"""
399
Send reply message.
400
401
Args:
402
body (str): Message text content
403
to (str): Recipient phone number
404
from_ (str): Sender phone number
405
action (str): Webhook URL after sending
406
method (str): HTTP method for action URL
407
status_callback (str): Delivery status webhook
408
409
Returns:
410
Message: Message element
411
"""
412
413
def redirect(
414
self,
415
url: str = None,
416
method: str = None
417
) -> Redirect:
418
"""
419
Redirect to new TwiML URL.
420
421
Args:
422
url (str): New TwiML URL
423
method (str): HTTP method ('GET' or 'POST')
424
425
Returns:
426
Redirect: Redirect element
427
"""
428
429
def to_xml(self) -> str:
430
"""
431
Generate XML string.
432
433
Returns:
434
str: Complete TwiML XML document
435
"""
436
437
class Message:
438
"""Message element for replies"""
439
def __init__(
440
self,
441
body: str = None,
442
to: str = None,
443
from_: str = None,
444
action: str = None,
445
method: str = None,
446
status_callback: str = None
447
): ...
448
449
def body(self, message: str) -> Body:
450
"""Set message body text"""
451
452
def media(self, url: str) -> Media:
453
"""Add media attachment"""
454
455
class Body:
456
"""Message body text"""
457
def __init__(self, message: str): ...
458
459
class Media:
460
"""Media attachment"""
461
def __init__(self, url: str): ...
462
```
463
464
Messaging TwiML examples:
465
466
```python
467
from twilio.twiml.messaging_response import MessagingResponse
468
469
# Simple reply
470
response = MessagingResponse()
471
response.message("Thanks for your message!")
472
print(response)
473
# Output: <Response><Message>Thanks for your message!</Message></Response>
474
475
# Reply with media
476
response = MessagingResponse()
477
message = response.message()
478
message.body("Here's the image you requested:")
479
message.media("https://example.com/image.jpg")
480
481
# Forward to another number
482
response = MessagingResponse()
483
response.message("Message forwarded", to="+15551234567")
484
485
# Auto-reply with redirect
486
response = MessagingResponse()
487
response.message("Processing your request...")
488
response.redirect("/process-message", method="POST")
489
490
# Conditional responses based on webhook data
491
response = MessagingResponse()
492
# This would typically be in a webhook handler
493
incoming_body = "HELP" # From webhook parameters
494
if incoming_body.upper() == "HELP":
495
response.message("Available commands: INFO, SUPPORT, STOP")
496
elif incoming_body.upper() == "STOP":
497
response.message("You have been unsubscribed.")
498
else:
499
response.message("Unknown command. Reply HELP for options.")
500
```
501
502
### TwiML Base Classes
503
504
Base functionality shared across voice and messaging TwiML generation.
505
506
```python { .api }
507
class TwiML:
508
"""Base class for all TwiML responses"""
509
510
def __init__(self):
511
"""Initialize TwiML response"""
512
513
def append(self, twiml: 'TwiML') -> 'TwiML':
514
"""
515
Append another TwiML element.
516
517
Args:
518
twiml (TwiML): Element to append
519
520
Returns:
521
TwiML: Self for chaining
522
"""
523
524
def to_xml(self) -> str:
525
"""
526
Generate XML string representation.
527
528
Returns:
529
str: XML document string
530
"""
531
532
def __str__(self) -> str:
533
"""String representation returns XML"""
534
535
class GenericNode:
536
"""Generic XML element for custom TwiML"""
537
538
def __init__(
539
self,
540
name: str,
541
body: str = None,
542
**kwargs
543
):
544
"""
545
Create custom XML element.
546
547
Args:
548
name (str): XML element name
549
body (str): Element text content
550
**kwargs: Element attributes
551
"""
552
```
553
554
Advanced TwiML usage:
555
556
```python
557
from twilio.twiml.voice_response import VoiceResponse
558
from twilio.twiml import GenericNode
559
560
# Chaining TwiML elements
561
response = VoiceResponse()
562
response.say("Welcome").pause(1).say("Please hold")
563
564
# Using generic nodes for custom elements
565
response = VoiceResponse()
566
custom = GenericNode("CustomElement", "Custom content", attr="value")
567
response.append(custom)
568
569
# Building complex nested structures
570
response = VoiceResponse()
571
gather = response.gather(action="/process")
572
gather.say("Press 1 for sales")
573
gather.pause(1)
574
gather.say("Press 2 for support")
575
response.say("No input received")
576
response.hangup()
577
578
# Multiple dial targets
579
response = VoiceResponse()
580
dial = response.dial()
581
dial.number("+15551111111", status_callback="/call-progress")
582
dial.number("+15552222222", status_callback="/call-progress")
583
dial.client("alice")
584
585
# XML output customization
586
response = VoiceResponse()
587
response.say("Hello")
588
xml_string = response.to_xml()
589
print(xml_string) # Pretty-printed XML
590
```
591
592
### Fax Response Generation
593
594
Create TwiML responses for fax transmission and reception handling.
595
596
```python { .api }
597
class FaxResponse:
598
"""Main class for generating fax TwiML responses"""
599
600
def __init__(self):
601
"""Initialize a new fax response"""
602
603
def receive(
604
self,
605
action: str = None,
606
method: str = None,
607
media_type: str = None,
608
store_media: bool = None,
609
page_size: str = None
610
) -> Receive:
611
"""
612
Receive incoming fax transmission.
613
614
Args:
615
action (str): Webhook URL after fax reception
616
method (str): HTTP method for action URL
617
media_type (str): Media type ('application/pdf', 'image/tiff')
618
store_media (bool): Store received fax media
619
page_size (str): Page size for received fax
620
621
Returns:
622
Receive: Receive verb element
623
"""
624
625
def reject(self, reason: str = None) -> Reject:
626
"""
627
Reject incoming fax.
628
629
Args:
630
reason (str): Rejection reason
631
632
Returns:
633
Reject: Reject verb element
634
"""
635
636
def to_xml(self) -> str:
637
"""
638
Generate XML string.
639
640
Returns:
641
str: Complete TwiML XML document
642
"""
643
644
class Receive:
645
"""Fax receive element"""
646
def __init__(
647
self,
648
action: str = None,
649
method: str = None,
650
media_type: str = None,
651
store_media: bool = None,
652
page_size: str = None
653
): ...
654
```
655
656
Fax TwiML examples:
657
658
```python
659
from twilio.twiml.fax_response import FaxResponse
660
661
# Receive incoming fax
662
response = FaxResponse()
663
response.receive(
664
action="/fax-received",
665
method="POST",
666
media_type="application/pdf",
667
store_media=True
668
)
669
print(response)
670
# Output: <Response><Receive action="/fax-received" method="POST" mediaType="application/pdf" storeMedia="true"/></Response>
671
672
# Reject fax
673
response = FaxResponse()
674
response.reject(reason="busy")
675
print(response)
676
# Output: <Response><Reject reason="busy"/></Response>
677
```
678
679
## TwiML Integration Patterns
680
681
### Webhook Integration
682
683
TwiML responses are typically generated in webhook handlers that respond to Twilio HTTP requests.
684
685
```python
686
from flask import Flask, request
687
from twilio.twiml.voice_response import VoiceResponse
688
689
app = Flask(__name__)
690
691
@app.route("/voice", methods=['GET', 'POST'])
692
def voice_handler():
693
"""Handle incoming voice calls"""
694
response = VoiceResponse()
695
696
# Access Twilio parameters
697
from_number = request.values.get('From')
698
to_number = request.values.get('To')
699
call_sid = request.values.get('CallSid')
700
701
response.say(f"Hello, you called {to_number} from {from_number}")
702
703
return str(response)
704
705
@app.route("/sms", methods=['GET', 'POST'])
706
def sms_handler():
707
"""Handle incoming SMS messages"""
708
from twilio.twiml.messaging_response import MessagingResponse
709
710
response = MessagingResponse()
711
712
# Access message parameters
713
from_number = request.values.get('From')
714
body = request.values.get('Body', '').strip()
715
716
if body.lower() == 'hello':
717
response.message("Hi there! How can I help you?")
718
else:
719
response.message("Thanks for your message!")
720
721
return str(response)
722
```
723
724
### Dynamic TwiML Generation
725
726
Build TwiML responses based on database queries or external APIs.
727
728
```python
729
def create_menu_twiml(menu_options):
730
"""Generate dynamic menu TwiML"""
731
response = VoiceResponse()
732
733
gather = response.gather(num_digits=1, action="/handle-menu")
734
gather.say("Please select from the following options:")
735
736
for i, option in enumerate(menu_options, 1):
737
gather.say(f"Press {i} for {option}")
738
739
response.say("Invalid selection. Please try again.")
740
response.redirect("/main-menu")
741
742
return response
743
744
# Usage
745
menu_items = ["Sales", "Support", "Billing", "Directory"]
746
twiml = create_menu_twiml(menu_items)
747
```