What Can You Build With Plivo's Web SDK?

Plivo's Web SDK utilizes WebRTC (Web Real-Time Communication) to allow businesses to build browser-based real-time communication tools without plugins. This new generation of communication tools uses a data connection to transmit voice-based calls. More specifically, our Web SDK allows you to do the following:

  • Make phone calls from a browser: this enables your browser act like a phone and make calls to mobile phones, landlines, or SIP-enabled devices including softphones.
  • Receive phone calls on a browser: receive calls on your browser-based app from any mobile phones, landlines, or SIP-enabled device including softphones.
  • Make phone calls between browsers (browser-to-browser): users can make phone calls with your browser-based app without needing to use the same browser, telecom software, or plugins.
  • Note: Plivo's web SDK supports most modern browsers. Even if the browser does not support HTML5, it will automatically fallback to Flash with full functionality.

What Do You Need?

Step 1: Get the Prerequisites

  1. Sign up for a free Plivo trial account.
  2. Install the right Helper Library based on the programming language you want to use.
  3. 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 on how to host your web application.
  4. In addition to your application, you'll also be instructed to download a set of asset files (e.g., CSS, JavaScript, images, etc) associated with your app. Use either your web server or a content delivery network (CDN) to serve up the asset files of your web app. Follow the instructions of your web server or CDN on how to serve your asset files.

Step 2: Set up the Back-End Web Server

Another critical component of an web app is the back-end web server. To make outbound calls from a Plivo SIP endpoint, it needs to be linked to a Plivo application. If the call is successful, then this application will return a valid XML. This web server will help us achieve the following:

  1. Provide the fundamental back-end logic for the client-side applications that you will build in the following tutorials (e.g., what should the app do if the receiver is busy? or what happens if the destination number is not recognized?).
  2. Serve up the XML application and/or make REST API calls to coordinate Plivo's voice calling services.

There are a two main ways you can set up a back-end web server.
Option 1: Using the default Plivo app

Use the default "Direct Dial" application that we've already added to your Plivo account. This is a web server we've setup (using the instructions in option 2) and added to your account by default. Just visit the Application page of your Plivo GUI. You don't need to do anything with this now, but you will be prompted to link this app to your SIP Endpoint in the following Web SDK tutorials.

Plivo Direct Dial Application

Option 2: Setup a route on your own web server

Let’s assume your web server is located at www.example.com. Below is a snippet to set up a route on your webserver. Lets call it, direct-dial. Now when we send an HTTP request to www.example.com/direct-dial this route will be invoked. You will be prompted to configure this URL in your Plivo application in the following Web SDK tutorials.

  1. Select the relevant language, copy the relevant code below into a text file and save it as direct-dial.
  2. Note: Make sure that the code is saved with the appropriate extension (e.g., .py for Python).
  3. Deploy the server to make this application available via the public Internet. The easiest and least time consuming is using a platforms-as-a-service that support Python (e.g., Heroku). You can also test things out temporarily using a service that provides a tunnel from the public internet to a port on your local machine (e.g., ngrok). However, in the long term you should deploy your app to your own servers (e.g., Digital Ocean).
  4. Next, see below on how to test your servers.


 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
from flask import Flask, request, make_response
import plivoxml
app = Flask(__name__)

@app.route('/direct-dial/', methods=['GET', 'POST'])
def sip_route():
    try:
        print "SIP Route %s" % request.values.items()
        to = request.args.get('ForwardTo', None)
        _from = request.args.get('CLID', None)
        dial_music = request.args.get('DialMusic', "")
        disable_call = request.args.get('DisableCall', "")
        if request.method == "GET":
            if not to:
                to = request.args.get('To', None)
            if _from is None:
                _from = request.args.get('From', '')
            cname = request.args.get('CallerName', '')
            hangup = request.args.get('HangupCause', None)
        else:
            if not to:
                to = request.form.get('To', None)
            if _from is None:
                _from = request.form.get('From', '')
            cname = request.form.get('CallerName', '')
            hangup = request.form.get('HangupCause', None)
        
        if hangup:
            response = make_response("SIP Route hangup callback")
            return response

        r = plivoxml.Response()

        if not to:
            print "SIP Route cannot identify destination number"
            r.addHangup()
        else:
            if to[:4] == 'sip:':
                is_sip_user = True
            else:
                is_sip_user = False
            if is_sip_user and disable_call in ('all', 'sip'):
                print "SIP Route calling sip user is disabled : %s" % str(disable_call)
                r.addHangup(reason="busy")
            elif not is_sip_user and disable_call in ('all', 'number'):
                print "SIP Route calling number is disabled : %s" % str(disable_call)
                r.addHangup(reason="busy")
            else:
                print "SIP Route dialing %s" % str(to)
                if not dial_music:
                    d = r.addDial(callerId=_from, callerName=cname)
                else:
                    d = r.addDial(callerId=_from, callerName=cname, dialMusic=dial_music)
                if is_sip_user:
                    d.addUser(to)
                else:
                    d.addNumber(to)

        response = make_response(r.to_xml())
        response.headers['Content-Type'] = 'text/xml'
        return response

    except Exception, e:
        print str(e)
        return "ERROR %s" % str(e)


