Answering Machine/Voicemail Detection

Answering machine or voicemail detection can be enabled by including the machine_detection parameter when making an outbound call using the Call API. Plivo will run the machine detection in the background and on a successful match, would make an HTTP request to the machine_detection_url. On positive detection, Plivo will send a Machine parameter with the value true to the machine_detection_url. You can now make a decision based on this parameter. Ideally you should then use the Transfer API to change the flow of the call. You can checkout Asynchronous Machine Detection for futher details on the parameters sent.

Prerequisites

  1. Sign up for a free Plivo trial account.
  2. Check out our Helper Libraries page and install the right helper based on the programming language you want to use.
  3. Buy a Plivo phone number (optional).
  4. Use a web hosting service to host your web application. There are many inexpensive cloud hosting providers that you can use for just a few dollars a month. Follow the instructions of your hosting provider to host your web application.

Implementation

  1. Copy the relevant code below into a text file and save it.
  2. Replace Your_AUTH_ID and Your_AUTH_TOKEN with the AUTH ID and AUTH TOKEN found on your Plivo dashboard.
  3. Add your from (source) phone number. This will show up as your Sender ID. Be sure that all phone numbers include country code, area code, and phone number without spaces or dashes (e.g., 14153336666).
  4. Add your to (destination) phone numbers. These are the phone numbers to which you will make a call. To place calls in bulk, separate your destination phone numbers with the "<" character (e.g., 14156667777<14157778888<14158889999). Be sure that all phone numbers include country code, area code, and phone number without spaces or dashes (e.g., 14153336666).

    Note: If you are using a trial account, your destination number needs to be verified with Plivo. Phone numbers can be verified at the Sandbox Numbers page.

  5. Edit the answer_url field with a URL to be requested by Plivo when the call is answered which tells Plivo what to do with the call.
  6. Edit the answer_method field with either GET or POST.
  7. Edit the machine_detection_url field with a URL to be requested by Plivo if an answering machine or voicemail is detected on the call.
  8. Edit the machine_detection_method field with either GET or POST.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import plivo, plivoxml
from flask import Flask

auth_id = "Your_AUTH_ID"
auth_token = "Your_AUTH_TOKEN"
p = plivo.RestAPI(auth_id, auth_token)

# Machine detection using Call API

params = {
    'to': '1111111111', # The phone numer to which the call will be placed
    'from': '2222222222', # The phone number to use as the caller id
    # The URL invoked by Plivo when the outbound call is answered
    'answer_url': "http://example.com/detect/",
    'answer_method': "GET", # Method to request the answer_url
    'machine_detection': "true", # Used to detect if the call has been answered by a machine. Valid values are "true" and "hangup".
    'machine_detection_time': "10000", # Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
    'machine_detection_url': "http://example.com/machine_detection/", # A URL where machine detection parameters will be sent by Plivo.
    'machine_detection_method': "GET" # Method used to invoke machine_detection_url
}

# Make an outbound call
response = p.make_call(params)
print str(response)


# As soon as the voicemail finishes and there is a silence for minSilence
# milliseconds, the next element in the XML is processed, without waiting for
# the whole period of length seconds to pass

@app.route('/detect/', methods=['GET','POST'])
def detect():
    try:
        r = plivoxml.Response()
        params = {
            'length': "1000", # Time to wait in seconds
            'silence' : "true", # When silence is set to true, if no sound is detected for minSilence milliseconds, end the wait and continue to the next element in the XML immediately
            'minSilence' : "3000" # Only used when silence is set to true. The minimum length in milliseconds of silence that needs to be present to qualify as silence
        }
        r.addWait(**params)
        r.addSpeak("Hello Voicemail")
        print r.to_xml()
        return Response(str(r), mimetype='text/xml')
    except Exception as e:
        print '\n'.join(traceback.format_exc().splitlines())

# Machine Detection URL example

@app.route('/machine_detection/',methods=['POST', 'GET'])
def machine_detection():
    from_number = request.args.get('From') # The number calling
    to_number = request.args.get('To') # The number being called
    machine = request.args.get('Machine') # This parameter will be true if a machine has been detected on the call.
    call_uuid = request.args.get('CallUUID') # The ID of the call.
    event = request.args.get('Event') # The event of the notification. When requesting the machine_detection_url, this parameter will always have the value 'MachineDetection'
    call_status = request.args.get('CallStatus') # The status of the call. This will hold the value of in-progress.

    print "From: %s " % (from_number)
    print "To: %s " % (to_number)
    print "Machine: %s " % (machine)
    print "Call UUID: %s " % (call_uuid)
    print "Event: %s " % (event)
    print "Call Status: %s " % (call_status)
    return "OK"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
