Using the Hangouts Telephone API

Have you ever wanted to create a Hangout app that connects its users to a phone line? You could do all sorts of  interesting things like bridge users to a help line or could make an app that would let users manage participants who are added over the phone…

The Google+ Hangouts Telephone API lets you do just this: place and manage outbound calls from Google+ Hangout apps. The API is great for performing various activities where a phone number is automatically added to a call.

In a basic example, you could have the Hangout dial a number when the Hangout begins, or could trigger a phone call through user interaction in the Hangout. In more complicated use cases, you might even want to also then send a series of phone tones over the line to the call at certain timings. This could open up a number of new possibilities ranging from letting users create DTMF music (e.g. “Funky town” on touch tones) to managing an automated calling system. The following video shows the demo:

You can get going easily by following the Quickstart instructions bellow and a full example with Twilio / Google+ hangout integration is provided, the Google+ Telephone Hangout Demo.

I have also added a start hangout button to let you launch the app on your own.

Quickstart: A basic app using the Telephone API

The following HTML represents a very basic Hangout app using the Telephone API (telephoneHangout.html):

<script src="//hangoutsapi.talkgadget.google.com/talkgadget/apps/gadgets/js/rpc.js"></script>
<!-- set to developer channel -->
<script src="//plus.google.com/hangouts/_/api/dev/hangout.js"></script>
<body>
  <script>
  function init() {
    // When API is ready...
    gapi.hangout.onApiReady.add(function() {
      try {
        console.log("started");
        var phoneNumber = gapi.hangout.getStartData();

        // You should perform validation of gd, but for now, ignore.

        console.log(phoneNumber);
        document.getElementById("outputArea").innerHTML =
            "Dialing: " + phoneNumber + " based on initial data...";

        gapi.hangout.telephone.beginCall(phoneNumber);
      } catch (e) {
        console.log('Error:');
        console.log(e.stack);
      }
    });
  }

  // Add startup listener immediately. If you need an
  // OAuth 2.0 access token, your startup will be different.
  init();
  </script>
  <div id="outputArea"></div>
</body>

To configure and run the app:

So this is great, you can now place calls outbound from your hangout. How about we make it trickier!  Let’s now pass tones to the number we’re dialing to perform various actions after the number is dialed.

Setting up a Twilio project for your fake call tree

Let’s create a call tree using the Twilio API. The tree will first prompt for a conference number then later will prompt for a participant code. The goal here is to pass extra values passed to the app in time for the tree to correctly handle them. I will not go into much detail for the Twilio API, their documentation is fantastic if you haven’t used it before. First, create the handler to receive the call [tapi.php]:

<?php
  header("content-type: text/xml");
  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
?>
<Response>
  <Say>Let's do something using the Hangout Telephone API</Say>
  <Gather numDigits="5" action="magic.php" method="POST">
    <Say>What's the magic tone?</Say>
  </Gather>
</Response>

Next, another handler to accept a code [magic.php]:

<?php
  // if the caller pressed anything but 1 send them back
  if($_REQUEST['Digits'] != '12345') {
      header("Location: fail.php");
      die;
  }

  header("content-type: text/xml");
  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
?>
<Response>
  <Say>Very good.</Say>
  <Say>Now for the PIN.</Say>
  <Gather numDigits="4" action="magic2.php" method="POST">
  </Gather>
</Response>

A final handler for accepting a pin [magic2.php]:

<?php
  // if the caller pressed anything but 1 send them back
  if($_REQUEST['Digits'] != '7890') {
      header("Location: fail.php");
      die;
  }

  header("content-type: text/xml");
  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
?>
<Response>
  <Say>You just sunk my battleship</Say>
  <Play>http://wheresgus.com/tapi/tada.mp3</Play>
</Response>

I also created a handler that will send a failure message, that is included in the project zip archive. If you have configured the project correctly and the Twilio API can see your PHP pages, you will now be able to dial your number, then punch in the first set of example tones (12345), punch in the second set of correct example tones (7890), and will then get the success sound.

Bringing it all together in the Hangout app

You’re now ready to automate using the Hangout app. You will have to first have a good way to pass data to your application and next will need to send the data programatically to the call.

Passing JSON data to the Hangout