if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)
 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
require 'rubygems'
require 'plivo'
require 'sinatra'

include Plivo

get '/direct-dial/' do
    puts "SIP Route #{request.params}"
    to = params[:ForwardTo] || params[:To] 
    from = params[:CLID] ? params[:CLID] : params[:From] ? params[:From] : ''
    cname = params[:CallerName] ? params[:CallerName] : ''
    hangup = params[:HangupCause]
    dial_music = params[:DialMusic] || nil
    disable_call = params[:DisableCall]

    if hangup
        puts "SIP Route hangup callback" 
    end

    r = Response.new()

    if to
        #we have someone we can call
        is_sip_user = to[0, 4] == "sip:"

        #hangup if recipient is a sip endpoint with inbound calling disabled
        if (is_sip_user) && (['all', 'sip'].include? disable_call)
            puts "SIP Route calling sip user is disabled : #{disable_call}"
            r.addHangup(reason='busy')

            #hangup if recipient is a sip endpoint with outbound calling disabled
        elsif (!is_sip_user) && (['all', 'number'].include? disable_call)
            puts "SIP Route calling number is disabled : #{disable_call}"
            r.addHangup(reason='busy')
        else #place call
            puts "SIP Route dialing #{to}"
            parameters = {
            'callerId' => from,
            'callerName' => cname,
            'dialMusic' => dial_music
            }.reject{ |_, value| value.empty?} #removes dial music key/value pair if it's blank.

            d = r.addDial(parameters)
            is_sip_user ? d.addUser(to) : d.addNumber(to)
        end
    else  
        #hangup call without recipient
        puts "SIP Route cannot identify destination number"
        r.addHangup()
    end

    puts r.to_xml()
    content_type 'text/xml'
    return r.to_s()
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
<?php
require 'vendor/autoload.php';
use Plivo\Response;
$dst = $_REQUEST['ForwardTo'];
if(! $dst)
    $dst = $_REQUEST['To'];
$src = $_REQUEST['CLID'];
if(! $src)
    $src = $_REQUEST['From'] or "";
$cname = $_REQUEST['CallerName'] or "";
$hangup = $_REQUEST['HangupCause'];
$dial_music = $_REQUEST['DialMusic'];
$disable_call = $_REQUEST['DisableCall'];
    
$r = new Response();
if($dst) {
    $dial_params = array();
    if($src)
        $dial_params['callerId'] = $src;
    if($cname)
        $dial_params['callerName'] = $cname;
    if(substr($dst, 0,4) == "sip:")
        $is_sip_user = TRUE;
    else
        $is_sip_user = FALSE;
    if($is_sip_user and in_array($disable_call, array("all", "sip"))) {
        $r->addHangup(array("reason" => "busy"));
    } elseif (! $is_sip_user and in_array($disable_call, array("all", "number"))) {
        $r->addHangup(array("reason" => "busy"));
    } else {
        if($dial_music)  {
            $dial_params["dialMusic"] = $dial_music;
            $d = $r->addDial($dial_params);
        } else
            $d = $r->addDial($dial_params);
        if($is_sip_user)
            $d->addUser($dst);
        else
            $d->addNumber($dst);
    } 
 } else {
     $r->addHangup();
 }
header("Content-Type: text/xml");
echo($r->toXML());
?>
 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
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using RestSharp;
using Nancy;
using Plivo;

