“For it is the duty of an astronomer to compose the history of the celestial motions through careful and expert study.”
Nicolaus Copernicus
Wikipedia defines Composability as:
Composability is a system design principle that deals with the inter-relationships of components. A highly composable system provides components that can be selected and assembled in various combinations to satisfy specific user requirements.
From a technology standpoint, there are several ways to enable composability, but high on the list are RESTful web services. Web services present a well-defined interface that permits an application to retrieve, create, update, and delete resources. These resources can be anything from a customer record to an IoT device, but one of the beauties of web services is that an application can be unaware of the implementation details of the resource. It can safely view it as a black box that consumes requests and creates responses. Additionally, the application is completely divorced as to where the resource lives. This separation of presentation and plumbing fosters reusability, scalability, and high availability.
Avaya’s Virtual Agent is a cloud-based platform that enables enterprises to quickly spin-up customer experience workflows that can be used for AI-infused self-service and/or act as a front door to a traditional contact center. It’s wrapped in a web design interface that enables enterprises to easily create menu tree interactions for voice and SMS text conversations.
However, Virtual Agent isn’t simply another “press 1 for service” IVR. It natively supports composability through its ability to integrate with third-party cloud services. These services provide Virtual Agent with the intelligence it needs to create unique experiences. Whether the workflow is integrated with a backend SQL database, a CRM system, or a mesh of IoT devices for real-time telemetry (or all three at the same time), Virtual Agent can dynamically change course to meet the caller’s needs.
Making it Happen
How is this accomplished? Every Virtual Agent node has the ability to add a web services integration that can be invoked as the node is entered or before it turns control over to the next node. This allows each node to act on data already present in a workflow or data requested during the node’s execution.
For example, if a node asks the caller for a six-digit account number, the integration used to verify those numbers will wait until the digits have been gathered. Conversely, a node that tells the caller the estimated wait time for a live agent might invoke the integration to the contact center as soon as the node is entered.
Context
Virtual Agent delivers persistent workflow Context to each node. For a voice call, the original Context includes the calling number, called number, and any SIP related information (e.g. SIP domain and SIP address).
Context is not static and grows as the workflow progresses. Each node interaction can add data to the Context. Those new Context items can be used by subsequent nodes. For example, the account number entered in one node is available to all other nodes that might require it.
Additionally, integrations can use and add to the Context. An integration to salesforce.com might place information about the opportunities the caller is associated with into the Context. This data can then be used by the workflow to tailor the customer experience. For instance, opportunity data can instruct the workflow to provide white-glove treatment to priority callers, or route callers to the most appropriate human agent.
Context is stored in a JSON object that passes from node to node, and to integrations. Here are some examples of what you might find in the Context for an incoming PSTN telephone call:
"CallInfo": { "CallSid": "CA777c3e32c91d1e4a42db42f29d8f6803", "SipDomain": null, "CalledParty": "+15553041234", "isSipDomain": false, "CallingParty": "+15556571234" }
The caller pressing the 1 digit will add this to the Context:
"UserInput": { "type": "dtmf", "value": "1", "option": { "id": 0, "name": "payment ivr", "leg_id": 6, "repeat": false, "to_ivr_leg": null, "menu_option": false } }
An integration called from the Virtual Agent node GetCustomerData might add this to the Context.
"GetCustomerData": { "Name": "Andrew Prokop", "Email": "ajprokop@avaya.com", "Priority": Gold" }
After GetCustomerData has added the integrations JSON return value to the Context, the data can be used in a Virtual Agent Condition as:
GetCustomerData.Name
Or a Virtual Agent Prompt as:
{{GetCustomerData.Name}}
Integration Nuts and Bolts
An integration is a RESTful POST web service that is invoked by a Virtual Agent node. It can be written in any programming language and hosted on any computing platform. I have written integrations that run as node.js Express applications on a Linux server as well as microservices on the AWS, Google, and Microsoft Azure clouds. Simple integrations might require something as rudimentary as a PHP script. All that is required from Virtual Agent is a publicly addressable URL.
The message body of the POST contains the Virtual Agent Context and the integration can use any Context data as it sees fit. Caller entered digit can be used for lookups. Context from previous integrations can help tailor a subsequent REST call. The possibilities are endless.
There is no restriction on what an integration can do. It can use SQL to retrieve and store data in a database, web services to invoke cloud services, vendor specific SDKs and code libraries, etc.
When an integration has completed its work, it returns data to Virtual Agent as a JSON object. If “Post response” has been selected by the Virtual Agent node that invoked the integration, that JSON object will be added to the persistent Context.
The following image shows a Virtual Agent node that saves the JSON return value of an integration to the Context. Note that the integration is invoked after the prompt has been played and the digits have been collected.

Pictorially, the connection between Virtual Agent, an integration, and an external service looks as follows:

Putting it All Together
The following code is a node.js integration that retrieves Bitcoin information from Coinbase. Note that the integration retrieves the caller entered input (1, 2, 3, or 4) from UserInput.value. Also note that the JSON response is returned to Virtual Agent in:
response.body = JSON.stringify(virtualAgentResult);
return response;
This particular integration is hosted in AWS as a Lambda function. These microservice platforms (AWS, Google, Azure) allow me to create a Virtual Agent workflow that is entirely cloud-based.
const request = require('request-promise'); const COINBASE_URL = "https://api.coinbase.com/v2/prices/spot?currency="; exports.handler = async (event) => { const response = { statusCode: 200, body: null, } var virtualAgentResult = { amount: null } var jsonBody = JSON.parse(event.body); if (!jsonBody.hasOwnProperty("UserInput")) { response.body = JSON.stringify(virtualAgentResult); return response; } var currency; if (jsonBody.UserInput.value == "1") { currency = "USD"; } else if (jsonBody.UserInput.value == "2") { currency = "GBP"; } else if (jsonBody.UserInput.value == "3") { currency = "CAD"; } else if (jsonBody.UserInput.value == "4") { currency = "MXN"; } var url = COINBASE_URL + currency; var options = { 'method': 'GET', 'url': url, 'headers': { 'Accept': 'application/json' } }; try { var coinbaseResponse = await request.get(options, function(e , r , body) { }); var jsonResponse = JSON.parse(coinbaseResponse); // Massage amount information for the Virtual Agent prompt var amount = jsonResponse.data.amount.split("."); var newAmount; if (jsonBody.UserInput.value == "4") { newAmount = amount[0] + " pesos and " + amount[1].substring(0, 2) + " centavos"; } else if (jsonBody.UserInput.value == "2") { newAmount = amount[0] + " pound sterling and " + amount[1].substring(0, 2) + " pence"; } else { newAmount = amount[0] + " dollars and " + amount[1].substring(0, 2) + " cents"; } virtualAgentResult.amount = newAmount; response.body = JSON.stringify(virtualAgentResult); return response; } catch (e) { response.body = JSON.stringify(virtualAgentResult); return response; } };
Mischief Managed
There is no limit to the kinds of information an integration can bring into Virtual Agent. Personally, I have written integrations to Microsoft SQL databases, salesforce.com, monday.com, ServiceNow, restdb.io, and several other cloud services. In some cases, I have different integrations to the same CRM to retrieve different kinds of data under different conditions. In other words, integrations can be purpose built or generic in nature.
I hope this has whetted your appetite to try your programming hand at creating Virtual Agent integrations. I would love to see what you come up with!