“Most martial artists want to know how a technique is done. A seasoned Sensei will demonstrate why.”
Soke Behzad Ahmadi
In my previous article, Serverless Avaya Virtual Agent Integrations, I presented a high-level description of serverless, microservices using AWS Lambda. This article is for those of you who are interested in how it’s done.
As I wrote, the steps are as follows:
- Write your code in the language of your choice. This code consists of the business logic of your integration. For example, if all you need to do is pull data from a database, the code might be limited to some SQL (e.g. “Select * From Table Where id=xyz”), packaging the returned dataset into a JSON object, and sending the object back to the API caller.
- Create a zip file. The zip contains your code, any external libraries referenced by your code, and a package.json file.
- Create a Lambda function description. At the end of this step, Lambda provides you with a public link for the function.
- Upload your zip file into the function. This connects your code with the public link. Call the link and you call your code.
Allow me to unpack them by creating a Lambda function that can be invoked by Avaya’s Virtual Agent to retrieve the current Bitcoin price in various currencies.
Write Your Code
My function will use a Coinbase REST API to obtain the current Bitcoin price. For a price in United States dollars, you call:
https://api.coinbase.com/v2/prices/spot?currency=USD
A successful invocation returns data similar to this:
{
“data”: {
“base”:”BTC”,
“currency”:”USD”,
“amount”:”22454.16”
}
}
To prepare the environment, create a “lambda-btc” directory on your PC. Within that directory, launch PowerShell and run:
- npm init
- npm install request-promise
“npm init” creates a package.json file. Take the default for “main” and name your node.js file index.js.
“npm install request-promise” is required since you will be using the request-promise package to invoke the Coinbase API. This not only updates my package.json file to add request-promise as a dependency, it creates a node-modules directory under lambda-btc. This directory contains all the code necessary to use request-promise.
You can now write the code for index.js. Note that the programming logic is contained in the exports.handler block and any parameters passed to the function are contained in event.body. In my case, I am invoking the function from Avaya Virtual Agent as a REST POST with a JSON body containing:
{“UserInput” : {“value” : “2”}}
In Virtual Agent, the “UserInput” object is used to pass caller entered DTMF digits in the “value” field. In the above example, the caller pressed a 2. If you look into the code you will find that:
1 == United States dollars
2 == British pound sterling
3 == Canadian dollars
4 == Mexican pesos
After the Bitcoin price for the requested currency is found, the function returns the following JSON object to the calling application:
{
“amount”: “17015 pound sterling and 89 pence”
}
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 newBalance; if (jsonBody.UserInput.value == "4") { newBalance = amount[0] + " pesos and " + amount[1].substring(0, 2) + " centavos"; } else if (jsonBody.UserInput.value == "2") { newBalance = amount[0] + " pound sterling and " + amount[1].substring(0, 2) + " pencecoin"; } else { newBalance = amount[0] + " dollars and " + amount[1].substring(0, 2) + " cents"; } virtualAgentResult.amount = newBalance; response.body = JSON.stringify(virtualAgentResult); return response; } catch (e) { response.body = JSON.stringify(virtualAgentResult); return response; } };
Here is the code for those who would rather use axios than request-promise. The biggest difference is that axios automatically does a JSON parse on the returned data. If you use axios, make sure you do an “npm install axios” to build the correct package.json file.
const axios = require('axios'); const COINBASE_URL = "https://api.coinbase.com/v2/prices/spot/"; exports.handler = async (event) => { const response = { statusCode: 200, body: null, } var virtualAgentResult = { amount: null } var params = { currency: "USD" } var jsonBody = JSON.parse(event.body); // Query parameters can be retrieved via event.rawQueryString; if (!jsonBody.hasOwnProperty("UserInput")) { response.body = JSON.stringify(virtualAgentResult); return response; } if (jsonBody.UserInput.value == "1") { params.currency = "USD"; } else if (jsonBody.UserInput.value == "2") { params.currency = "GBP"; } else if (jsonBody.UserInput.value == "3") { params.currency = "CAD"; } else if (jsonBody.UserInput.value == "4") { params.currency = "MXN"; } var options = { 'method': 'GET', 'params': params, 'url': COINBASE_URL, 'headers': { 'Accept': 'application/json' } }; try { const coinbaseResponse = await axios(options); // Massage amount information for the Virtual Agent prompt var amount = coinbaseResponse.data.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; } };
Create a Zip File
Now that the code has been written, you will have a directory structure that looks like this:

You need to zip up everything except package-lock.json into a single zip file. This file will be uploaded into AWS Lambda in the fourth step. You can name the zip file anything you want.
Create Lambda Function
To create a Lambda function, go to your AWS console and launch Lambda. Once you are there, go to Lambda–>Functions and click on “Create Function.” Use “Author from scratch” and name your function. I called mine “coinbase.”

Expand “Advance settings” and configure the function as the following:

I don’t know if it’s necessary to select the CORS option, but it doesn’t hurt.
Click “Create Function” to finish the process.
Upload Zip File
To replace the default “Hello World” code that Lambda uses, go to the Code section and click on “Upload from.” Chose “.zip file” and select the file you created in Step Two.

You have now successfully loaded your code package into the Lambda function. The function can be invoked via the “Function URL” found in the “Function overview” section.
To test my REST services, I always use Google Postman. In this example, I used the Lambda Function URL for the POST URL. I then created a POST Body as described above and hit Send.

Voila! My function worked and is ready to be integrated my workflows.
Add Function as Virtual Agent Integration
This is a bonus step.
Since my goal was to create a Lambda microservice that can be invoked by Avaya Virtual Agent, I created a Virtual Agent and added the Lambda function URL as the integration in a VA node. Note that the integration is called as an “After action” Sequence. This allows the prompt to be played and the digit collected before the function is called.

When that node is hit in the Virtual Agent flow, the caller will be asked for a currency type (1, 2, 3, or 4), Virtual Agent will invoke the Lambda function, and in the Valid node, the caller will hear the Bitcoin price in dollars, pound sterling, or pesos.

Pretty cool, isn’t it? No Linux server nor having to write a node.js Express application to house the function call. With serverless microservices, my programming life has become much easier!
Mischief Managed
I gave you a lot to chew on, but if you follow these steps you can have your very own Lambda function, too. Of course, I hope you consider using your functions when building Avaya Virtual Agents, but the sky is the limit as to where microservices are useful.
Happy Programming!