require 'rubygems'
require 'sinatra'
require 'plivo'
include Plivo

AUTH_ID = "Your_AUTH_ID"
AUTH_TOKEN = "Your_AUTH_TOKEN"
p = RestAPI.new(AUTH_ID, AUTH_TOKEN)

# Machine detection using Call API

params = {
    'to' => '2222222222', # The phone number to which the call will be placed
    'from' => '1111111111', # The phone number to use as the caller id
    'answer_url' => 'https://example.com/detect/', # The URL requested by Plivo when the outbound call is answered
    'answer_method' => 'GET', # The method used to request the answer_url
    'machine_detection' => "true", # Used to detect if the call has been answered by a machine. The valid values are true and hangup.
    'machine_detection_time' => "10000", # Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
    'machine_detection_url' => "http://example.com/machine_detection/", # A URL where machine detection parameters will be sent by Plivo.
    'machine_detection_method' => "GET" # Method used to request machine_detection_url
}

# Make an outbound call
response = p.make_call(params)
print response

# As soon as the voicemail finishes speaking, and there is a silence for minSilence milliseconds,
# the next element in the XML is processed, without waiting for the whole period of length seconds to pass

get '/detect/' do
    r = Response.new()
    params = {
        'length' => "1000", # Time to wait in seconds
        'silence' => "true", # When silence is set to true, if no sound is detected for minSilence milliseconds, end the wait and continue to the next element in the XML immediately
        'minSilence' => "3000" # Only used when silence is set to true. The minimum length in milliseconds of silence that needs to be present to qualify as silence
    }
    r.addWait(params)
    r.addSpeak("Hello Voicemail")

    puts r.to_xml()
    content_type 'text/xml'
    return r.to_s()
end

# Machine Detection URL example

get '/machine_detection/' do
    r = Response.new()
    from_number = params[:From]
    to_number = params[:To]
    machine = params[:Machine]
    call_uuid = params[:CallUUID]
    event = params[:Event]
    status = params[:CallStatus]

    puts "From: #{from_number}, To: #{to_number}, Machine: #{machine}, Call UUID: #{call_uuid}, Event: #{event}, Status: #{status}"
    return "OK"
end
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var express = require('express');
var app = express();

var plivo = require('plivo');
var p = plivo.RestAPI({
  authId: 'Your_AUTH_ID',
  authToken: 'Your_AUTH_TOKEN'
});

app.set('port', (process.env.PORT || 5000));

var params = {
    'to': '2222222222',    // The phone numer to which the call will be placed
    'from': '1111111111', // The phone number to be used as the caller id
    'answer_url': 'http://example.com/detect/', // The URL invoked by Plivo when the outbound call is answered
    'answer_method': 'GET', // The method used to call the answer_url
    'machine_detection': "true", // Used to detect if the call has been answered by a machine. The valid values are true and hangup.
    'machine_detection_time': "5000", // Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
    'machine_detection_url': "http://example.com/machine_detection/", // A URL where machine detection parameters will be sent by Plivo.
    'machine_detection_method': "GET" // Method used to request machine_detection_url
};

// Make an outbound call and print the response
p.make_call(params, function (status, response) {
    console.log('Status: ', status);
    console.log('API Response:\n', response);
});

app.post('/detect/', function(request, response) {
    var r = plivo.Response();
    var params = {
        'length': "1000", // Time to wait in seconds
        'silence': "true", // When silence is set to true, if no sound is detected for minSilence milliseconds, end the wait and continue to the next element in the XML immediately
        'minSilence': "3000" // Only used when silence is set to true. The minimum length in milliseconds of silence that needs to be present to qualify as silence
    };
    r.addWait(params);
    r.addSpeak("Hello Voicemail");

    console.log(r.toXML());
    response.set({'Content-Type': 'text/xml'});
    response.send(r.toXML());
});

app.post('/machine_detection/', function(request, response){
    var from_number = request.body.From;
    var to_number = request.body.To;
    var machine = request.body.Machine;
    var call_uuid = request.body.CallUUID;
    var event = request.body.Event;
    var status = request.body.CallStatus;

    console.log("From: ", from_number,
                "To: ", to_number,
                "Machine: ", machine,
                "Call UUID: ", call_uuid,
                "Event: ", event,
                "Status: ", status);

    response.send("OK");
});

