Contact Center Augmentation
In this tutorial, you will implement the following contact center scenario:
- The user calls your Vonage number,
- The initial greeting is read out, and the user chooses an option from the menu provided,
- The user is connected to an agent,
- After the agent closes the call, the user is transferred to a customer satisfaction survey.
For a better understanding of the flow, see Call Flow guide.
Prerequisites
To complete this tutorial, you need:
- A Vonage account,
- The Nexmo CLI installed and set up,
- ngrok - to make your development web server accessible to Vonage's servers over the Internet,
- Node.JS installed,
- two phone calling devices with PSTN numbers assigned - "user phone" and "agent phone", for example, two mobile phones or an application with calling capability like Vonage Business app.
Install the dependencies
Install the express web application framework and body-parser packages:
$ npm install express body-parser
Purchase a Vonage number
If you don't already have one, buy a Vonage number to receive inbound calls.
First, list the numbers available in your country (replace GB
with your two-character country code):
nexmo number:search GB
Purchase one of the available numbers. For example, to purchase the number 447700900001
, execute the following command:
nexmo number:buy 447700900001
Create a Voice API application
Use the CLI to create a Voice API application with the webhooks that will be responsible for answering a call on your Vonage number (/webhooks/answer
) and logging call events (/webhooks/events
), respectively.
These webhooks need to be accessible by Vonage's servers, so in this tutorial you will use ngrok
to expose your local development environment to the public Internet. This article explains how to install and run ngrok
.
Run ngrok
using the following command:
ngrok http 3000
Make a note of the temporary host name that ngrok
provides and use it in place of example.com
in the following command:
nexmo app:create "CCApp" --capabilities=voice --voice-event-url=https://example.com/webhooks/event --voice-answer-url=https://example.com/webhooks/answer --keyfile=private.key
The command returns an application ID (which you should make a note of) and your private key information (which you can safely ignore for the purposes of this tutorial).
Link your number
You need to link your Vonage number to the Voice API application that you created. Use the following command:
nexmo link:app VONAGE_NUMBER VONAGE_APPLICATION_ID
You're now ready to write your application code.
Write your answer webhook
When Vonage receives an inbound call on your virtual number, it will make a request to your /webhooks/answer
route. This route should accept an HTTP GET
request and return a Nexmo Call Control Object (NCCO) that tells Vonage how to handle the call.
Your NCCO should use the talk
action to greet the caller, and the input
action to get user DTMF input (key pressed):
const app = require('express')()
const bodyParser = require('body-parser')
const https = require('https')
const Vonage = require('@vonage/server-sdk')
const vonage = new Vonage({
apiKey: VONAGE_API_KEY,
apiSecret: VONAGE_API_SECRET,
applicationId: VONAGE_APPLICATION_ID,
privateKey: VONAGE_APPLICATION_PRIVATE_KEY_PATH
})
app.use(bodyParser.json())
app.get('/webhooks/answer', (request, response) => {
console.log('answer: ', request.query)
const ncco = [{
action: 'talk',
text: 'Thank you for calling Example Inc.! Press 1 to talk to the sales department, press 2 to get technical support.',
bargeIn: true
},
{
action: 'input',
eventUrl: [
`${request.protocol}://${request.get('host')}/webhooks/input`],
type: [ 'dtmf' ],
dtmf: {
maxDigits: 1
}
}
]
response.json(ncco)
})
Write your event webhook
Implement a webhook that captures call events so that you can observe the lifecycle of the call in the console:
app.post('/webhooks/event', (request, response) => {
console.log('event:', request.body)
response.sendStatus(200);
})
Vonage makes a POST
request to this endpoint every time the call status changes.
Write your input webhook
DTMF input results will be sent to the specific URL you set in the input
action: /webhooks/input
. Add a webhook to process the result and add some user interaction.
In case of a successful recognition, the request payload will look as follows:
{
"speech": {
},
"dtmf": {
"digits": "1",
"timed_out": true
},
"from": USER_NUMBER,
"to": VONAGE_NUMBER,
"uuid": "abfd679701d7f810a0a9a44f8e298b33",
"conversation_uuid": "CON-64e6c8ef-91a9-4a21-b664-b00a1f41340f",
"timestamp": "2020-04-17T17:31:53.638Z"
}
Retrieve the user's input from the dtmf.digits
array to determine the options they selected. To connect both user and agent of the corresponding department, you'll create an outbound call to the agent endpoint. You can use a test phone number in this sample for simplicity, for example, your mobile number. In real life, the endpoint might be a PSTN number or SIP endpoint to connect your existing PBX or contact center. It could also be a WebRTC client if you are developing your contact center solution from scratch - the Vonage Client SDK provides everything you need to implement your own Contact Center agent application.
Finally, both calls (legs) should be moved to one conference room (named conversation). To do that, you should use conversation
action with the same name
both for the user call (the inbound leg) and the agent leg (the outbound call). To generate the conversation name, you may use any unique ID generation method, for example, using the actual timestamp.
As an option, you may use connect
action in the NCCO to connect the user to the agent. The difference is that with connect
, the call will be immediately completed when any of the call participants hang up, and there is only one leg left. So, it would be impossible to transfer the user to the survey after the call; if that's not needed in your case, connect
is a bit more handy option. If you want the user still connected after the agent completes the call, choose conversation
as shown in the example below.
Add the code to handle the input callback:
app.post('/webhooks/input', (request, response) => {
console.log('input:', request.body)
// generating unique conversation name
var conversationName = 'conversation_' + Date.now()
console.log('conversationName: ', conversationName)
// selecting agent/department endpoint
var departmentId = request.body.dtmf.digits
var department = ''
var departmentNumber = ''
switch (departmentId) {
case '1':
department = 'Sales'
departmentNumber = AGENT_NUMBER
break
case '2':
department = 'Support'
departmentNumber = OTHER_AGENT_NUMBER //you can use the same number for the sample
break
default:
break
}
var ncco = ''
if (department != '') {
// NCCO for the user leg
ncco = [{
action: 'talk',
text: 'Please wait while we connect you to ' + department
}, {
action: 'conversation',
name: conversationName
}
]
// creating the agent leg and moving it to the same conversation
vonage.calls.create({
to: [
{
type: 'phone',
number: departmentNumber
}
],
from: {
type: 'phone',
number: VONAGE_NUMBER
},
ncco: [
{
action: 'conversation',
name: conversationName
}]
}, (error, response) => {
if (error) console.error('outbound error:', error)
if (response) {
console.log('outbound ok')
}
})
} else { // something went wrong, fallback route
ncco = [{
action: 'talk',
text: 'Press 1 to talk to the sales department, press 2 to get technical support.',
bargeIn: true
}, {
action: 'input',
eventUrl: [
`${request.protocol}://${request.get('host')}/webhooks/input`
],
dtmf: {
maxDigits: 1
}
}]
}
response.json(ncco)
})
Add survey NCCO
Next, to implement the customer satisfaction survey at the end of the call, you should handle completed
event for the agent's leg. It will arrive at the same event webhook, so you should extend the event webhook with transfer request to the survey NCCO. In order to do that, you have to store the user and the agent leg identifiers:
var userLegId = ''
var agentLegId = ''
app.get('/webhooks/answer', (request, response) => {
console.log('answer: ', request.query)
userLegId = request.query.uuid
console.log('userLegId: ', userLegId)
...
app.post('/webhooks/input', (request, response) => {
console.log('input:', request.body)
// creating the agent's leg and moving it to the same conversation
vonage.calls.create({
...
}, (error, response) => {
if (error) console.error('outbound error:', error)
if (response) {
agentLegId = response.uuid
console.log('agentLegId: ', agentLegId)
}
})
} else ...
In the real-life scenario, you should implement a cache to store the pairs of user/agent leg identifiers. The sample code shown in this tutorial will work properly only for one concurrent call.
Extend your event webhook with REST API update call method with inline NCCO with talk
and input
actions to move the user leg to the survey part:
app.post('/webhooks/event', (request, response) => {
console.log('event:', request.body)
if (request.body.uuid == agentLegId && request.body.status == 'completed') {
vonage.calls.update(userLegId, {
action: 'transfer',
destination: {
type: 'ncco',
ncco: [ {
action: 'talk',
text: 'Please valuate quality of service by entering a digit, 1 to 5'
},
{
action: 'input',
type: [ 'dtmf' ],
dtmf: {
maxDigits: 1
},
eventUrl: [ `${request.protocol}://${request.get('host')}/webhooks/survey` ]
}
]
}
}, (err, res) => {
if (err) {
console.error('transfer error:', err)
} else {
console.log('transfer ok')
}
})
}
response.sendStatus(200)
})
Write your survey webhook
Add survey webhook to print the results:
app.post('/webhooks/survey', (request, response) => {
console.log('survey: ', request.body)
var phone = request.body.from
var date = request.body.timestamp
var score = request.body.dtmf.digits
console.log('[%s] User %s gave %d', date, phone, score)
const ncco = [
{
action: 'talk',
text: 'Thank you, good bye.'
}
]
response.json(ncco)
})
Create your Node.js server
Finally, write the code to instantiate your Node.js server:
const port = 3000
app.listen(port, () => console.log(`Listening on port ${port}`))
Test your application
- Run your Node.js application by executing the following command:
node index.js
Call your Vonage number from "user phone" and listen to the welcome message.
Open the dial pad and press 1 or 2.
Answer the inbound call "agent phone".
Hang up on the agent phone.
Listen to the survey message and press any key on the first device.
Observe the console log to see the survey result.
Troubleshooting
If you don't hear the user and agent sound, potentially it might be because the two legs are being processed in different locations. You can determine this by seeing different conversation_uuid_to
values in the transfer
events for user and agent legs. To fix that, try to configure the SDK to use a specific data center as described in the Troubleshooting guide:
const options = {
apiHost: 'api-us-1.nexmo.com',
restHost: 'rest-us-1.nexmo.com'
}
const vonage = new Vonage({
apiKey: VONAGE_API_KEY,
apiSecret: VONAGE_API_SECRET
applicationId: VONAGE_APPLICATION_ID,
privateKey: VONAGE_APPLICATION_PRIVATE_KEY_PATH
}, options)
Conclusion
With Vonage Voice API you can empower your existing contact center solution with IVR of any logic complexity, which depends only on your target use case and is virtually unlimited. Or you can build your own solution from scratch using the Voice API and Client SDK. Switching between the scripted part of the call and live conversation (and back) gives you the ability to mix any phone calling use cases and create a seamless customer experience.
Where Next?
- Learn more about Call Flow with Voice API;
- Improve customer experience with Speech Recognition, as an alternative or together with DTMF input;
- See how to use Call Recording for future references and post-call analytics;
- Get direct access to the media with WebSockets for real-time analytics and AI integration.
- Check Vonage AI offering to get IVR or voice bot built by our experts for your specific use-case.