Because we’ll be performing additional operations, it’s convenient to pass data as JSON to the Hangout app. The following URL shows you how to pass in a phone number and various additional values to the app: https://hangoutsapi.talkgadget.google.com/hangouts/_?gid=521280096254&gd={“number”: “2065551212”, “confCode”: “12345”, “leadPin”: “7890”} Now, when the app sees the JSON, it must parse it into an object:

    // Global to store the start data and phone call
    var gPhone;
    var gCall;
    gapi.hangout.onApiReady.add(function() {
      try {
        console.log("started");
        var startData = gapi.hangout.getStartData();
        var phoneNumber = JSON.parse(startData);

        gPhone = phoneNumber;

        // You should perform validation of gd, but for now, ignore.
        gCall = gapi.hangout.telephone.beginCall(phoneNumber.number);
      } catch (e) {
        console.log('Error:');
        console.log(e.stack);
      }
    });

At this point, you can pass additional commands to the call by making an API call to gCall.sendTone and have a record of various codes to pass to the phone.

Sending tones to the call

When you send tones to the call, you must pass them digit by digit as opposed to just passing a long string of values. This is done to give the developer full control of when the tones are sent. The following example function will send the global codes passed in the hangout start data to the call:

  function sendCodeTones(){
    console.log("Sending: " + gPhone.confCode);

    // Send numbers in sequence
    for (var i=0; i < gPhone.confCode.length; i++){
      setTimeout("gCall.sendTone(gPhone.confCode[" + i + "])", i*250);
    }
  }

In my case, I’m using 250 ms delays between sending the tones, you could use longer or shorter values based on your PSTN.

The final implementation

Let’s look at a complete  hangout app that will call, send tones when the call is ready to receive them, and then sends a second set of tones after that.

<script src="//hangoutsapi.talkgadget.google.com/talkgadget/apps/gadgets/js/rpc.js"></script>
<!-- set to developer channel -->
<script src="//plus.google.com/hangouts/_/api/dev/hangout.js"></script>
<body>
  <script>
  var gPhone;
  var gCall;
  function init() {
    // When API is ready...
    gapi.hangout.onApiReady.add(function() {
      placeCall();
    });
  }  

  function placeCall(){
    console.log("started");
    var startData = gapi.hangout.getStartData();
    console.log(startData);

    var phoneNumber = JSON.parse(startData);
    console.log(phoneNumber);

    gPhone = phoneNumber;

    // You should perform validation of gd, but for now, ignore.

    document.getElementById("outputArea").innerHTML =
        "Dialing: " + phoneNumber.number + " based on initial data...";

    gapi.hangout.telephone.onCallInitiated.add(callInitiatedEventHandler);    
    gCall = gapi.hangout.telephone.beginCall(phoneNumber.number);
  }

  function sendCodeTones(){
    console.log("Sending: " + gPhone.confCode);

    // Send numbers in sequence
    for (var i=0; i < gPhone.confCode.length; i++){
      setTimeout("gCall.sendTone(gPhone.confCode[" + i + "])", i*250);
    }

    setTimeout("sendPinTones()", 5000);
  }

  function sendPinTones(){
    console.log("Sending: " + gPhone.leadPin);

    // Send numbers in sequence
    for (var i=0; i < gPhone.leadPin.length; i++){
      setTimeout("gCall.sendTone(gPhone.leadPin[" + i + "])", i*250);
    }
  }

  function callInitiatedEventHandler(call){
    call.callInformation.onCallStateChanged.add(callStateChangedEventHandler);       
  }

  function callStateChangedEventHandler(callState){
    console.log("call state changed to:");
    console.log(callState.newState);

    if (callState.newState == gapi.hangout.telephone.CallState.CONNECTED){
      setTimeout("sendCodeTones()", 12000);
    }
  }

  // Add startup listener immediately. If you need an
  // OAuth 2.0 access token, your startup will be different.
  init();
  </script>
  <div id="outputArea"></div>
</body>

Now, when you run the app, you can pass in your Twilio phone number, start the call, pass in two sets of configurable codes, and will have everything automated within the hangout. As an example, the following code will initiate the call with my Twilio number and the appropriate codes: https://hangoutsapi.talkgadget.google.com/hangouts/_?gid=521280096254&gd={“number”: “(253) 214-3890”, “confCode”: “12345”, “leadPin”: “7890”} All of the project files used in the demo are available in the Hangout demo app zip. Have fun!

Additional Considerations

Passing the data as raw JSON in a GET request could cause issues if the user had a bad copy/paste and also could reveal information within the request. You could make the application more secure by passing an identifier  rather than the raw JSON data to the application for the conference.  The application would communicate with your server to retrieve the dial-in information securely based on an alternative value passed as start data to the Hangout app.