Interactive Voice Response (IVR)

With the Voice API you easily create an IVR by generating NCCOs in a webhook.

Interactive Voice Response (IVR) is a technology that allows a computer to interact with humans through the use of voice and DTMF tones input on the handset keypad. You create IVR systems for mobile purchases, banking payments and services, retail orders, utilities, travel information and weather conditions.

In this section you will see how to build an IVR system using Nexmo APIs:

Prerequisites

To follow the steps in this tutorial you need to:

  • Setup a Nexmo account
  • Rent a virtual number using Dashboard or Developer API and set the webhook endpoint to your app
  • Create an application and associate it with your virtual number. Nexmo retrieves the initial NCCO from the answer_url webhook and sends the dtmf input to the eventUrl webhook defined in the initial NCCO

Create your IVR

In an IVR, when your user calls your virtual number you first send welcome message using text-to-speech or an audio stream. This message tells your user which button to push for your available services. Then, in function of the dtmf input, you generate the NCCOS that create a customized experience for your user.

Using bargeIn, your user does not have to listen to the whole announcement. If he or she already knows the choices, when they press an option during a talk or stream action, the announcement is stopped and the IVR executes their instruction. BargeIn makes the talk or stream actions asynchronous. You must set an input action later in the NCCO stack.

The workflow for an IVR is:

Participant answer_url Participant eventUrl Participant Nexmo Participant User User->Nexmo: Call virtual number Nexmo->answer_url: Send call info answer_url->Nexmo: Return NCCO with options\n Set eventUrl Nexmo->User: Send welcome message set in NCCO User->Nexmo: Press digits in function of welcome message Nexmo->eventUrl: Send dtmf input eventUrl->Nexmo: Send customized NCCO\n in function of dtmf input Note over Nexmo: Execute actions in NCCO Nexmo->User: Customized user experience