namespace GoPlivo
{
    public class PlivoDirectDial : NancyModule
    {
        public PlivoDirectDial()
        {
            Get["/direct-dial/"] = x => {
                string dst = Request.Query["ForwardTo"];
                string src = Request.Query["CLID"];
                string cname = Request.Query["CallerName"];
                string hangup = Request.Query["HangupCause"];
                string dialMusic = Request.Query["DialMusic"];
                string disableCall = Request.Query["DisableCall"];
                if ( String.IsNullOrEmpty(dst) )
                    dst = Request.Query["To"];
                if ( String.IsNullOrEmpty(src) )
                    src = Request.Query["From"];
                    
                if (String.IsNullOrEmpty(cname))
                    cname = "";
                if ( !String.IsNullOrEmpty(hangup) )
                    return "SIP Route hangup callback";

                Plivo.XML.Response response = new Plivo.XML.Response();

                if ( String.IsNullOrEmpty(dst) ) {
                    Console.WriteLine("SIP Route cannot identify destination number");
                    response.AddHangup(new Dictionary<string, string> {
                        {"reason", "busy"},
                    });
                } else {
                    bool isSipUser = false;

                    if (dst.Length > 4 && dst.Substring(0, 4) == "sip:")
                        isSipUser = true;

                    if (isSipUser && disableCall == "all" || disableCall == "sip") {
                        Console.WriteLine(String.Format("SIP Route calling sip user is disabled : %s", disableCall));
                        response.AddHangup(new Dictionary<string, string>() {
                            { "reason", "busy" },
                        });
                    } else if (!isSipUser && disableCall == "all" || disableCall == "number") {
                        response.AddHangup(new Dictionary<string, string>() {
                            { "reason", "busy" },
                        });
                    } else {
                        Console.WriteLine(String.Format("SIP Route dialing %s", dst));
                        
                        if (!String.IsNullOrEmpty(dialMusic) && isSipUser) {
                            var dial = response.AddDial(new Dictionary<string, string>() {
                                {"callerId", src},
                                {"callerName", cname},
                                {"dialMusic", dialMusic}
                            }); 
                            dial.AddUser(dst, new Dictionary<string, string>() { });
                            response.Add(dial);
                        } else if (String.IsNullOrEmpty(dialMusic) && isSipUser) {
                            var dial = new Plivo.XML.Dial(new Dictionary<string, string>() {
                                {"callerId", src},
                                {"callerName", cname},
                            });
                            dial.AddUser(dst, new Dictionary<string, string>() { });
                            response.Add(dial);
                        } else if (!String.IsNullOrEmpty(dialMusic) && !isSipUser) {
                            var dial = new Plivo.XML.Dial(new Dictionary<string, string>() {
                                {"callerId", src},
                                {"callerName", cname},
                                {"dialMusic", dialMusic},
                            });
                            dial.AddNumber(dst, new Dictionary<string, string>() { });
                            response.Add(dial);
                        } else if (String.IsNullOrEmpty(dialMusic) && !isSipUser) {
                            var dial = new Plivo.XML.Dial(new Dictionary<string, string>() {
                                {"callerId", src},
                                {"callerName", cname},
                            });
                            dial.AddUser(dst, new Dictionary<string, string>() { });
                            response.Add(dial);
                        }
                    }
                }
                Debug.WriteLine(response.ToString());

                var output = response.ToString();
                var res = (Nancy.Response)output;
                res.ContentType = "text/xml";
                return res;
            };
        }
    }
}
  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
120
121
122
123
124
package plivoexample;

import java.io.IOException;

