Tracking Call Status: How Can You Tell If a Human Answers the Phone?

February 16, 2016
Written by

Twilio Bug Logo

Update: Twilio now has Answering Machine Detection so you don’t have to build your own!

As you’re automating calls via your Twilio account, sometimes you want to avoid voicemail and play top secret messages for humans only. This sounds easy enough. But it might not work the way you expect.

Problem

Can you tell whether a call was answered by a human or machine? A search for this question might lead you to Answering Machine Detection (AMD). Unfortunately, AMD is more art than science, and tends to fail frequently across different handsets and voicemail providers.

Problem Solved

We suggest something like a Turing test, but in reverse. The solution is Call Screening or what Twilio refers to as Human Detection. Human Detection uses the <Gather> verb to respond to keys pressed by a human. Let’s see how to do this on the most basic level in various popular programming languages.

Choose Your Destiny

Perhaps simplest to understand, if you’re familiar with TwiML and XML, is the following example beginning with the file HumanDetection.xml:

<!--?xml version="1.0" encoding="UTF-8"?-->
<Response>
  <Say>Press any key to hear an important and confidential message about your appointment.</Say>
  <Gather timeout="10" action="/message.xml" method="GET"> </Gather>
  <Say>You did not press a key. Goodbye.</Say>
</Response>

Before proceeding through a call, we use the TwiML verb <Gather> to collect input that a caller enters on their keypad and apply the action attribute to redirect to a message the caller can hear only after pressing some key. Note, the timeout attribute value is set to ten seconds. If you do not specify a value, the default is five seconds. If the timeout is reached before input is received, Twilio will not make a request to the action “/message.xml” but will continue to process the TwiML verb immediately following <Gather> which is <Say> in this instance. Keep in mind that Twilio will re-request the current document’s URL by default which can lead to unwanted looping behavior if, for example, you leave out an action URL.

But when a human does enter input, it will proceed to the message.xml file which looks like this:

<!--?xml version="1.0" encoding="UTF-8"?-->
<Response>
  <Say>This message is meant only for humans.</Say>
</Response>

And there you have it, the detection of mere mortals in action! But we have helper libraries so you don’t have to write XML by hand.

We’ll rewrite HumanDetection.xml for Python and see how this works in a little Flask app. Then you’ll find snippets to get you started in different languages that continue to redirect to message.xml. I’ve now hosted both XML files with a TwiMLBin URL you can use to test with immediately.

Build Call Screening with Python

Using the twilio-python helper library. Create a file HumanDetection.py. If you want to see a README to make this work, grab this gist.

from flask import Flask

from twilio import twiml
from twilio.rest import TwilioRestClient

# Grab your Account Sid and Auth Token from twilio.com/user/account
account_sid = "YOUR_ACCOUNT_SID"
auth_token = "YOUR_AUTH_TOKEN"
client = TwilioRestClient(account_sid, auth_token)

app = Flask(__name__)


@app.route('/voice', methods=['POST'])
def detect_humans():
    r = twiml.Response()
    r.say("Machines don't press keys, but humans do. Press a key now.")
    r.gather(timeout="10", 
             action="http://twimlbin.com/external/0d2c3c0909a0e23e")
    r.say("Too bad, you did not press a key. Goodbye!")

    return str(r)

call = client.calls.create(to="YOUR_NUMBER", from_="YOUR_TWILIO_NUMBER",
                           url="YOUR_VOICE_URL")

if __name__ == "__main__":
    app.run()

Be sure your server is publicly available using something like ngrok running on port 5000. Run ‘python HumanDetection.py’ in its project directory (after checking off the README list of course) and when the phone rings, let your Twilio number know that you’re indeed a human.

Build Call Screening with Ruby

Using the twilio-ruby helper library. Create HumanDetection.rb.

require 'rubygems'
require 'twilio-ruby'
require 'sinatra'

get '/voice' do
  response = Twilio::TwiML::Response.new do |r|
    r.Say "Humans reign, please press a key to continue."
    r.Gather action: "http://twimlbin.com/external/0d2c3c0909a0e23e"
    r.Say "You did not press a key. Goodbye!"
  end
  response.text
end

You’ll want to run ngrok on port 4567 with this one, ‘ruby HumanDetection.rb’.

Build Call Screening with Java

Using the twilio-java helper library. Create HumanDetection.java. For a complete project setup check out this post by my colleague Chris.

package com.twilio;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.Gather;


public class HumanDetection extends HttpServlet {
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
        TwiMLResponse twiml = new TwiMLResponse();


        Gather gather = new Gather();
        gather.setAction("http://twimlbin.com/external/0d2c3c0909a0e23e");
        Say sayInGather = new Say("Please enter something like a human.");

        try{
            gather.append(sayInGather);
            twiml.append(gather);
        } catch (TwiMLException e) {
            e.printStackTrace();
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}

Build Call Screening with PHP

Using twilio-php helper library. Create HumanDetection.php.

<?php
# Include Twilio PHP helper library.
require "/path/to/twilio-php/Services/Twilio.php";

# Create response object.
$response = new Services_Twilio_Twiml();
$response->say("Hello, press any key to hear a top secret message.");
$gather = $response->gather(array(
    'action' => "http://twimlbin.com/external/0d2c3c0909a0e23e"
));
$response->say("You did not reveal yourself to be human. Goodbye!");

print $response;

?>

Run ‘php -S localhost:8888’ in your project directory.

Finish It

Now you should be able to detect when a human answers the phone and respond accordingly. This example is the basis of building out more complex phone trees and IVR use cases.

Tell us what you’ve been up to lately. Next time you’re in need of the human connection, just gather around the community We’re here to help!

Email me: mspeir@twilio.com

Tweet me: @meganspeir