IVR Module

Overview

The IVR module can initiate outbound IVR calls and receive call status from IVR vendors. Text templates, typically CCXML/VXML, are stored in Template entities. Specifics about vendors are stored in Config entities.

/status REST request

IVR Call status information is sent to the IVR module by the IVR provider using REST calls, typically using CCXML’s <send> element or VXML’s <data> element. The IVR module stores the call status information in CallDetailRecord entities (CDR) in the database.

REST call

The IVR provider makes a GET or POST HTTP call to Motech & the IVR module:

http://{motech-server}/{motech-war-file}/module/ivr/status/{config}

Where {motech-server} is the address of the Motech server, {motech-war-file} is the name of the Motech war file, by default motech-platform-server and {config} is the name of the configuration representing the IVR provider.

The GET or POST parameters provide Motech with the information about the IVR call status. The following parameters are interpreted by Motech:

from:is the phone number making the call.
to:is the phone number receiving the call.
callDirection:should be either INCOMING or OUTGOING. Any other string will be added to the providerExtraData map and callDirection will be UNKNOWN.
callStatus:is the status of the call.
motechCallId:if available (ie: included as a parameter in the outgoing call HTTP call), the Motech originated GUID uniquely identifying this call.
providerCallId:if available, the provider’s call id. For example, CCXML’s session.id would be a good choice.
timestamp:if provided, a string representing the time at which the status is being sent, if not, the time at which the status is being received by Motech.

Note

Any parameters not recognized in the list above will be added to the providerExtraData map. Parameters listed in the config’s ignoreStatusFields list are ignored. Should the IVR provider not be able to name some of the REST call parameters, the config’s statusFieldMap can be used. Call status from the IVR provider can be mapped to other value. To map the values sent by the IVR provider to the custom values, the config’s callStatusMapping can be used.

Each successful REST call results in one new CallDetailRecord database record.

Motech Event

In addition to storing CDR in the database, the IVR module also sends a org.motechproject.event.MotechEvent for each IVR Call Status REST call:

subject:ivr_call_status
payload:TIMESTAMP, CONFIG, FROM, TO, CALL_DIRECTION, CALL_STATUS, MOTECH_CALL_ID, PROVIDER_CALL_ID, PROVIDER_EXTRA_DATA

Motech Task Trigger

The ivr_call_status event subject is also a Motech Task Trigger.

/template REST request

Text [1] template requests are sent to the IVR module by IVR providers using REST calls, typically using VXML’s <submit> element. Apart from returning text templates, this REST request works exactly the same as the /status REST request, it stores CallDetailRecord entities, sends Motech events and triggers Motech Tasks.

[1]Text or CCXML or VXML or anything really. Some non VXML providers like, for example, India’s KooKoo, operate the same way except the templates are not VXML but generally some sort of proprietary XML.

Velocity

Essentially, Velocity is a template engine which enables you to dynamically return text. Here’s a simple template named hello:

Hello, $world.

Assuming Motech is running on your local machine and you created an IVR config named voxeo, you can get the IVR module to return the template by CURLing:

$curl -w "\n" "http://localhost:8080/motech-platform-server/module/ivr/template/voxeo/hello"
Hello, $world.
$

Nothing special, eh? But now add a query parameter named world with some value:

$curl -w "\n" "http://localhost:8080/motech-platform-server/module/ivr/template/voxeo/hello?world=Frank"
Hello, Frank.
$

All query parameters are available. But wait, there’s more! Read on...

The $dataServices element

In addition to query parameters, the special element $dataServices [2] is available inside your templates. It can be used to query the database using the following methods:

  • findOne(entityClassName, lookupName, params): returns one entity instance
  • findMany(entityClassName, lookupName, params): returns a list of entity instances
  • count(entityClassName, lookupName, params): returns a number of entity instances
  • retrieveAll(entityClassName): returns all instances of an entity
  • countAll(entityClassName): returns the number of all instances of an entity
[2]Using a query parameter named $dataServices is not a good idea and will produced undefined results.
The methods above use the following arguments:
entityClassName:
 the fully qualified class name for that entity, for example for a DDE [3] org.motechproject.ivr.domain.CallDetailRecord or for a EUDE [4] named Patient : org.motechproject.mds.entity.Patient
lookupName:the name [5] of the lookup to use
params:a map containig zero or more key:value pairs corresponding to the arguments required by the given lookup, see how to use a map in the following sample template.
[3]Developer Defined Entity
[4]End User Defined Entity
[5]Don’t confuse the lookup name (ie: ‘Find by name’) with the lookup method name (ie: ‘findByName’).

So, let’s say, for example, we created a Patient MDS entity with a name and a number field and a ‘Lookup by Number’ lookup which takes a number argument. The following template would extract the name of the patient whose number is ‘123’:

Hello, $dataServices.findOne("org.motechproject.mds.entity.Patient", "Lookup by Number", {"number" : "123"}).name

Injecting custom services

Not only the $dataServices element can be used in IVR templates. It is also possible to inject any arbitrary OSGi service into the Velocity context. All services configured in the servicesMap field of the configuration field will be available to the template executed with that configuration. All these services will be injected as variables, so for example if your configuration is as follows: myService:org.example.service.MyService, then org.example.service.MyService will be available as $myService in the Velocity template.

REST call

The IVR provider makes a GET or POST HTTP call to Motech & the IVR module:

http://{motech-server}/{motech-war-file}/module/ivr/template/{config}

See /status REST request for additional details.

Motech Event