import com.plivo.helper.exception.PlivoException;
import com.plivo.helper.xml.elements.Dial;
import com.plivo.helper.xml.elements.Hangup;
import com.plivo.helper.xml.elements.Number;
import com.plivo.helper.xml.elements.PlivoResponse;
import com.plivo.helper.xml.elements.User;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class directDialApp extends HttpServlet {
    private static final long serialVersionUID = 1L;    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        String toNumber    = request.getParameter("ForwardTo")==null?"":request.getParameter("ForwardTo");
        String fromNumber  = request.getParameter("CLID")==null?"":request.getParameter("CLID");
        String callerName  = request.getParameter("CallerName")==null?"":request.getParameter("CallerName");
        String hangupCause = request.getParameter("HangupCause")==null?"":request.getParameter("HangupCause");
        String disableCall = request.getParameter("DisableCall")==null?"":request.getParameter("DisableCall");
        String dialMusic   = request.getParameter("DialMusic")==null?"":request.getParameter("DialMusic");

        // flags
        boolean isSipUser = false;

        // if 'ForwardTo' is not set specifically use 'To'.
        // caveat: if 'ForwardTo' is not set when app is linked 
        // to a number, it would a loop.
        if ( toNumber.isEmpty() ) {
            toNumber  = request.getParameter("To")==null?"":request.getParameter("To");
        }

        // if caller ID is not set specifically use 'From'
        if ( fromNumber.isEmpty() ) {
            fromNumber  = request.getParameter("From")==null?"":request.getParameter("From");
        }
      
        // Plivo XML elements
        PlivoResponse plivoResponse = new PlivoResponse();

        Hangup hangup = new Hangup();
        hangup.setReason("busy");

        Dial dial = new Dial();
        Number number ;
        User sipUser ;
        // if invoked as a hangup_url send a text response
        if ( !hangupCause.isEmpty() ) {
            System.out.println("SIP Route hangup callback");
        }
      
        try {
            // if no destination nunber is available, hang up.
            if ( toNumber.isEmpty() ) {
                System.out.println("SIP Route cannot identify destination number");
                plivoResponse.append(hangup);
            } else {
                if ( toNumber.length() > 4 && toNumber.substring(4) == "sip:" ) {
                    isSipUser = true;
                }
                // check for outbound call disable status - all, sip or number
                if ( isSipUser && disableCall == "all" || disableCall == "sip" ) {
                    System.out.printf("SIP Route calling sip user is disabled : %s", disableCall);
                    plivoResponse.append(hangup);
                } else if ( !isSipUser && disableCall == "all" || disableCall == "number" ) {
                    System.out.printf("SIP Route calling number is disabled : %s", disableCall);
                    plivoResponse.append(hangup);
                } else {
                    System.out.printf("SIP Route dialing %s", toNumber);
                    if ( !dialMusic.isEmpty() && !isSipUser ) {
                        dial.setCallerName(callerName);
                        dial.setCallerId(fromNumber);
                        dial.setDialMusic(dialMusic);
                        number = new Number(toNumber);
                        dial.append(number);
                    } else if ( dialMusic.isEmpty() && !isSipUser ) {
                        dial.setCallerName(callerName);
                        dial.setCallerId(fromNumber);
                        number = new Number(toNumber);
                        dial.append(number);
                    } else if ( !dialMusic.isEmpty() && isSipUser ) {
                        dial.setCallerName(callerName);
                        dial.setCallerId(fromNumber);
                        dial.setDialMusic(dialMusic);
                        sipUser = new User(toNumber);
                        dial.append(sipUser);
                    } else if ( dialMusic.isEmpty() && isSipUser ) {
                        dial.setCallerName(callerName);
                        dial.setCallerId(fromNumber);
                        sipUser = new User(toNumber);
                        dial.append(sipUser);
                    }
                    plivoResponse.append(dial);
                }
            }
        } catch (PlivoException e) {
            System.out.printf("Error while generating XML - ", e.getLocalizedMessage());
        }
            
        System.out.println(plivoResponse.toXML());
        response.addHeader("Content-Type", "text/xml");
        response.getWriter().print(plivoResponse.toXML());
    }

    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 directDialApp()),"/direct-dial/");
        server.start();
        server.join();
    }
}

Step 3: Test your server

After you've deployed your server, let's make sure that everything is working before we move on to the next step.

  1. Load the URL of your server application address into a web browser.
  2. If you see the XML below, then congratulations! your server has been deployed successfully.
  3. Note: the XML output below is the most basic response that you will receive. Each application that you will build in the tutorial will have different XML outputs depending on the logic and instructions that have been given to Plivo. For more details on how XMLs work, visit the XML reference page


1
2
3
<Response>
<Hangup/>
</Response>

Next Steps

Learn how to Make an Outgoing Call with a Browser.

  1. Make a Call With a Browser to a Phone Number
  2. Make a Call With a Browser to a SIP Endpoint
  3. Hangup the Call
  4. Manually Input Destination Phone Numbers
  5. Pre-program Destination Phone Numbers (Click-to-call)
  6. Modify the Login Interface
  7. Receive Incoming Calls
  8. Create Dynamic SIP Endpoints