Webhooks

What is a webhook?

A webhook  is a callback sent via HTTP to a URL of your choice. It is a simple way of enabling communication from our servers to yours. Nexmo uses webhooks to notify you about incoming calls and messages, events in calls and delivery receipts for messages.

 Which APIs support webhooks?

Information resulting from requests to the SMS API, Voice API, Number Insight API, US Short Codes API and Nexmo virtual numbers is sent in an HTTP request to your webhook endpoint on an HTTP server.

Nexmo sends and retrieves the following information using webhooks:

  • SMS API - sends the delivery status of your message and receives inbound SMS
  • Voice API - retrieves the Nexmo Call Control Objects you use to control the call from one webhook endpoint, and sends information about the call status to another
  • Number Insight Advanced Async API - receives complete information about a phone number
  • US Short Codes API - sends the delivery status of your message and receives inbound SMS

Setting webhook endpoints

Webhooks are used to deliver incoming messages and delivery receipts.

Incoming messages

To set up the webhook used for incoming messages, go to the Your Numbers  section of the Nexmo Dashboard. Click 'edit' for the virtual number and set the Callback URL.

You can also use the nexmo-cli  to set the incoming messages endpoint for an individual number.

Delivery Receipts

See the Delivery Reciepts guide in the SMS documentation.

The Number Insight Advanced API allows you to have the results of a number lookup sent synchronously or asyncronously.

Set the callback argument to a webhook URL to receive the lookup asynchronously.

See Number Insight Advanced Async for more details.

For Voice API requests, webhooks can be set at an application level, when creating a call, or in the actions in an NCCO.

Application-level webhooks

Numbers you have purchased that are connected to Nexmo applications will use the answer_url to retrieve an NCCO, and the event_url to send call status information to you.

You can set these using the Application API, in the Nexmo Dashboard  or using the Nexmo CLI  tool.

Number-level webhooks

You can set a status webhook for each number you purchase. This will be used to send events to you regarding each number.

These can be set up in the Numbers section of the Dashboard  , via the Nexmo CLI  or via the Update a Number API call (specifically, the voiceStatusCallback property).

On creating an outbound call

When making a new outbound call, you need to set the answer_url in the call to a URL containing an NCCO. Nexmo's servers will retrieve the NCCO from this endpoint and follow its instructions in handling the outbound call.

Inside an NCCO

Inside an NCCO, the following action types take a webhook URL for use when that action is executed:

  • record.eventUrl - set the webhook endpoint that receives information about the recording for a Call or Conversation
  • conversation.eventUrl - set the URL to the webhook endpoint Nexmo calls asynchronously when a conversation changes state for this conversation action
  • connect.eventUrl - set the URL to the webhook endpoint Nexmo calls asynchronously when a conversation changes state for this connect action
  • input.eventUrl - set the URL to the webhook endpoint Nexmo sends the digits pressed by the callee
  • stream.streamUrl - set an array of URLs pointing to the webhook endpoints hosting the audio file to stream to the Call or Conversation

Receiving webhooks

To interact with Nexmo webhooks:

  1. Create a Nexmo account.
  2. Write scripts to handle the information sent or requested by Nexmo. Your server must respond with success status code to inbound messages from Nexmo.
  3. Put your scripts on your HTTP server.
  4. Send a request with the webhook endpoint set.

Information about your request is then sent to your webhook endpoint.

The following code examples are webhooks for the SMS API:

var app = require('express')();
app.set('port', (process.env.PORT || 5000));
app.use(require('body-parser').urlencoded({
    extended: false
}));
// Handle GET webhook
app.get('/delivery-receipt-webhook', function(req, res) {
    handleWebhook(req.query, res);
});
// Handle POST webhook
app.post('/delivery-receipt-webhook', function(req, res) {
    handleWebhook(req.body, res);
});

function handleWebhook(params, res) {
    if (!params['status'] || !params['messageId']) {
        console.log('This is not a delivery receipt');
    } else {
        //This is a DLR, check that your message has been delivered correctly
        if (params['status'] !== 'delivered') {
            console.log("Fail:", params['status'], ": ", params['err-code']);
        } else {
            console.log("Success");
          /*
            * The following parameters in the delivery receipt should match the ones
            * in your request:
            * Request - from, dlr - to\n
            * Response - message-id, dlr - messageId
            * Request - to, Responese - to, dlr - msisdn
            * Request - client-ref, dlr - client-ref
           */
        }
    }
    res.sendStatus(200);
}

app.listen(app.get('port'), function() {
    console.log('Example app listening on port', app.get('port'));
});
<?php
// work with get or post
$request = array_merge($_GET, $_POST);

// Check that this is a delivery receipt.
if (!isset($request['messageId']) OR !isset($request['status'])) {
    error_log('This is not a delivery receipt');
    return;
}

//Check if your message has been delivered correctly.
if ($request['status'] == 'delivered') {
    error_log("Your message to {$request['msisdn']} (message id {$request['messageId']}) was delivered.");
    error_log("The cost was {$request['price']}.");
} elseif ($request['status'] == 'accepted') {
    error_log("Your message to {$request['msisdn']} (message id {$request['messageId']}) was accepted by the carrier.");
    error_log("The cost was {$request['price']}.");
} else {
    error_log("Your message to {$request['msisdn']} has a status of: {$request['status']}.");
    error_log("Check err-code {$request['err-code']} against the documentation.");
}
#To run this code, replace the MyHandler in
#https://wiki.python.org/moin/BaseHttpServer With the following code,
from urlparse import urlparse, parse_qs
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(s):
        """Tell Nexmo that you have recieved the GET request."""
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
        """Parse parameters in the GET request"""
        parsed_path = urlparse(s.path)
        try:
                delivery_receipt = dict(
                [p.split('=') for p in parsed_path[4].split('&')])
        except:
                delivery_receipt = {}

            """Check the is a delivery receipt"""
        if 'text' in delivery_receipt:
            print ("This is not a delivery receipt")
        elif delivery_receipt['status'] != "delivered":
            print "Fail:" + delivery_receipt['status']
            + ": " + delivery_receipt['err-code'] +  ".\n"
        else:
            print "success"
                """Handle the DLR for a message sent succesfully
                    The following parameters in the delivery receipt should
                    match the ones in your request:
                     * Request - from, dlr - to
                     * Response - message-id, dlr - messageId
                     * Request - to, Responese - to, dlr - msisdn
                     * Request - client-ref, dlr - client-ref
                    Use the documentation to check the values."""
require 'socket'
require 'uri'

def handle_delivery_receipt(request_line)
#Parse the parameters and check if the message was delivered
  params = URI::decode_www_form(request_line).to_h
  if params["status"].nil? or params["messageId"].nil?
    p ('This is not a delivery receipt')
  elsif params["status"] != 'delivered'
    p ("Your request " + params["status"] +
    "because of " + params["err-code"] );
  else
    p ("Success for request " + params['messageId'] )
  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
  handle_delivery_receipt(path)

  # 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

Configuring your firewall

If you restrict inbound traffic (including delivery receipts), you need to whitelist the following IP addresses in your firewall. Inbound traffic from Nexmo might come from any of the following:

  • 174.37.245.32/29
  • 174.36.197.192/28
  • 173.193.199.16/28
  • 119.81.44.0/28