Skip to main content

FlexML AMD Application

AMD stands for Answering Machine Detection. This guide explains how to create a FlexML application using the Amd verb to protect your destination number from spam calls made by automated systems.

warning

The Amd verb is an experimental feature. It is still in development, and the results may not be 100% accurate.

1. Plan Application Structure

Before building the application, let's outline its behavior and response logic. The application will follow this workflow:

  1. Caller calls the phone number that is associated with our FlexML application.
  2. Application greets the caller and waits for some actions.
  3. Application analyzes the caller's speech trying to determine whether the caller is human or machine and, depending on the result, redirects the caller to the correct destination:
    • Human → Redirect to destination
    • Machine → Hang up
    • Not Sure → Treat as human

Application Algorithm

FlexML has verbs for all these actions, so we can proceed with development.

2. Build Application

In this section, we'll build a FlexML application using Python to handle call routing.

Prerequisites
  • Register with CarrierX to access the CarrierX portal.
  • Rent a phone number from CarrierX to associate it with the created FlexML endpoint later.
  • Have Python installed on your local machine.
  • Add Flask micro web framework to your Python installation. It will serve as a simple web server and will run our FlexML application.

Step 1: Create the First Route

We'll start with a default route (we'll call it hello) that accepts the call and responds. The flexml_amd.py file will contain the Python code for this route (and subsequent routes for simplicity).

note

