Accept inbound calls

An inbound call is when a single person calls the virtual number associated with your Voice app.

The workflow for incoming calls is:

Inbound call workflow

  1. A user calls the virtual number associated with your application.
  2. Nexmo retrieves the NCCO from your answer_url webhook endpoint.
  3. Nexmo follows the actions in your NCCO.
  4. Nexmo sends status information to your webhook endpoint at event_url.

To handle inbound calls with the Voice API:

  1. Create a Nexmo application.

    Note: answer_url in the application you created points to a static Conversation NCCO.

  2. Create the NCCOs for the business logic you are implementing in your Voice app:

    [
        {
            "action": "connect",
            "eventUrl": ["https://example.com/events"],
            "from": "447700900000",
            "endpoint": [
              {
                "type": "phone",
                "number": "447700900001"
              }
            ]
        }
    ]
    
    [
        {
            "action": "talk",
            "text": "Please leave a message after the tone, then press #. We will get back to you as soon as we can",
            "voiceName": "Emma"
        },
        {
            "action": "record",
            "eventUrl": [
                "http://myrecordings/voicemails"
            ],
            "endOnSilence": "3",
            "endOnKey" : "#",
            "beepStart": "true"
        },
        {
            "action": "talk",
            "text": "Thank you for your message. Goodbye"
        }
    ]
    
  3. Create the webhook that returns the NCCO for your Conversation. The code examples show how to create a private communication system where you connect an inbound call to a hidden outbound phone number:

    Note: You must serve the NCCO with a Content-Type header of application/json or text/plain

    <?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
    
        //Dynamically create the NCCO to connect the inbound call from a mapped virtual number to
        //a phone number
        if( $to == "441632960960")
          $ncco='[
            {
            "action": "connect",
            "eventUrl": ["https://example.com/events"],
            "from": "441632960960",
            "endpoint": [
              {
                "type": "phone",
                "number": "441632960961"
              }
            ]
          }
        ]';
        else
          $ncco='[
            {
            "action": "connect",
            "eventUrl": ["https://example.com/events"],
            "from": "441632960963",
            "endpoint": [
              {
                "type": "phone",
                "number": "441632960965"
              }
            ]
          }
          ]';
        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
    
            #Dynamically create the NCCO to connect the inbound call from a mapped virtual number to
            #a phone number
            if call_to == "441632960960":
                ncco=[
                    {
                        "action": "connect",
                        "eventUrl": ["https://example.com/events"],
                        "from": "441632960960",
                        "endpoint": [
                          {
                            "type": "phone",
                            "number": "441632960961"
                          }
                        ]
                    }
                ]
            else:
                ncco=[
                    {
                        "action": "connect",
                        "eventUrl": ["https://example.com/events"],
                        "from": "441632960963",
                        "endpoint": [
                          {
                            "type": "phone",
                            "number": "441632960965"
                          }
                        ]
                    }
                ]
    
            #For more advanced Conversations you use the paramaters to personalize the NCCO
            #Dynamically create the NCCO to run a conversation from your virtual number
    
            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))
    
    equire '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
      #For more advanced Conversations you use the paramaters to personalize the NCCO
    
      #Dynamically create the NCCO to run a conversation from your virtual number
      if to == "441632960960"
        ncco=[
          {
            "action": "connect",
            "eventUrl": ["https://example.com/events"],
            "from": "441632960960",
            "endpoint": [
              {
                "type": "phone",
                "number": "441632960961"
              }
            ]
          }
        ]
      else
        ncco=[
          {
            "action": "connect",
            "eventUrl": ["https://example.com/events"],
            "from": "441632960963",
            "endpoint": [
              {
                "type": "phone",
                "number": "441632960965"
              }
            ]
          }
        ]
      end
    
      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
    
  4. Handle the call state changes at event_url. To inspect and debug state changes without writing your own server, create an online endpoint service such as requestb.in  or https://hookbin.com/  . The following code examples show you how to handle changes in Conversation state:

    <?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);
      // Work with the call status
      if (isset($decoded_request['status'])) {
        switch ($decoded_request['status']) {
          case 'ringing':
              echo("Handle conversation_uuid, this return parameter identifies the Conversation");
              break;
          case 'answered':
              echo("You use the uuid returned here for all API requests on individual calls");
              break;
          case 'complete':
              //if you set eventUrl in your NCCO. The recording download URL
              //is returned in recording_url. It has the following format
              //https://api.nexmo.com/media/download?id=52343cf0-342c-45b3-a23b-ca6ccfe234b0
              //Make a GET request to this URL using a JWT as authentication to download
              //the Recording. For more information, see Recordings.
              break;
          default:
              break;
      }
          return;
      }
    }
    
    /*
     *  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("HTTP/1.1 200 OK");
        break;
      default:
        //Handle your errors
        handle_error($request);
        break;
    }
    
    import time
    import BaseHTTPServer
    import re
    import json
    
    #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'))
    
            # Check if your messages are successful
            if inbound_message['status'] == 'ringing':
                print ("Handle conversation_uuid, this return parameter identifies the Conversation")
            if inbound_message['status'] == 'answered':
                print ("You use the uuid returned here for all API requests on individual calls")
            if inbound_message['status'] == 'complete':
                print ("Find your recording")
                #if you set eventUrl in your NCCO. The recording download URL
                #is returned in recording_url. It has the following format
                # https://api.nexmo.com/media/download?id=52343cf0-342c-45b3-a23b-ca6ccfe234b0
                #Make a GET request to this URL using a JWT as authentication to download
                #the Recording. For more information, see Recordings.
            """Tell Nexmo that you have recieved the POST request."""
            s.send_response(200)
            s.send_header("Content-type", "text/html")
            s.end_headers()
    
    require '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)
    
      #Check the is an inbound message
      if (inbound_message['status'] == 'ringing')
        p ("Handle the conversation_uuid return parameter that identifies this Conversation")
      elsif (inbound_message['status'] == 'answered')
        p ("YYou use the uuid returned here for all API requests on individual calls")
      elsif (inbound_message['status'] == 'complete')
        #if you set eventUrl in your NCCO. The URL to download
        #the recording from is set in recording_url.
        #To download the recording
        #curl recording_url&api_key=***&api_secret=***” -o “recording.mp3”
      end
    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)
        handle_inbound_message(data)
      end
    
      # Return the 200 so Nexmo does not send the DLR to you repeatedly
      resp = "Thank you"
      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
      socket.puts resp
      # Close the socket, terminating the connection
      socket.close
    end
    
  5. Your Conversation starts when somebody calls the virtual number associated with the application.