Call Routing Using Voice Input
In this how-to guide, we will guide you through implementing a basic call routing application with FreeClimb that utilizes user DTMF (e.g. keypad) and voice input to route users. This system will perform the following actions:
- Receive an incoming call via a FreeClimb application
- Get DTMF (e.g. keypad) or speech input from a user
- Redirect a user to the appropriate endpoint
You're ready for this how-to guide if you have:
Followed the IVR sample app set-up instructions
Step 1: Create the express server
The server will provide endpoints where user input can be captured. Create an index.js
file in your project directory, import the needed dependencies, and create/configure the Express application:
require('dotenv-safe').config()
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`Starting server on port ${port}`)
})
module.exports = { app }
Step 2: Handle an incoming call
The first step in our call routing application will be to handle incoming calls. To do this, add the following to index.js
:
const {
createConfiguration,
DefaultApi,
PerclScript,
Say,
Pause,
Redirect,
GetSpeech,
Hangup
} = require('@freeclimb/sdk')
const host = process.env.HOST
const accountId = process.env.ACCOUNT_ID
const apiKey = process.env.API_KEY
const freeclimb = new DefaultApi(createConfiguration({ accountId, apiKey }))
let mainMenuErrCount = 0
app.post('/incomingCall', (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: 'Welcome to the Node IVR Sample app baseline.' }),
new Pause({ length: 100 }),
new Redirect({ actionUrl: `${host}/mainMenuPrompt` })
]
}).build()
)
})
Step 3: Collect speech and digits via a main menu
Next we'll collect input from the user to direct them to their next destination. To do this, we'll create endpoints for speech input, DTMF collection, and menu routing and add them to your index.js
file:
app.post('/mainMenuPrompt', (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new GetSpeech({
actionUrl: `${host}/mainMenu`,
grammarFile: `${host}/mainMenuGrammar`,
grammarType: 'URL',
grammarRule: 'option',
prompts: [
new Say({
text:
'Say existing or press 1 for existing orders. Say new or press 2 for new orders, or Say operator or press 0 to speak to an operator'
})
]
})
]
}).build()
)
})
Next we'll add the logic for how the app will handle user input to index.js
:
app.post('/mainMenu', (req, res) => {
let menuOpts
const getSpeechResponse = req.body
const response = getSpeechResponse.recognitionResult
if (req.body.reason === 'digit') {
menuOpts = new Map([
[
'1',
{
script: 'Redirecting your call to existing orders.',
redirect: `${host}/endCall`
}
],
[
'2',
{
script: 'Redirecting your call to new orders.',
redirect: `${host}/endCall`
}
],
[
'0',
{
script: 'Redirecting you to an operator',
redirect: `${host}/transfer`
}
]
])
} else if (req.body.reason === 'recognition') {
menuOpts = new Map([
[
'EXISTING',
{
script: 'Redirecting your call to existing orders.',
redirect: `${host}/endCall`
}
],
[
'NEW',
{
script: 'Redirecting your call to new orders.',
redirect: `${host}/endCall`
}
],
[
'OPERATOR',
{
script: 'Redirecting you to an operator',
redirect: `${host}/transfer`
}
]
])
}
if ((!response || !menuOpts.get(response)) && mainMenuErrCount < 3) {
// error counting keeps bad actors from cycling within your applications
mainMenuErrCount++
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: 'Error, please try again' }),
new Redirect({ actionUrl: `${host}/mainMenuPrompt` })
]
}).build()
)
} else if (mainMenuErrCount >= 3) {
// we recommend giving your customers 3 tries before ending the call
mainMenuErrCount = 0
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: 'Max retry limit reached' }),
new Pause({ length: 100 }),
new Redirect({ actionUrl: `${host}/endCall` })
]
}).build()
)
} else {
mainMenuErrCount = 0
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: menuOpts.get(response).script }),
new Redirect({ actionUrl: menuOpts.get(response).redirect })
]
}).build()
)
}
})
Step 4: Create a grammar file
Additionally, we'll also define a custom grammar file to validate any speech input from the user. To do so, create a file called mainMenuGrammar.xml
in the root directory of your project with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<grammar xml:lang="en-US" tag-format="semantics-ms/1.0" version="1.0" root="option" mode="voice" xmlns="http://www.w3.org/2001/06/grammar">
<rule id="option" scope="public">
<item>
<item repeat="0-1"><ruleref uri="#UMFILLER"/></item>
<item>
<one-of>
<item>existing<tag>$._value = "EXISTING";</tag></item>
<item>new<tag>$._value = "NEW";</tag></item>
<item>operator<tag>$._value = "OPERATOR";</tag></item>
</one-of>
</item>
<item repeat="0-1"><ruleref uri="#TRAILERS"/></item>
</item>
</rule>
<rule id="UMFILLER">
<one-of>
<item> uh </item>
<item> um </item>
<item> hm </item>
<item> ah </item>
<item> er </item>
</one-of>
</rule>
<rule id="TRAILERS">
<one-of>
<item> maam </item>
<item> sir </item>
</one-of>
</rule>
</grammar>
To learn more about grammars and creating your own XML files, check out our How to Write Grammars guide.
Step 5: Get the grammar file
We'll also add an endpoint used by the GetSpeech
PerCL command to load our grammar file for use. To do this, add the following to index.js
before /mainMenu
:
app.get('/mainMenuGrammar', function (req, res) {
const file = `${__dirname}/mainMenuGrammar.xml`
res.download(file)
})
Step 6: Transfer and end call
Finally we'll add in appropriate endpoints for simulating a call transfer and ending a call. Do so by adding to following to index.js
:
app.post('/transfer', (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: 'Please wait while we transfer you to an operator' }),
new Redirect({ actionUrl: `${host}/endCall` })
]
}).build()
)
})
app.post('/endCall', (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new Say({
text:
'Thank you for calling the Node IVR sample app baseline, have a nice day!'
}),
new Hangup({})
]
}).build()
)
})
Step 7: Run your app
To hear your IVR Voice-Enabled Call Routing app in action, run the following command at the command line:
yarn start
Once you do this, call the FreeClimb number associated with your application. From there you should be able to experience your call routing application.
For a more advanced IVR how-to guide, try out our Pay by Phone with Voice Calling sample app.
Congrats! You can now build your own custom IVR voice-enabled call routing application. 🥳🥳
Updated about 1 month ago