While this example uses Python, you can implement the server and routing in any programming language.

  1. Import Flask, and initialize the Flask application with the app = Flask(__name__) command.

    from flask import Flask  
    app = Flask(__name__)
  2. Define the hello route (accessible via POST at http://example.com/hello).

    • Use the Say verb to greet the caller. It will pronounce the words stored inside the <Say></Say> tags.
    • Wrap the response in a Response tag.
    • Add a Pause to add some time before the application responds to the call, so that the voice could be correctly loaded even on slower connections. The length of the pause is set in seconds, and three seconds is alright for most cases.

The resulting code for our hello route will look as follows:

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
return '''<Response>
<Pause length="3"/>
<Say>Welcome to CarrierX FlexML application. Please announce yourself</Say>
</Response>'''

Step 2: Start the Flask Server

Test the application by running the Flask server locally:

  1. Set the environment variable:

    WindowsMacOS/Linux
    set FLASK_APP=flexml_amd.pyexport FLASK_APP=flexml_amd.py
  2. Start the Flask server:

    WindowsMacOS/Linux
    python -m flask runpython3 -m flask run

Now your application is running, but it is not visible to the outside world. Expose the localhost route publicly over the Internet so that your endpoint could access and load the FlexML instructions. To do this, you can use the free ngrok tool. If you choose to use ngrok, follow the instructions on their website to download it.

Once your Flask server is running, your terminal will list the port it is running on. Expose this port by running the following command. Make sure to replace 5000 (which is the Flask default port) with the port that your Flask server is running on.

ngrok http 5000

After running ngrok, you'll receive a public URL (e.g., https://4146-188-2-25-253.ngrok-free.app/hello).

ngrok Terminal Window

Associate this URL with a FlexML endpoint or a DID associated with some FlexML endpoint (see the FlexML Endpoint Guide to learn how).

Now we have an introductory route that greets the caller.

This logic can be described with the following block diagram:

Application Algorithm Greeting

The next step is to add the ability to our application to check if caller is human or machine.

Step 3: Check the Caller

Next, we'll use the Amd verb to analyze the caller. We'll apply the following rules:

  1. Greeting length: Callers should introduce themselves briefly (≤2 seconds) or at least will say 'Hello'. Longer greetings of speech without introduction are flagged as machine-like.

  2. Initial silence: Callers should start speaking within 5 seconds. Delays are flagged as machine-like.

  3. Post-greeting pause: A short silence (≥500ms) after the greeting is expected. Immediate continuation is flagged as machine-like.

To match these rules, the following attributes of the Amd verb are used:

AttributeDescription
greetingMax greeting length (ms). Exceeding this flags the caller as a machine.
initialSilenceMax silence (ms) before speaking. Exceeding this flags the caller as a machine.
afterGreetingSilenceRequired silence (ms) after greeting. Too short a pause flags the caller as a machine.
totalAnalysisTimeTotal time (ms) for analysis (includes all phases).
note

For a full list of Amd attributes, refer to the FlexML Syntax documentation.

The final code for the Amd verb should look like this:

<Amd initialSilence="5000" greeting="2000" afterGreetingSilence="500" totalAnalysisTime="8000" />

Update the hello route:

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
return '''<Response>
<Pause length="3"/>
<Say>Welcome to CarrierX FlexML application. Please announce yourself</Say>
<Amd initialSilence="5000" greeting="2000" afterGreetingSilence="500" totalAnalysisTime="8000" />
</Response>'''

The updated logic:

Application Algorithm Enter

Step 4: Parse Analysis Results

Once the analysis is made, the application must parse its results and either redirect the human caller to the correct destination or hang up on the machine caller.

Let's add the amd route where we will parse the results and make a decision. For now, we will simply announce the route and do nothing else afterwards:

@app.route('/amd', methods=['POST'])
def amd():

For the call to be redirected to the new amd route, we add the action="/amd" and method="POST" attributes to the Amd verb. The response for the hello route will look like this:

<Response>
<Pause length="3"/>
<Say>Welcome to CarrierX FlexML application. Please announce yourself</Say>
<Amd action="/amd" method="POST" initialSilence="5000" greeting="2000" afterGreetingSilence="500" totalAnalysisTime="8000" />
</Response>

At this stage, we have the following resulting code for our application:

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
return '''<Response>
<Pause length="3"/>
<Say>Welcome to CarrierX FlexML application. Please announce yourself</Say>
<Amd action="/amd" method="POST" initialSilence="5000" greeting="2000" afterGreetingSilence="500" totalAnalysisTime="8000" />
</Response>'''

@app.route('/amd', methods=['POST'])
def amd():

The hello route, once the Amd verb makes the decision based on the caller behavior, sends the following data to the amd route:

{
"AccountSid": "",
"AMDCause": "LONGGREETING-2000-2000",
"AMDStatus": "MACHINE",
"ApiVersion": "2.0",
"CallSid": "4f0d84c72ff90967f58351cbe8364a77",
"CallerName": "+19093189030",
"Direction": "inbound",
"From": "19093189030",
"OriginalFrom": "+19093189030",
"OriginalTo": "19093189029",
"RequestUrl": "https://4146-188-2-25-253.ngrok-free.app/hello",
"To": "19093189029"
}

This part is of interest to us:

{
"AMDCause": "LONGGREETING-2000-2000",
"AMDStatus": "MACHINE"
}

Where:

AMDStatus: HUMAN, NOTSURE, MACHINE, HANGUP.

AMDCause: Reason for the decision (e.g., LONGGREETING).

This is the result of the Amd verb analysis and the reason for the decision, and in our example it considered the caller MACHINE for the reason of a too long greeting (it exceeded 2 seconds).

In case of the correct detection of a human caller, the response will contain something like this:

{
"AMDCause": "HUMAN-500-500",
"AMDStatus": "HUMAN",
}

As this data is sent in a form of JSON, our application must get it and correctly retrieve the necessary AMDStatus value.

This is done with the request.get_json() method that is available in the Flask request module, which parses data as JSON.

Add the following lines to the amd route of the application:

data = request.get_json()
amd_status = data.get('AMDStatus','')

Here the data variable equals to all the JSON data received from the hello route, and amd_status accepts the AMDStatus value and is equal to MACHINE or HUMAN.

And now we can compare the received status with our expectations. The result should follow this logic:

  • if amd_status is HUMAN or NOTSURE, the application will redirect the caller to the destination (using the Dial FlexML verb).
  • if amd_status is MACHINE or HANGUP, the application will hang up.

The final code of our application:

from flask import Flask, request

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
return '''<Response>
<Pause length="3"/>
<Say>Welcome to CarrierX FlexML application. Please announce yourself</Say>
<Amd action="/amd" method="POST" initialSilence="5000" greeting="2000" afterGreetingSilence="500" totalAnalysisTime="8000" />
</Response>'''

@app.route('/amd', methods=['POST'])
def amd():
data = request.get_json()
amd_status = data.get('AMDStatus','')
if amd_status == 'HUMAN' or amd_status == 'NOTSURE':
return '''<Response>
<Say>Thank you! You will be now connected to the destination phone number</Say>
<Dial>
<Number>19093189031</Number>
</Dial>
</Response>'''
else:
return '''<Response>
<Hangup/>
</Response>'''

Test the application by calling the number and observing the behavior for human/machine responses.

3. Further Reading

You've built a FlexML application with AMD capabilities! Explore these resources for more::