Twilio Voice Calls — Making and Receiving Phone Calls Programmatically
In this tutorial, you will learn about Twilio Voice Calls. We cover key concepts, practical examples, and best practices to help you master this topic.
Twilio Programmable Voice allows you to make and receive phone calls programmatically, with TwiML controlling call flow including text-to-speech, input gathering, call recording, and conferencing.
What You'll Learn
- How to make outgoing calls with Twilio
- How to use TwiML for call control
- How to gather user input during calls
Why It Matters
Phone calls are essential for: automated appointment reminders, verification calls (when SMS fails), interactive voice response (IVR) systems, and outbound sales automation. Twilio Voice handles the telephony infrastructure so you can focus on the call logic.
Real-World Use
DodaTech uses Twilio Voice for: phone-based 2FA (fallback when SMS is delayed), appointment reminder calls with text-to-speech, outbound notifications about account suspensions, and an IVR system for customer support routing.
from twilio.rest import Client
from twilio.twiml.voice_response import VoiceResponse, Say, Gather
client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
def make_verification_call(to_number, code):
"""Make an automated call that reads a verification code"""
response = VoiceResponse()
response.say("Your DodaTech verification code is:", voice='alice')
response.say(' '.join(code), voice='alice') # Reads digits individually
response.say("Repeat: " + ' '.join(code), voice='alice')
response.hangup()
call = client.calls.create(
twiml=str(response),
to=to_number,
from_='+15551234567'
)
print(f"Call initiated: {call.sid}")
return call.sid
Interactive Voice Response
@app.route('/twilio/voice/ivr', methods=['POST'])
def ivr_main_menu():
response = VoiceResponse()
gather = Gather(
num_digits=1,
action='/twilio/voice/handle-menu',
timeout=5
)
gather.say(
"Welcome to DodaTech support. "
"Press 1 for account support. "
"Press 2 for technical support. "
"Press 3 to speak to a representative.",
voice='alice'
)
response.append(gather)
# If no input, try again
response.say("Sorry, I didn't receive any input. Goodbye.")
response.hangup()
return Response(str(response), mimetype='text/xml')
@app.route('/twilio/voice/handle-menu', methods=['POST'])
def handle_menu():
digits = request.form.get('Digits', '')
response = VoiceResponse()
if digits == '1':
response.say("Redirecting to account support.")
response.dial('+15559876543') # Forward to agent
elif digits == '2':
response.say("Redirecting to technical support.")
response.dial('+15558765432') # Forward to tech support
elif digits == '3':
response.say("Please hold while we connect you.")
response.dial('+15557654321') # Forward to operator
else:
response.say("Invalid option.")
response.redirect('/twilio/voice/ivr')
return Response(str(response), mimetype='text/xml')
Call Recording
def make_recorded_call(to_number, message):
"""Make a call with recording enabled"""
response = VoiceResponse()
response.say(message, voice='alice')
response.record(
action='/twilio/voice/recording-complete',
method='POST',
max_length=30,
play_beep=True
)
response.hangup()
call = client.calls.create(
twiml=str(response),
to=to_number,
from_='+15551234567',
record=True,
recording_status_callback='/twilio/voice/recording-status'
)
return call.sid
@app.route('/twilio/voice/recording-status', methods=['POST'])
def recording_status():
recording_sid = request.form.get('RecordingSid')
recording_url = request.form.get('RecordingUrl')
call_sid = request.form.get('CallSid')
duration = request.form.get('RecordingDuration')
database.store_recording(call_sid, recording_sid, recording_url, duration)
return '', 200
Common Mistakes
1. Not Handling Call Failure Webhooks
Calls can fail for many reasons (busy, no answer, invalid number). Always set a status_callback URL to handle failures.
2. Forgetting to Set a Timeout
Without a timeout, Twilio rings indefinitely (up to the carrier limit). Set timeout=30 seconds for reasonable ringing.
3. Using Incorrect TwiML Verbs
Use Say for text-to-speech, Play for audio files, Gather for input collection, Dial for forwarding. Each serves a different purpose.
4. Not Testing with Multiple Devices
Call behavior differs between landlines, mobile, and VoIP. Test all target device types.
5. Ignoring Call Status Values
Calls transition through queued, ringing, in-progress, completed, busy, failed, no-answer. Handle each status appropriately.
Practice Questions
- What TwiML verb reads text aloud during a call?
- How do you collect DTMF input from the caller?
- How do you record a call?
- What status indicates a successful call?
- How do you forward a call to an agent?
Answers
<Say>. 2.<Gather>with num_digits and action URL. 3. Set record=True and recording_status_callback. 4.completed. 5. Use<Dial>with the agent's phone number.
Challenge
Build an IVR system with: a main menu with 3 options (account, technical, operator), input collection with timeout and retry, call forwarding to appropriate departments, call recording for quality assurance, and status callbacks for logging.
FAQ
Mini Project
Build a complete phone verification and IVR system: user requests phone verification, Twilio calls the user, reads a verification code using text-to-speech, user presses 1 to confirm, the system marks the phone as verified, and offers a menu for additional options.
What's Next
- Learn about Twilio Conference for multi-party calls
- Explore Twilio Conversations API for multi-channel chat
- Continue to Twilio Functions for Serverless logic
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro