When forwarding calls, there are instances when the call is not answered (i.e. busy or not well completed). Our customers often want to initiate another action in these scenarios (e.g. redirect the call to another number or connect the caller to a voicemail).
Using Twilio Programmable Voice, we can build a small application that will monitor in an asynchronous way the next action that can be executed by monitoring calling parameters.
Prerequisites
This tutorial is for developers at a beginner level or advanced level or for any person that wants to build their first application with Twilio and understand Callbacks and Webhooks, it is recommended that we at least can install and have the following tools:
- Python
- Pip
- Flask
- A code Editor
- Ngrok
- Twilio Account
Use Case
We want to build an application that firstly forwards incoming calls from Twilio number to a cellphone number, in case nobody picks up We will make that the caller does not get any dead air or that the call is missed, We will ensure the caller goes to a Voicemail from Twilio in case the call gets no answered and in case of Busy the call will go to another phone number, We are making sure here that in both scenarios the caller gets to a different possible destination instead of just a missed call.
To do this, We will build a Webserver for webhooks from which Twilio will request instructions from.
When our Twilio phone number is dialed, Twilio will first look for the configuration the number has for incoming calls, in our case, we are setting Twilio uses a Webhook URL to get new instructions, this URL will be our Flask server, and our code will be hosted in it, this server will send TwiML that Twilio will execute creating the Call Forwarding,
A second call will be generated the difference from a regular call forwarding is that in the previous TwiML received We will also have another URL endpoint, which will be an Action URL, this will be used for Twilio to send a few parameters in which one of them will be Twilio reporting the DialCallStatus
.
Possible DialCallStatus
values are:
Value | Description |
---|---|
completed |
The called party answered the call and was connected to the caller. |
answered |
When calling a conference, the called party answered the call and was connected to the caller. |
busy |
Twilio received a busy signal when trying to connect to the called party. |
no-answer |
The called party did not pick up before the timeout period passed. |
failed |
Twilio was unable to route to the given phone number. This is frequently caused by dialing a properly formatted but non-existent phone number. |
canceled |
The call was canceled via the REST API before it was answered. |
In this use case, we will be reporting no-answer and busy to our Webhook Server and executing an action that will send the call to a different TwiML.
Setting Up Our Machine
We are going to install the tools that we need in order for our Flask Server to run smoothly.
A Flask server is defined as server software that is capable of running HTTP requests on the public Internet private LAN, and private WANs and comprises of one or many computers bundled together and dedicatedly working for running the software application on the worldwide web.
Install Python
To initiate we will need to install Python in our machine, if you’re using a Mac or Linux machine, you probably already have Python installed.
You can check this by opening up a terminal and running the following command:
python3 --version
In case you see that there is no Python we can do it by following this guide, which will show how to install it for either Windows, Mac, or Linux Computer:
https://www.tutorialsteacher.com/python/install-python
normally we will just need to download a file and run the installation wizard, then check again with the python3 --version and confirm we have Python installed.
Install PIP
PIP is a package management system used to install and manage software packages/libraries written in Python. These files are stored in a large “online repository” termed as Python Package Index (PyPI). pip uses PyPI as the default source for packages and their dependencies. So whenever you type:
pip install package_name
pip will look for that package on PyPI and if found, it will download and install the package on your local system.
Install it on Mac: https://www.geeksforgeeks.org/how-to-install-pip-in-macos/
Install it in Windows: https://www.geeksforgeeks.org/how-to-install-pip-on-windows/
You can check that it is correctly installed by running pip3 --version on either Mac or Windows.
Install Flask Server
Now that we have PIP we can install Flask by running the command: pip3 install flask
We can see if Flask is well installed by running the command: flask --version we should see the Flask version that we have installed on our Windows or Mac computer.
We can download Visual Studio Code which is going to be our Code Editor to write Python for our Flask server application:
https://code.visualstudio.com/download
Test Flask Server
In Visual Studio Code, we can create a new file called run.py
and add these lines to it:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
Now run it. In your terminal, type:
python run.py
You should see the output:
* Running on http://127.0.0.1:5000/
Navigate to http://localhost:5000 in a browser. You should see a “Hello World” message.
Note: In the case, the "python run.py" gives you an error, you can try "python3 run.py"
This is showing us that the Server is running as expected and now we are good to make our Application.
Create the call forwarding in your App:
We are firstly making the call forwarding, You need to create a file in Visual Studio Code, we can call it app.py
from flask import Flask, request
import logging
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)
@app.route("/forward", methods=["POST","GET"])
def Incoming():
return ('<Response> <Dial Timeout="10" action="/Dialstatus" method="GET"> +1858258587 </Dial></Response>', 200)
if __name__ == "__main__":
app.run(debug=True, port=4000)
Notice that here we will specify what TwiML will be returned to Twilio to be executed, we are currently setting that we will be going to do forwarding to number +1858258587.
Also, we are setting here the "action" attribute as /Dialstatus which we have not built in our app yet.
This current call can work but we are still not able to handle situations for unanswered or busy calls.
Add an endpoint Webhook in Application for DialStatus:
In order to manage calls that could not be answered or are busy, We need to add a few lines in our same app.py file in Visual Studio for the /Dialstatus endpoint URL which we set as the Action attribute:
from flask import Flask, request
import logging logging.basicConfig(level=logging.INFO) app = Flask(__name__) #This is to do the forwarding of the call: @app.route("/forward", methods=["POST","GET"]) def Incoming(): return ('<Response> <Dial Timeout="10" action="/Dialstatus" method="GET"> +1858258587 </Dial></Response>', 200) #This will be for monitoring the DialStatus parameter that We will receive from Twilio @app.route("/Dialstatus", methods=["POST","GET"]) def WebhookTestInbound(): call_status = request.values.get('DialCallStatus', None) if call_status == 'no-answer': return ('<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>hi please leave a message after the tone</Say> <Record playBeep="true" /></Response> ', 200)
elif: call_status == 'busy': return ('<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>Sending the call to secondar contact</Say> <Dial>+573022918290 </Dial></Response> ', 200) else: return'', 204 if __name__ == "__main__": app.run(debug=True, port=4000) |
Notice that now this application is making that in /Dialstatus route we will receive the parameter of the status of the previous outbound dial call to our server and if the DialCallStatus is no-answer or busy We will return now different TwiML for each scenario, in this example, we are sending the call to record a Voicemail for the unanswered call and We are sending the call to a different number for the Busy call, you can modify to do any TwiML like sending the call to another Webhook URL, play a message, etc.
Run the file with python3 app.py, and then test the flask server in Localhost with the route we gave it, in this case, localhost:4000/forward and localhost:4000/Dialstatus if you put this in your browser, in the terminal of Visual Studio we can check if the responses were successful:
Run it with Ngrok
Now we want to make this server accessible from the public internet, to do that we can use Ngrok, if you don't have Ngrok installed you can follow these instructions: https://ngrok.com/download
When you install it we will run it in our computer terminal "ngrok http 4000"
This is making that now the localhost:4000 is transformed into a reachable link from the internet and the /Forward and /Dialstatus will be also online since they are in our app.py which is our Flask server that contains both in localhost:4000 now tunneled as the Ngrok URL.
Now we copy the forwarding URL from this output and we can add that Ngrok URL to the Number in "when a call comes in" in the Twilio console so that we will first ensure we are forwarding calls and then the application itself will be running and making sure the Action URL with the /Dialstatus will be handling the no-answer and busy calls:
We can try testing the forward call and push to ignore the call, then We will be forwarded to Twilio voicemail and that's how We can handle forwarding calls using the Dialstatus parameters with our own built Webserver!