Voice Journey
In this guide, 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.
Prerequisites
To complete this guide, you need:
- A Vonage account,
- The Vonage 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:
Create a Voice 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/voice/answer) and logging call events (/webhooks/voice/event), respectively.
These webhooks need to be accessible by Vonage's servers, so in this guide you will use ngrok to expose your local
development environment to the public Internet. This article explains how to install
and run ngrok and configure Vonage to send requests.
If you do not have an application, you can create one using the CLI
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 guide).
Run ngrok using the following command:
Make a note of the temporary host name that ngrok provides and use it in place of example.com in the following
command:
Buy a phone number
To handle inbound calls to your application, you need a number from Vonage. If you already have a number to use, jump to the next section to associate the existing number with your application.
You can use the Vonage CLI to buy the phone number:
Search for a Number
You can purchase a number using the Vonage CLI. The following command searches for a number a purchase (specify an alternate two-character country code to purchase a number in another country).
Purchase a number
Once you have found a number you are satisfied with, you can purchase that using the vonage numbers buy command:
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):
app.get('/webhooks/voice/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/voice/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:
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 guide 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:
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?
- Find out more about Call Flow with the Voice API
- Learn how to build a call menu (Interactive Voice Response) from scratch by following our tutorial
- 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.