tropo

Tropo, Inc. offers a platform and APIs that enable developers to quickly build and deploy communications applications. By combining voice, video, messaging and real-time presence, our platform accelerates innovation and creates new revenue opportunities

Introduction

Tropo, Inc. offers a platform and APIs that enable developers to quickly build and deploy communications applications.  By combining voice, video, messaging and real-time presence, our platform accelerates innovation and creates new revenue opportunities for carriers and their partners.

The communications industry is being actively re-invented.  Skype and Google Voice are changing user behavior, while services such as Tropo provide developers with a simple API that adds voice and SMS to their existing web applications.  At the same time, Apple’s App Store has radically changed how we consume content, applications and services; creating an effective model for connecting developers with billions of potential users.  The result has been an explosion of innovation with new value being created for all involved.

Our mission is to empower carriers to realize new services and quickly take them to market.  To accomplish this goal, we designed Tropo.js to bring the innovation and monetization potential of the App Store into the carrier’s core network.

require('tropo').on('subscribe', function(event) {
    event.subscriber.on('call:connected', function(event) { 
        var msg = "Hello world";
        console.log(msg);
        event.call.say(msg);
    });
});

Design Goals

Tropo.js is a high-level API for writing applications that run on telephone calls inside a carrier's core network. It's designed to be a simple alternative to the traditional methods of writing IMS (the platforms that run a carrier's core network) applications using low level SIP APIs.  The primary design goals include the following:

  • Events - The event-driven nature of Node.js fits quite well with telephony and real-time communications.  The goal of the Tropo.js API is to model communications as a natural series of real-time events.
  • Long running apps - A running Tropo app receives network events on behalf of a Telco user (subscriber).  For a given subscriber, events such as incoming calls and changes in location are all routed to the same instance of the Node.js application, therefore allowing the developer to maintain a bit of state and give them the ability to orchestrate multi-modal conversations without requiring external synchronization.
  • Calls are key but not the entire story - Unlike Tropo, the Tropo API provides primitives for modeling each leg of a call, referred to as ‘connections.'  Connections will play a larger role as the API evolves because there are ways to combine connections without requiring a call.  Imagine an always-on video security system or a baby monitor.  Those audio/video streams can be captured, combined, mixed, etc.  Is this a call?  Probably not.
  • Idiomatic Node - The Tropo API is packaged as a Node.js module and as such should follow the conventions of the runtime’s native libraries.  For example, the use of EventEmitter and standard events, such as ‘end’ and ‘error’ are present throughout the API.

Installation

It is recommended to install the node module globally on development machines. This allows the Tropo Command Line Interface that is included to be executed without including a path. See below for information on the Command Line Interface.

npm install tropo -g

The -g instructs NPM to install Tropo as a global (available system wide) package.

NOTE: Installing NPM packages globally may require root or sudo access on your system.

The Tropo Command Line Interface (CLI) is installed during SDK installation. If installed globally, the CLI will be added to the system path. If the SDK is installed as a local module (by omitting the -g switch in the NPM install command), the CLI will also be installed in the current working directory and will not appear in your path. For convenience, the CLI directory can be added to the system path. To do so, add {working-directory}/tropo/bin to the system path.

Usage

Speed dialing: when I call "123" or other short codes, translate that to the correct number or SIP address.

 var tropo = require('tropo'); 
 tropo.on('subscriber', function(event) {
   var subscriber = event.subscriber;
   return subscriber.on('call:outgoing', function(event) {
         var call = event.call;
         var codes = {'123':'tel:+16505551212', '456':'sip:alice@voipango.voip'};
     return call.connect(codes[call.to]);
   });
 });

Access log the caller ID of the incoming call

 var tropo = require('tropo'); 
 tropo.on('subscriber', function(event) {
   var subscriber = event.subscriber;
   subscriber.on('call:incoming', function(event) {
     console.log('call from: ' + call.to);
   });
 });

Record all your calls

 var tropo = require('tropo');  
 tropo.on('subscriber', function(event) {
   var subscriber = event.subscriber;
   return subscriber.on('call:connected', function(event) {
      var call = event.call;
            var recording = call.record({
               format: 'mp3',
               startBeep: true,
               startPaused: false,
               maxDuration: 500,
               initialTimeout: 10,
               finalTimeout: 30
            }).
            on('start', function(event) {
               console.log('Recording has started');
            }).
            on('end', function(event) {
               // save recording to a file
               var istream = event.recording.createReadStream();
               var ostream = fs.createWriteStream('/tmp/foo.mp3');
               istream.pipe(ostream);
            }).
            on('error', function(event) {
               console.log('Error while recording: ' + event.error);
            });
        }
}

API Specification

The primary object in the Tropo SDK, the Tropo Object, offers basic platform version information and EventEmitter facilities for handling new app subscriptions.

The Tropo Object is a singleton and may be accessed by requiring the tropo module.

var tropo = require('tropo');

String indicating the Tropo SDK version

Indicates that the current app has received a new subscription. The event contains a Subscriber Object representing a user of the underlying communications network.

The Subscriber object has several properties:

  • id: the address of the subscribers
  • number: same as id
  • config: a JSON object that contains configuration data that was provided when the subscription was specified in the provisioning system; if no configuration was given, config has the value of null.
  • permissions: the permissions as JSON array of strings (e.g., ["CALL_LISTEN", "CALL_WHISPER"]) that the subscriber has given to the application. If no permissions have been given, permissions has the value of [].

This event indicates that the subscriber with the given address was un-subscribed.

Represents a user of the the underlying communications network and is the source of most events.

var tropo = require('tropo');
 
tropo.on('subscribe', function(event) {
    var subscriber = event.subscriber;
    subscriber.on('call:incoming', function(event) {
        console.log('Incoming call', event.call);
    })
    .on('call:outgoing', function(event) {
        console.log('Outgoing call', event.call);
    })
    .on('call:end', function(event) {
        console.log('Call ended', event.call);
    })
    .on('call:connected', function(event) {
        console.log('Call connected', event.call);
    }); 
});

String representing the unique identifier for the subscriber

String representing the address of the subscriber

String representing the subscriber configuration object

Set of subscriber permissions. The possible values are:

  • CALL_OFFER: App may access incoming/outgoing calls
  • CALLER_ID: On an incoming call, App may see the name/address of the caller
  • CALL_RECORD: App may record the call
  • CALL_RING_LIST: App may manipulate the destination on a call
  • CALL_WHISPER: App may play announcement on the call
  • CALL_LISTEN: App may listen some input on the call
  • SET_CALLER_ID: App may set the caller id

Indicated that the subscriber is receiving an incoming call. This event fires before the subscribers' devices start ringing, allowing the app to change the outcome of the call. Emits a Call Object that may be used to change the outcome of the call by rejecting it or changing the subscriber devices that will ring.

As previously mentioned, this event handler provides the app with an opportunity to change the outcome of the call. If the app does nothing to modify the call (reject, redirect, etc.) the default behavior is to simply ring the subscriber’s phone when returning from this event handler. If the application requires more time or needs to perform an async operation, the Call Object provides an intercept function to do exactly that.

Indicated that the subscriber is placing an outgoing call. This event fires before the call is sent through the phone network, allowing the app to change the outcome of the call. Emits a Call Object that may be used to change the outcome of the call by rejecting it or by changing the target address.

As previously mentioned, this event handler provides the app with an opportunity to change the outcome of the call. If the app does nothing to modify the call (reject, redirect, etc.), the default behavior is to simply ring the target number when returning from this event handler. If the application requires more time or needs to perform an async operation, the Call Object provides an intercept function to do exactly that.

A shortcut event indicating that a call just ended. Emits the Call Object of the completed call. Handling this event has the same effect as registering an end listener for all subscriber calls.

Shortcut event triggered when either an incoming or outgoing call is answered and the two parties are connected. This event will fire only when the second connection is made. See the Connection API for more details.

The Call object models a session among two or more parties.

subscriber.on('call:incoming', function(event) {
    var call = event.call;
    console.log('Call ' + call.id + ' ' + call.direction + ' from ' + 
        call.from + ' to ' + call.to);
 
    // Prevent automatic call completion so we can 
    // check a web service to see what to do with the call
    call.autoConnect = false;
 
    // Check a web service to see what to do
    service.getCurrentMode(function (response) { 
        switch (response.mode) {
            case 'reject':
                call.reject();
                break;
            case 'redirect':
                call.connect(response.redirectTo);
                break;
            default:
                call.connect();
                break;
        }
    });
 
    call.on('connection:ringing', function(event) { 
        var connection = event.connection;
        console.log('Connection is ringing  address ' + event.address);
    })
    .on('connection:connected', function(event) { 
        var connection = event.connection;
        console.log('Connected to ' + connection.address);
    })
    .on('connection:disconnected', function(event) { 
        var connection = event.connection;
        console.log('Connection to ' + connection.address + ' terminated');
    })
    .on('end', function(event) { 
        console.log('Call ' + call.id + ' terminated');
    })
    .on('error', function(event) { 
        console.log('Call ' + call.id + ' had error ' + event.error);
    });
 
});

String representing the unique identifier for the call

String indicating who initiated the call. Values are ‘in’ for an incoming call and ‘out’ for outgoing.

String representing the original target address for the call. For incoming calls this will be one of the subscriber’s addresses, and for outgoing calls this will be the original number dialed by the subscriber.

String representing the address that initiated the call. For incoming calls this will be the other party’s address (Caller ID), and for outgoing calls this will be the subscriber's address.

An Array containing active Connections on the call. A call starts with a single connection (the initiator) and as other endpoints are joined, the connections property will be updated to reflect the addition (See Event: connected).

Boolean indicating whether the framework should complete the incoming or outgoing call when returning from the ‘call:incoming’ or ‘call:outgoing’ event handlers. Setting this property to true before returning from one of those event handlers will result in the call being held until the app decides what to do. The default for this value is true. NOTE: It’s critical that the application decide what to do with the call in a timely manner. To enforce this, most Tropo deployments will mark any app taking longer than 5 seconds as unresponsive and that application will no longer be able to participate in the call.

The authentication token used to send request to the Tropo server on behalf of this call. The Tropo SDK will inject this token automatically so normally you will not be using it.

Rejects an incoming or outgoing call. This function is only valid when the call is in a pre-connected state (call.connections.length == 1). Calling this function while connected will raise an error.

Accepts a String or an Array of addresses to invite into the call; the first endpoint to answer gets connected and the rest are canceled. This function returns a Connection Object representing the participant that is being invited into the call. When multiple participants are passed as parameters the connection's address property will be updated with attributes of the participant that answers the call. Optionally a set of options can be passed. The available options are the following:

  • bridge Boolean. Whether the Tropo AS should bridge the destination target or not. The devault value is false.
  • callerId String. A desired caller id to set in the call. You need the SET_CALLER_ID permission to make use of this option. The devault value is null.

Adds a new Connection to the call. Connections represent participants of the call that can be a mix of real people or audio/video devices adding media into the call. There are two stages in the call where connections can be added: Offer Phase and Post-Connection Phase.

The Offer Phase is handled via the ‘call:incoming’ or ‘call:outgoing’ event handlers. During this phase, the initiator has dialed a phone number or address and is waiting for the call to connect. In this phase, the application can override the dialed address by calling Call.connect() before returning from the event handler or after performing some async operation if Call.autoConnect is false.

Once the call is established (post-connection phase), invoking this function will simply add to the connection list by inviting those endpoints to participate.

Currently, connections are added by address. Future versions of Tropo may allow combining existing connections by ID or some other means allowing ad-hoc conferences to be created and for two existing calls to be ‘merged’.

Disconnects the call and all the connections associated with it.

Begins recording the call. This function accepts an object whose properties will determine the recording format, various timeouts, etc.

  • format String. The format/encoding to use when recording [‘mp3’, ‘wav’]
  • startBeep Boolean. Determines if beep will be played to the use when recording begins
  • maxDuration Integer. The maximum duration of the recording in seconds
  • initialTimeout Integer. The amount of silence (in seconds) at the beginning of the recording indicating that the user will not provide input. Defaults to 0 (no timeout).
  • finalTimeout Integer. The amount of silence (in seconds) indicating that the user has completed providing input. Defaults to 0 (no timeout).
  • expireAfter Integer. The amount of time (in seconds) after which the recording will expire in the server and will be deleted. Defaults to null (no expire).
  • expireAfterFetch Boolean. Determines whether the recording will expire after it has been fetched from the server. If set to true, the recording will be removed from the server after it has been transfered. Defaults to null (no expire).
  • to String. An optional destination file name that can be used to control recording file naming. When provided, Ameche AS will try to use that filename as destination. In case of name collision the Ameche AS might alter the file name when required. Defaults to null

This function returns a Recording Object that emits lifecycle events and functions for controlling the recording session. (@see Recording Object)

Plays audio to everyone on the call. Arguments are Strings in one of three formats

  • Plain Text - Will be spoken as TTS in the default voice/language.
  • URL - The data returned by fetching the URL is treated as an audio file. The audio format is discovered via file extension or Content-Type directive.
  • SSML - If the String begins with <speak>, this will be treated as SSML and delivered as is to the underlying voice engine.

Optional parameters:

  • repeat Boolean. Determines whether the prompt should be repeated until the say verb is stopped or the call ends. Defaults to false (no repeat).

This function returns a Say Object that emits lifecycle events and functions for controlling the announcment. (@see Say Object)

Event triggered when an associated connection has been created in response to an invocation of Call.connect() (@see Call.connect())

Shortcut event triggered when an associated connection has been accepted by the underlying communication network and has begun ringing (@see Connection Event:ringing).

Shortcut event triggered when an associated connection has been successfully established (@see Connection Event:connected).

Shortcut event triggered when an associated connection has been successfully established (@see Connection Event:connected).

Triggered when the last connection has been disconnected and the call is over.

Fired when an error occurs on the call.

The Connection object represents a leg of a call. Once established, it is associated with a single call participant.

call.on('connection:created', function(event) { 
    var conn = event.connection;
    console.log('Connection with state ' + conn.state 
        + ' has ringlist ' + conn.ringlist);
 
    conn.on('ringing', function(event) {
        console.log('Connection is ringing address ' + event.address);
    })
    .on('connected', function(event) {
        console.log('Connection established to ' + event.address);
 
        // We're connected, now hang up
        conn.disconnect();
    })
    .on('disconnected', function(event) {
        console.log('Connection broken to ' + conn.address);
    })
    .on('error', function(event) { 
        console.log('Connection had error ' + event.error);
    });     
});

The address for this connection. When the connection object is a result of a ringlist operation ( a connect command with multiple targets ) then this property will stay in undefined state until one of the target participants answers. Then, connection's address property will be set to that participant's address.

String representing the current state of the connection. Possible values are: initial, ringing, connected, disconnected

Array containing the remote addresses being invited to join the call

Immediately disconnects the connection and removes any reference to it from associate Call.connections

Plays audio to the call leg. Arguments are Strings in one of three formats:

  • Plain Text - Will be spoken as TTS in the default voice/language.
  • URL - The data returned by fetching the URL is treated as an audio file. The audio format is discovered via file extension or Content-Type directive.
  • SSML - If the String begins with <speak>, this will be treated as SSML and delivered as is to the underlying voice engine.

This function returns a Say Object that emits lifecycle events and functions for controlling the announcment. (@see Say Object)

Starts an IVR operation. This function receives a set of grammar which might be:

  • A String with a Voxeo's simple grammar. For example, "[5 DIGITS]"
  • A String with an URL pointing to a grammar. For example, "http://grammarserver.com/digits?min=1&max=10"
  • A Grammar object with a custom grammar. For example,
<grammar version="1.0" xml:lang="en-US" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.w3.org/2001/06/grammar 
    http://www.w3.org/TR/speech-grammar/grammar.xsd"
    xmlns="http://www.w3.org/2001/06/grammar"> 
 
    <meta name="maxTime" content="4000"/>
    <meta name="minSpeechDuration" content="4000"/>
    <meta name="minVolume" content="10"/>
    <meta name="finalSilence" content="2000"/>
    <meta name="terminate" content="true"/>
 
    <ruleref uri="urn:xmpp:rayo:cpa:dtmf:1"/>
    <ruleref uri="urn:xmpp:rayo:cpa:modem:1"/>
</grammar>

To create a custom Grammar, the Tropo module exports a Grammar object:

    var tropo = require('tropo');
    var grxml = '<grammar....</grammar>';
    var grammar = new tropo.Grammar('application/srgs+xml', grxml);
    ...
    var listen = connection.listen(grammar);

This function returns a Listen Object that emits lifecycle events and functions for controlling the IVR operation. (@see Listen Object)

Triggered when the connection has been accepted and has begun ringing

Triggered when the connection has been successfully established

Triggered when the connection has been disconnected

Fired when an error occurs on the connection.

A Recording object models the operation of recording a call or a single connection.

var recording = call.record({
    format: 'mp3',
    startBeep: true,
    startPaused: false,
    maxDuration: 500,
    initialTimeout: 10,
    finalTimeout: 30,
    expireAfter: 6000,
    expireAfterFetch: true,
}).
on('start', function(event) {
        console.log('Recording has started');
}).
on('end', function(event) {
    // save recording to a file
    var istream = event.recording.createReadStream();
    var ostream = fs.createWriteStream('/tmp/foo.mp3');
    istream.pipe(ostream);
}).
on('error', function(event) {
    console.log('Error while recording: ' + event.error);
});

Read-Only string representing the format/encoding to use when recording [‘mp3’, ‘wav’]

Read-Only boolean determines if beep will be played to the user when recording begins

Read-only float indicating the maximum duration of the recording in seconds

Read-only float indicating the amount of silence (in seconds) at the beginning of the recording indicating that the user will not provide input. Defaults to 0 (no timeout).

Read-only float indicating the amount of silence (in seconds) indicating that the user has completed providing input. Defaults to 0 (no timeout).

Read-only float indicating the duration of the recording in seconds. Defaults to -1 when not set.

Read-only integer indicating the size in bytes of the recording stream

Read-only integer indicating the amount of time (in seconds) after which the recording will expire in the server and will be deleted. Defaults to null (no expire).

Read-only boolean that determines whether the recording will expire after it has been fetched from the server. If set to true, the recording will be removed from the server after it has been transfered. Defaults to null (no expire).

Read-only string that specifies the desired filename for the recording file.

Read-only string that tells the SDK which filename did the Ameche AS used to store the recording file.

Returns a new ReadableStream used to read the recording file.

Immediately stops the recording and will trigger an ‘end’ event.

Instructs the server to delete the recording and invokes an optional callback function with the resposne. Invoking this method is not necessary if expireAfter or expireAfterFetch have been set as in that case the server will do it automatically for us.

Triggered when the recording has started.

Triggered when the recording is complete. The cause attribute indicates what has triggered the end event and it might have the following values:

  • success: The recording ended successfully as expected.
  • stop: The recording ended because a stop command was received.
  • initial_timeout: The recording ended because the initial timeout was exceeded.
  • final_timeout: The recording ended because the final timeout was exceeded.
  • max_duration: The recording ended because the maximum duration was exceeded. In this case there is a recording file with the specified time duration.
  • hangup: The recording ended because the call hung.

Triggered if an error occurred during recording.

A Say object models the playback of audio to one or more parties on a call.

var say = call.say({
    “Hello and thanks for calling”,
    “http://server.com/greeting.mp3
}).
on('end', function(event) {
    console.log('Playback of ' + util.inspect(event.say.tokens) + ' complete');
}).
on('error', function(event) {
    console.log('Error playing audio: ' + event.error);
});

Array of Strings representing the arguments passed to say()

Stops playback of audio

Triggered when playback of audio is complete. The cause attribute indicates what has triggered the end event and it can have the following values:

  • success: The say command ended successfully as expected.
  • stop: The say command ended because a stop command was received.
  • hangup: The say command ended because the call hung.

Triggered if an error occurred during playback of audio.

A Listen object models IVR operations on a connection.

var listen = connection.listen("[5 DIGITS]", {
    mode: 'dtmf',
    terminator: '#',
    initialTimeout: 3,
    interDigitTimeout: 3
}).
 
listen.on('end', function(event) {
    if (event.result) {
            console.log('Result: ' + event.result + '. Utterance: ' + event.utterance);
    } else {
            var cause = event.cause;
        if (cause == 'nomatch') {
            console.log("Could not recognize user's input");
        } else if (cause == 'noinput') {
                console.log('The user did not type anything');
        }
    }
});
 
listen.on('error', function(event) {
    console.log('Error while recording: ' + event.error);
});
 

IVR mode. Can be either 'any', 'dmtf' or 'speech'.

Terminator character used on dtmf input. There is no default value.

Recognizer used for the IVR operation. Default is 'en-us'.

The timeout (in seconds) used to determine that no input will be received. By default, this timeout is disabled. You can set a value to enable it.

The timeout (in seconds) used to determine the end of DTMF input. By default, this timeout is disabled. You can set a value to enable it.

A float value from 0.0 to 1.0 that determines the noise sensitivity of the media recognizer. The default value is 0.5. Increasing this value will make the interpreter more sensitive to quiet input, while decreasing this value will make it less sensitive to quiet input.

A float value from 0.0 to 1.0 that sets the minimum confidence required for the recognizer to recognize the speech based on the grammar. The default value is 0.3. If one were to change this default value to 0.1, then any utterance would be matched, while if it were to be adjusted to 1.0, then virtually every user utterance would be considered a nomatch.

The timeout (in seconds) used to determine the end of Speech input. By default this timeout is disabled. You can set a value to enable it.

Array of Strings representing one of the grammars passed to the listen() method.

The interpretation of the IVR operation. It will only be present if the Listen ends successfully.

An utterance interpretation of the IVR operation. It will only be present if the Listen ends successfully.

Stops the IVR operation.

Triggered upon IVR termination. The cause attribute indicates what has triggered the end event and it can have the following values:

  • match: The listen command ended with a match.
  • nomatch: The listen command did not match call's input.
  • noinput: The listen command ended because there was no input.
  • stop: The listen command ended because a stop command was received.
  • hangup: The listen command ended because the call hung.

Triggered if an error occurred while running the IVR opereation.