To implement this workflow:

  1. Supply your users with a virtual number to contact.

  2. When a user calls the virtual numbers, Nexmo forwards information about the Call to your webhook endpoint.

  3. At the webhook endpoint, store the caller information and send an NCCO with the welcome message and options:

    <?php
    
    $method = $_SERVER['REQUEST_METHOD'];
    $request = array_merge($_GET, $_POST);
    
    $ncco = "";
    switch ($method) {
      case 'GET':
        //Retrieve with the parameters in this request
        $to = $request['to']; //The endpoint being called
        $from = $request['from']; //The endpoint you are calling from
        $uuid = $request['conversation_uuid']; //The unique ID for this Call
        //Store the parameters in your database to identify this conversation in further interactions
    
        //Generate the welcome message
        $ncco='[
          {
            "action": "talk",
            "text": "Welcome to a Voice API I V R. Press 1 for maybe and 2 for not sure followed by the hash key",
            "voiceName": "Amy"
          },
          {
            "action": "input",
            "submitOnHash": "true",
            "eventUrl": ["https://example.com/ivr"]
          }
        ]';
        header('Content-Type: application/json');
        echo $ncco;
        break;
      default:
        //Handle your errors
        handle_error($request);
        break;
    }
    
    #To run this code, replace the MyHandler in
    #https://wiki.python.org/moin/BaseHttpServer With the following code,
    import time
    import BaseHTTPServer
    import re
    import json
    
    class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_GET(s):
            #Parse parameters in the GET request
            parsed_path = urlparse(s.path)
            try:
                    params = dict(
                    [p.split('=') for p in parsed_path[4].split('&')])
            except:
                    params = {}
    
            #Retrieve with the parameters in this request
            call_to = params['to']      #The endpoint being called
            call_form = params['from']  #The endpoint you are calling from
            call_uuid = params['conversation_uuid']     #The unique ID for this Conversation
            #Store the parameters in your database to identify this conversation in further interactions
    
            #Generate the welcome message
            ncco=[
              {
                "action": "talk",
                "text": "Welcome to a Voice API I V R. Press 1 for maybe and 2 for not sure followed by the hash key",
                "voiceName": "Amy"
              },
              {
                "action": "input",
                "submitOnHash": "true",
                "eventUrl": ["https://example.com/ivr"]
              }
            ]
    
            print "GET Request from " + s.path
            s.send_response(200)
            s.send_header("Content-type", "application/json")
            s.end_headers()
            s.wfile.write(json.dumps(ncco))
    
    require 'socket'
    require 'uri'
    require 'json'
    
    #Dynamically create the NCCO to send synthsized speech to a virtual number
    # Initialize a TCPServer
    def generate_ncco(request_line)
    #Parse the parameters and check if the message was delivered
      params = URI::decode_www_form(request_line).to_h
    
      #Retrieve with the parameters in this request
      to = params['to']         #The endpoint being called
      from = params['from']     #The endpoint you are calling from
      uuid = params['conversation_uuid']    #The unique ID for this Conversation
        #Store the parameters in your database to identify this conversation in further interactions
    
      #Generate the welcome message
        ncco=[
          {
            "action": "talk",
            "text": "Welcome to a Voice API I V R. Press 1 for maybe and 2 for not sure followed by the hash key",
            "voiceName": "Amy"
          },
          {
            "action": "input",
            "submitOnHash": "true",
            "eventUrl": ["https://example.com/ivr"]
          }
        ]
    
      return ncco.to_json
    end
    
    server = TCPServer.new('', 9999)
    # Wait for connections
    loop do
      # Wait until a client connects
      socket = server.accept
      method, path = socket.gets.split
      resp = generate_ncco(path)
      # Return the 200 OK
    
      headers = ["HTTP/1.1 200 OK",
                 "Content-Type: text/html; charset=iso-8859-1",
                 "Content-Length: #{resp.length}\r\n\r\n"].join("\r\n")
      socket.puts headers
      #Return the NCCO
      socket.puts resp
      # Close the socket, terminating the connection
      socket.close
    end
    
    [
      {
        "action": "talk",
        "text": "Welcome to a Voice API I V R. ",
        "voiceName": "Amy",
        "bargeIn": false
      },
      {
        "action": "talk",
        "text": "Press 1 for maybe and 2 for not sure followed by the hash key",
        "voiceName": "Amy",
        "bargeIn": true
      },
      {
        "action": "input",
        "submitOnHash": "true",
        "eventUrl": ["https://example.com/ivr"]
      }
    ]
    
  4. Your user hears the welcome message and presses a key to choose an option.

  5. Your code at the eventUrl webhook endpoint returns a customized NCCO in function of the keys pressed by your user:

    <?php
    /*
     *  Place this script at event_url for your Nexmo application
     */
    $method = $_SERVER['REQUEST_METHOD'];
    // work with get or post
    $request = array_merge($_GET, $_POST);
    
    /*
     *  Do something for changed call status
    */
    function handle_call_status()
    {
      $decoded_request = json_decode(file_get_contents('php://input'), true);
      // Set a default message for incorrect key presses
      $ncco = "";
      if (isset($decoded_request['dtmf'])) {
        switch ($decoded_request['dtmf']) {
          case '1':
              $ncco = '[
                  {
                    "action": "talk",
                    "text": "Thank you, I will forward you to the maybe department",
                    "voiceName": "Amy"
                  },
                  {
                    "action": "connect",
                    "eventUrl": ["https://example.com/events"],
                    "from": "441632960960",
                    "endpoint": [
                      {
                        "type": "phone",
                        "number": "441632960961"
                      }
                    ]
                  }
                ]';
              break;
          case '2':
          $ncco = '[
              {
                "action": "talk",
                "text": "Thank you, I will forward you to the not sure department",
                "voiceName": "Amy"
              },
              {
                "action": "connect",
                "eventUrl": ["https://example.com/events"],
                "from": "441632960960",
                "endpoint": [
                  {
                    "type": "phone",
                    "number": "441632960962"
                  }
                ]
              }
            ]';
              break;
          default:
            $ncco = '[
                {
                  "action": "talk",
                  "text": "I am sorry, I did not catch that. Please press 1 for maybe and 2 for not sure followed by the hash key",
                  "voiceName": "Amy"
                },
                {
                  "action": "input",
                  "submitOnHash": "true",
                  "eventUrl": ["https://example.com/ivr"]
                }
              ]';
              break;
      }
          return $ncco;
      }
    }
    
    /*
     *  Handle errors
    */
    function handle_error($request){
         //code to handle your errors
    }
    
    /*
      Send the 200 OK to Nexmo and handle changes to the call
    */
    switch ($method) {
      case 'POST':
        //Retrieve your dynamically generated NCCO.
        $ncco = handle_call_status();
        header('Content-Type: application/json');
        echo $ncco;
        break;
      default:
        //Handle your errors
        handle_error($request);
        break;
    }
    
    #To run this code, replace the MyHandler in
    #https://wiki.python.org/moin/BaseHttpServer With the following code,
    class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_POST(s):
            """Parse parameters in the POST request"""
            content_len = int(s.headers.getheader('content-length', 0))
            post_body = s.rfile.read(content_len)
            inbound_message = json.loads(post_body.decode('utf-8'))
            ncco = [
                {
                    "action": "talk",
                    "text": "I'm sorry, I did not catch that. Please press 1 for maybe and 2 for not sure followed by the hash key",
                    "voiceName": "Amy"
                },
                {
                    "action": "input",
                    "submitOnHash": "true",
                    "eventUrl": ["https://example.com/ivr"]
                }
            ]
    
            # Check if your messages are successful
            if inbound_message['dtmf'] == '1':
              ncco = [
                  {
                    "action": "talk",
                    "text": "Thank you, I'll forward you to the maybe department",
                    "voiceName": "Amy"
                  },
                  {
                    "action": "connect",
                    "eventUrl": ["https://example.com/events"],
                    "from": "441632960960",
                    "endpoint": [
                      {
                        "type": "phone",
                        "number": "441632960961"
                      }
                    ]
                  }
                ]
            if inbound_message['dtmf'] == '2':
              ncco = [
                  {
                    "action": "talk",
                    "text": "Thank you, I'll forward you to the not sure department",
                    "voiceName": "Amy"
                  },
                  {
                    "action": "connect",
                    "eventUrl": ["https://example.com/events"],
                    "from": "441632960960",
                    "endpoint": [
                      {
                        "type": "phone",
                        "number": "441632960962"
                      }
                    ]
                  }
                ]
    
            """Tell Nexmo that you have recieved the POST request."""
            s.send_response(200)
            s.send_header("Content-Type", "application/json")
            s.end_headers()
            s.wfile.write(json.dumps(ncco))
    
    equire 'socket'
    require 'uri'
    require 'pstore'
    require 'JSON'
    
    def handle_inbound_message(request_line)
    
      #Parse the parameters and check if the message was delivered
      inbound_message = JSON.parse(request_line)
      ncco = [
          {
              "action": "talk",
              "text": "I'm sorry, I did not catch that. Please press 1 for maybe and 2 for not sure followed by the hash key",
              "voiceName": "Amy"
          },
          {
              "action": "input",
              "submitOnHash": "true",
              "eventUrl": ["https://example.com/ivr"]
          }
      ]
      #Create an NCCO in function of the button pressed by your user
      if (inbound_message['dtmf'] == '1')
        ncco = [
            {
              "action": "talk",
              "text": "Thank you, I'll forward you to the maybe department",
              "voiceName": "Amy"
            },
            {
              "action": "connect",
              "eventUrl": ["https://example.com/events"],
              "from": "441632960960",
              "endpoint": [
                {
                  "type": "phone",
                  "number": "441632960961"
                }
              ]
            }
          ]
      elsif (inbound_message['dtmf'] == '2')
        ncco = [
            {
              "action": "talk",
              "text": "Thank you, I'll forward you to the not sure department",
              "voiceName": "Amy"
            },
            {
              "action": "connect",
              "eventUrl": ["https://example.com/events"],
              "from": "441632960960",
              "endpoint": [
                {
                  "type": "phone",
                  "number": "441632960962"
                }
              ]
            }
          ]
      end
      return ncco
    end
    
    # Initialize a TCPServer
    server = TCPServer.new('', 9999)
    
    # Wait for connections
    loop do
      # Wait until a client connects
      socket = server.accept
      method, path = socket.gets.split
      headers = {}
      while line = socket.gets.split(' ', 2)              # Collect HTTP headers
        break if line[0] == ""                            # Blank line means no more headers
        headers[line[0].chop] = line[1].strip             # Hash headers by type
      end
      content_length = headers["Content-Length"].to_i
      unless content_length == 0
        data = socket.read(content_length)
        ncco = handle_inbound_message(data)
      end
    
      # Return the 200 so Nexmo does not send the DLR to you repeatedly
      headers = ["HTTP/1.1 200 OK",
                 "Content-Type: application/json",
                 "Content-Length: #{resp.length}\r\n\r\n"].join("\r\n")
      socket.puts headers
      #return the NCCO
      socket.puts ncco
      # Close the socket, terminating the connection
      socket.close
    end
    
    #Create an NCCO in function of the button pressed by your user
      if (inbound_message['dtmf'] == '1')
        ncco = [
            {
              "action": "talk",
              "text": "Thank you, I'll forward you to the maybe department",
              "voiceName": "Amy"
            },
            {
              "action": "connect",
              "eventUrl": ["https://example.com/events"],
              "from": "441632960960",
              "endpoint": [
                {
                  "type": "phone",
                  "number": "441632960961"
                }
              ]
            }
          ]
      elsif (inbound_message['dtmf'] == '2')
        ncco = [
            {
              "action": "talk",
              "text": "Thank you, I'll forward you to the not sure department",
              "voiceName": "Amy"
            },
            {
              "action": "connect",
              "eventUrl": ["https://example.com/events"],
              "from": "441632960960",
              "endpoint": [
                {
                  "type": "phone",
                  "number": "441632960962"
                }
              ]
            }
          ]
      end
    

And that's it. You have built an IVR. To do this you have provisioned and configured a virtual number, sent a generic welcome message, handled inbound calls and created a customized experience for your user.