The event sent is similar to that in /status REST request with two exceptions: the subject is ivr_template_request and the event payload contains an additional template element which contains the name of the requested template.

Motech Task Trigger

The Motech Task Trigger is also similar to that in /status REST request with the same two exceptions as above, a different title and an additional element, you guessed it: the template name, to the payload.

Initiating Outbound Calls

To initiate an outbound call from an IVR provider, the IVR makes a call to the IVR provider. The following two parameters are required:

configName:the name of the IVR provider config where outgoingCallUriTemplate specifies the IVR provider outbound call URI and jsonRequest specifies the HTTP POST method format
params:the parameters needed by the IVR provider to make the call, eg: destination number, resource id, status callback URI, security credentials, etc...

The call to the IVR provider is constructed by using the config’s outgoingCallUriTemplate field as the base URI, substituting any [xxx] placeholders with the values in params. For HTTP requests, if jsonRequest is selected params are converted into JSON Object otherwise params are added to the HTTP request parameters.

The IVR module currently supports two URI protocols in outgoingCallUriTemplate:

http:in this case the IVR module will initiate calls by making REST calls to the IVR provider, under the specified address and include the parameters as GET parameters or POST body
file:in this case the IVR module will initiate calls by generating call files with properties, in the specified location. The IVR provider is supposed to pick up those files and initiate call based on the passed properties

There are three ways to have the IVR module initiate a call.

Initiating an outbound call via an API call

Module writers can use the org.motechproject.ivr.service.OutboundCallService initiateCall method.

initiateCall method can be called without the configName parameter. In this case call will be initiated with the default configuration.

Initiating an outbound call via a REST call

GET or POST HTTP call to: http://{motech-server}/{motech-war-file}/module/ivr/call/{config}

Where {config} is used for configName and the HTTP query parameters are used for params

Note

The default security rules for the /call http endpoint are USERNAME_PASSWORD.

Initiating an outbound call via the tasks

Create a task where the action is IVR - Initiate Call. Use the UI to specify the config and params parameters:

IVR Module - Initiate outbound call via the Tasks Module - UI

Settings

IVR provider Configs are created in the Settings tab. Click on Modules / IVR / Settings:

IVR Module - Settings

Configs consist of:

  • name: The config name

  • authenticationRequired:

    Select if the IVR provider requires authentication headers when initiating outbound calls.

  • username:

    Optional username for providers that require authentication.

  • password:

    Optional password for providers that require authentication.

  • outgoingCallMethod: Which HTTP method to use, either GET or POST.

  • jsonRequest: Select if the IVR provider requires HTTP POST method using JSON format.

  • statusFieldMap:

    A map where each key corresponds to a field name coming from the IVR provider and each value corresponds to the matching IVR CallDetailRecord field.

  • callStatusMapping:

    A map where each key corresponds to a status from the IVR provider and each value corresponds to the status which will be used in CDR log. For example to map status “13” from the IVR provider to “Subscriber not reachable” this field must contain a pair 13: Subscriber not reachable.

  • outgoingCallUriTemplate:

    A URI template where any [xxx] string will be replaced by the value identified by the xxx [6] key in the provided params map. Additionally, the entire params map is added to the parameters of the call. Supported URI protocols are http:// (generates REST call) and file:// (generates file)

  • ignoredStatusFields:

    A list of fields to be ignored when receiving IVR Call Status from the provider. All other fields received during IVR Call Status and not mapped to CDR fields are added to the providerExtraData CallDetailRecord map field.

  • servicesMap:

    A map (in the “key1: value1, key2: value2” notation) of services that can be injected in Velocity templates where key is the name used in Velocity and value is the class of the OSGi service, for example to inject org.motechproject.mds.service.EntityService as entityService, use entityService: org.motechproject.mds.service.EntityService

  • jsonResponse:

    Select if the provider returns JSON data after placing an outbound call.

[6]Note: no square brackets

A configuration can be set as the default from the UI and in the ivr-config.conf file. The default configuration is marked with “star”. After creating first configuration it will be marked as default. It can be changed by clicking “Set Default” button in current selected configuration.

Call Detail Records

Like configs, CallDetailRecord fields are viewed using the data_services Data Browser:

IVR Module - Editing an existing config

Call Detail Records consist of:

  • timestamp: The time at which the event happened, if not supplied by the provider, then supplied by the IVR module.
  • configName: Name of the config that this CDR pertains to.
  • from: Phone number which originated the call.
  • to: Phone number which received the call.
  • callDirection: INBOUND or OUTBOUND, relatively to the IVR module. Or UNKNOWN.
  • callStatus: The status of the call. It depends on the IVR provider and on the used settings.
  • templateName: The name of the requested template. Only for /template requests.
  • motechCallId: A Motech (ie IVR Module) generated GUID uniquely identifying a call.
  • providerCallId: An IVR provider generated identifier, useful to query the provider (who generally has some kind of a web interface) about a specific call.
  • providerExtraData: A map containing any additional parameter received from the IVR provider and not mapped to any of the above fields.

Custom exception

There is a custom exception class IvrTemplateException that can be thrown from templates. Using this exception you can control the error code in the HTTP response. In the following constructor you can provide a suitable error code:

public IvrTemplateException(String message, HttpStatus errorCode) {
    super(message);
    this.errorCode = errorCode;
}

This will allow you to return an error code that your IVR provider can understand.

Using IVR module with IVR providers

IVR module supports multiple IVR providers. For more information on how to integrate them with the module, please visit the links below.