Skip to main content

Handling Calls from Code

In Making and Receiving Phone Calls we learned how to use Compatibility XML bins to control outbound and inbound calls. In particular, we showed how to play a simple audio message.

Handling calls from code gives us more flexibility. Instead of serving a static XML bin, we can use a custom web server to decide the content of the XML depending on the state of the call, on the caller, on the time, etc.

In this guide, we will show how to create a simple phone service which will tell the user the current day, with a variation on weekends.

Setting up the environment

We are going to use a few libraries to make our job easier. In particular, we'll need the SignalWire Compatibility SDK and, depending on the language you're using, we'll also need a web server. Let's install them:

pip install flask signalwire

Producing XML

We'd like to set up an endpoint (our own "dynamic" XML bin) which emits an XML document, like this one:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>Welcome! Today is Friday.</Say>
</Response>

The XML will be sent back to the client as a response to their request.

In principle, we could manually create such XML by means of string concatenation or interpolation. However, if you are using one of our supported languages, you can use our libraries to more easily produce the same XML:

from twilio.twiml.voice_response import VoiceResponse

response = VoiceResponse()
response.say('Welcome! Today is Friday.')

# Prints the XML string.
print(response.to_xml())

Serving the XML

Now that we know how to generate a proper Compatibility XML document, let's see how to set up a web server to serve those documents.

Setting up a web server

How you set up the web server is mostly specific to the language and framework of your choosing. A basic skeleton might be the following:

main.py
from flask import Flask, request, Response
from twilio.twiml.voice_response import VoiceResponse

app = Flask(__name__)

@app.route("/", methods=['GET', 'POST'])
def call_handler():
"""
This endpoint will be queried by SignalWire whenever an incoming call is received.
We respond with an XML document which specifies the next instructions.
"""
print(request.values)
# The logic goes here
return

In the next section we will configure a phone number to make an HTTP request to our call handler endpoint when an incoming call is received. The HTTP request will contain parameters including values such as the source number or the body of the incoming message.

For our simple phone service demo, our call handler needs to reference the current date and time, as well as the phone number of the caller. Our call handler will emit a modified cXML document based on those values, allowing our application to modify the call flow and content based on server logic.

main.py
from flask import Flask, request, Response
from twilio.twiml.voice_response import VoiceResponse
from datetime import datetime

app = Flask(__name__)

@app.route("/", methods=['GET', 'POST'])
def call_handler():
"""
This endpoint will be queried by SignalWire whenever an incoming call is received.
We respond with an XML document which specifies the next instructions.
"""
print(request.values)

# Get the caller's number
caller = request.values.get('From')

now = datetime.now()

response = VoiceResponse()
response.say(f"Welcome! Today is {now.strftime('%A')}.")
response.say(f"Your phone number ends in {caller[-2:]}.")
if now.weekday() >= 5:
response.say(f"Enjoy the weekend.")

return Response(response.to_xml(), mimetype='text/xml')

This simple message handler works like a dynamic XML bin, whose content depends on the received message.

Try running the application, which will listen on port 8080:

python3 -m flask --app main.py run --host='0.0.0.0' --port 8080

Since you are likely behind a NAT, to test this application on your local machine you need a public IP address that SignalWire can reach. We suggest using ngrok: refer to our guide on ngrok for more information.

In short, while the application is up and listening on port 8080, run ngrok from a new terminal like this:

ngrok http 8080

You'll get a public random URL (such as https://983f-93-41-16-193.ngrok.io/) that you can use to access your local application.

Configuring the number

Now that the code is ready and the HTTP endpoint is reachable from the web, we need to configure our SignalWire number to access it.

First, we need to create a new cXML script resource that points to your ngrok URL. To do so, go to the "Resources" section from your sidebar, and create a new Script Resource. Select the Script type as cXML.

Script resource selector with cXML highlighted.

When configuring the cXML script, select the "External URL" option, and paste the ngrok URL.

cXML script configuration with external URL

Identify your Dashboard and select between Legacy and New UIs using the tabs below.

Resources that were previously accessible in the sidebar of the legacy UI are now located in the unified Resource menu.

If you don't have a phone number yet, make sure to buy one. You will need at least one number to receive messages.

Once you have a phone number ready to use, we'll need to assign it to this Resource. Navigate to the "Phone Numbers" section in the Sidebar, and open the Settings page for the number you are using. Click Assign Resource to assign the number to the cXML Script.

Assign Resource

Refer to Making and Receiving Phone Calls for more information about this step.

You don't need to create a new cXML resource for your call.

Simply open the settings for the number you want to configure, and under "Voice and Fax Settings", choose to handle incoming calls using "LaML Webhooks", then paste the public ngrok URL which connects to your application. Make sure to check the "Use External URL for LaML Webhook handler?" checkbox.

legacy UI cxml webhook configuration

Try dialing the configured phone number: after a few seconds you'll receive an automated reply, whose content will be different depending on the day of the week.

Conclusion

We have shown how to handle calls from code, by emitting XML documents whose content depends on external factors. We did all this in the context of the Compatibility API.

For more advanced, real-time applications, you'll want to check out our Realtime SDK. Find more details in the guide about First Steps with Voice in the Realtime SDK.