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 inTemplate
entities. Specifics about vendors are stored inConfig
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 inCallDetailRecord
entities (CDR) in the database.
REST call¶
The IVR provider makes a
GET
orPOST
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 defaultmotech-platform-server
and{config}
is the name of the configuration representing the IVR provider.The
GET
orPOST
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
orOUTGOING
. Any other string will be added to theproviderExtraData
map andcallDirection
will beUNKNOWN
.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’signoreStatusFields
list are ignored. Should the IVR provider not be able to name some of the REST call parameters, the config’sstatusFieldMap
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’scallStatusMapping
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 storesCallDetailRecord
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 instancefindMany(entityClassName, lookupName, params)
: returns a list of entity instancescount(entityClassName, lookupName, params)
: returns a number of entity instancesretrieveAll(entityClassName)
: returns all instances of an entitycountAll(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] namedPatient
: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 aname
and anumber
field and a ‘Lookup by Number’ lookup which takes anumber
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 theservicesMap
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
, thenorg.example.service.MyService
will be available as$myService
in the Velocity template.
REST call¶
The IVR provider makes a
GET
orPOST
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 additionaltemplate
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 andjsonRequest
specifies the HTTPPOST
method formatparams
: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 inparams
. For HTTP requests, ifjsonRequest
is selectedparams
are converted into JSON Object otherwiseparams
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 theconfigName
parameter. In this case call will be initiated with the default configuration.
Initiating an outbound call via a REST call¶
GET
orPOST
HTTP call to:http://{motech-server}/{motech-war-file}/module/ivr/call/{config}
Where
{config}
is used forconfigName
and the HTTP query parameters are used forparams
Note
The default security rules for the
/call
http endpoint areUSERNAME_PASSWORD
.
Initiating an outbound call via the tasks¶
Settings¶
IVR provider Configs are created in the Settings tab. Click on Modules / IVR / 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, eitherGET
orPOST
.
jsonRequest
: Select if the IVR provider requires HTTPPOST
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 thexxx
[6] key in the providedparams
map. Additionally, the entireparams
map is added to the parameters of the call. Supported URI protocols arehttp://
(generates REST call) andfile://
(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
asentityService
, useentityService: 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:
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
orOUTBOUND
, relatively to the IVR module. OrUNKNOWN
.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.