app.listen(app.get('port'), function() {
    console.log('Node app is running on port', app.get('port'));
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!-- machine_detection_call.php -->

<?php
    require 'vendor/autoload.php';
    use Plivo\RestAPI;

    $auth_id = "Your_AUTH_ID";
    $auth_token = "Your_AUTH_TOKEN";
    $p = new RestAPI($auth_id, $auth_token);

    $params = array(
        'to' => '14155069431', # The phone numer to which the call will be placed
        'from' => '18583650866', # The phone number to use as the caller id
        'answer_url' => "https://example.com/detect.php", # The URL requested by Plivo when the outbound call is answered
        'answer_method' => "GET", # The method used to request the answer_url
        'machine_detection' => "true", # Used to detect if the call has been answered by a machine. The valid values are true and hangup.
        'machine_detection_time' => "10000", # Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
        'machine_detection_url' => "https://example.com/machine_detect.php", # A URL where machine detection parameters will be sent by Plivo.
        'machine_detection_method' => "GET" # Method used to request machine_detection_url
    );

    $response = $p->make_call($params);
    print_r ($response);
?>

<!-- detect.php-->

<?php
    require 'vendor/autoload.php';
    use Plivo\Response;

    $r = new Response();
    // Add Wait tag
    $params = array(
        'length' => "1000", # Time to wait in seconds
        'silence' => "true", # When silence is set to true, if no sound is detected for minSilence milliseconds, end the wait and continue to the next element in the XML immediately
        'minSilence' => "3000" # Only used when silence is set to true. The minimum length in milliseconds of silence that needs to be present to qualify as silence
    );
    $r->addWait($params);
    $r->addSpeak("Hello Voicemail");
    Header('Content-type: text/xml');
    echo($r->toXML());
?>

<!-- machine_detect.php-->

<?php
    $from_number = $_REQUEST['From'];
    $to_number = $_REQUEST['To'];
    $machine = $_REQUEST['Machine'];
    $call_uuid = $_REQUEST['CallUUID'];
    $event = $_REQUEST['Event'];
    $call_status = $_REQUEST['CallStatus'];
    error_log("From: $from_number , To: $to_number , Machine: $machine , Call UUID: $call_uuid , Event: $event , Call Status: $call_status");
    echo "OK";
?>
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// machineDetectionCall.java
package plivoexample;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import com.plivo.helper.api.client.*;
import com.plivo.helper.api.response.call.Call;
import com.plivo.helper.exception.PlivoException;

public class machineDetectionCall {
    public static void main(String[] args) throws IllegalAccessException {

        String auth_id = "Your_AUTH_ID";
        String auth_token = "Your_AUTH_TOKEN";

        RestAPI api = new RestAPI(auth_Id, auth_Token, "v1");

        LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
        parameters.put("to","2222222222"); // The phone number to which the call will be placed
        parameters.put("from","1111111111"); // The phone number to be used as the caller id
        parameters.put("answer_url","https://example.com/detect/"); // The URL requested by Plivo when the outbound call is answered
        parameters.put("answer_method","GET"); // method to request the answer_url
        parameters.put("machine_detection","true"); // Used to detect if the call has been answered by a machine. The valid values are true and hangup.
        parameters.put("machine_detection_time", "10000"); // Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
        parameters.put("machine_detection_url", "https://example.com/machine_detection/"); // A URL where machine detection parameters will be sent by Plivo.
        parameters.put("machine_detection_method", "GET"); // Method used to request machine_detection_url

        try {
           Call resp = api.makeCall(parameters);
           System.out.println(resp);
        } catch (PlivoException e) {
           System.out.println(e.getLocalizedMessage());
        }
    }
}

// detect.java
package plivoexample;

import java.io.IOException;
import com.plivo.helper.exception.PlivoException;
import com.plivo.helper.xml.elements.PlivoResponse;
import com.plivo.helper.xml.elements.Speak;
import com.plivo.helper.xml.elements.Wait;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class detect extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        PlivoResponse response = new PlivoResponse();
        Wait wait = new Wait();
        wait.setLength(10); // Time to wait in seconds
        wait.setMinSilence(3000); // When silence is set to true, if no sound is detected for minSilence milliseconds, end the wait and continue to the next element in the XML immediately
        wait.setSilence(true); // Only used when silence is set to true. The minimum length in milliseconds of silence that needs to be present to qualify as silence

        Speak spk = new Speak("Hello Voicemail");

        try {
            response.append(wait);
            response.append(spk);
            System.out.println(response.toXML());
            resp.addHeader("Content-Type", "text/xml");
            resp.getWriter().print(response.toXML());
        } catch (PlivoException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        String port = System.getenv("PORT");
        if(port == null)
            port = "8000";
        Server server = new Server(Integer.valueOf(port));
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
        context.addServlet(new ServletHolder(new detect()),"/detect/");
        context.addServlet(new ServletHolder(new machineDetection()),"/machine_detection/");
        server.start();
        server.join();
    }
}

// machineDetection.java
package plivoexample;

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

public class machineDetection extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String from_number = req.getParameter("From");
        String machine = req.getParameter("Machine");
        String to_number = req.getParameter("To");
        String call_uuid = req.getParameter("CallUUID");
        String event = req.getParameter("Event");
        String status = req.getParameter("CallStatus");
        System.out.println("From: " + from_number + " Machine: " + machine + " To: " + to_number +
                            " Call UUID: " + call_uuid + " Event: " + event + " Call Status: " + status);
        resp.getWriter().print("OK");
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// make outbound call with machine detection enabled
using System;
using System.Collections.Generic;
using RestSharp;
using Plivo.API;

namespace send_sms
{
    class bulk_sms
    {
        static void Main(string[] args)
        {
            RestAPI plivo = new RestAPI("Your_AUTH_ID", "Your AUTH_TOKEN");

            // Make an outbound call
            IRestResponse<Call> resp = plivo.make_call(new Dictionary<string,string>()
            {
                {"to", "2222222222" }, // The phone number to which the call has to be placed
                {"from", "1111111111"}, // The phone number to be used as the caller id
                {"answer_url","http://example.com/detect/"}, // The URL requested by Plivo when the outbound call is answered
                {"answer_method","GET"}, // Method to request the answer_url
                {"machine_detection","true"}, // Used to detect if the call has been answered by a machine. The valid values are true and hangup.
                {"machine_detection_time","10000"}, // Time allotted to analyze if the call has been answered by a machine. The default value is 5000 ms.
                {"machine_detection_url","http://example.com/machine_detection/"}, // A URL where machine detection parameters will be sent by Plivo.
                {"machine_detection_method","GET"} // Method used to request machine_detection_url
            });

            //Prints the message details
            Console.Write(resp.Content);

            Console.ReadLine();
        }
    }
}

// Example for answer_url and machine_detection_url
using System;
using System.Collections.Generic;
using System.Diagnostics;
using RestSharp;
using Plivo.XML;
using Nancy;

namespace machine_detection
{
    public class Program : NancyModule
    {
        public Program()
        {

            // As soon as the voicemail finishes speaking, and there is a silence for minSilence milliseconds,
            // the next element in the XML is processed, without waiting for the whole period of length seconds to pass

            Get["/detect/"] = x =>
            {
                Plivo.XML.Response resp = new Plivo.XML.Response();
                resp.AddWait(new Dictionary<string, string>()
                {
                    {"length","1000"},
                    {"silence","true"},
                    {"minSilence","3000"}
                });

                resp.AddSpeak("Hello Voicemail", new Dictionary<string, string>() { });

                Debug.WriteLine(resp.ToString());

                var output = resp.ToString();
                var res = (Nancy.Response)output;
                res.ContentType = "text/xml";
                return res;
            };

            //Machine Detection URL example
            Get["/machine_detection/"] = x =>
            {
                String from_number = Request.Query["From"];
                String machine = Request.Query["Machine"];
                String to_number = Request.Query["To"];
                String call_uuid = Request.Query["CallUUID"];
                String event_ = Request.Query["Event"];
                String status = Request.Query["CallStatus"];

                Debug.WriteLine("From: {0}, Machine: {1}, To: {2}, Call UUID: {3}, Event: {4}, Call Status: {5}", from_number, machine, to_number, call_uuid, event_, status);
                return "OK";
            };
        }
    }
}

Sample Response

Make Outbound call
(201, {
        u'message': u'call fired', 
        u'request_uuid': u'a52a7ae0-0551-462c-9cf0-1f79f79737c8', 
        u'api_id': u'45305402-959f-11e4-b932-22000ac50fac'
    }
)

XML returned by the answer_url
<Response>
    <Wait minSilence="3000" silence="true" length="10"/>
    <Speak>Hello Voicemail</Speak>
</Response>

Machine Detection URL output
From : 2222222222 
To : 1111111111 
Machine : true 
Call UUID : 45704ba2-959f-11e4-802f-e9b058eeb9e5 
Event : MachineDetection 
Call Status : in-progress

Next Step

Learn about the Wait XML Element.

  1. Make an Outbound Call
  2. Play a Text-to-speech Message
  3. Connect Call to a Second Person
  4. Greet Caller by Name
  5. Play MP3/WAV Audio to Caller
  6. Hangup Using API
  7. Receive Incoming Call
  8. Forward Incoming Call
  9. Record Using API
  10. Screen Incoming Call
  11. Reject incoming call
  12. Get Details of all Calls
  13. Get Details of a single Call
  14. Get Details of Live Calls
  15. Build an IVR Phone Menu
  16. Conference Call
  17. Call Forward
  18. SIP Endpoint (Direct-Dial)
  19. Inbound Trunk
  20. Outbound Trunk