Delivery receipts

When Nexmo sends an SMS to a carrier, the carrier should return a delivery receipt (DLR). Carriers send delivery receipts at a moment of their choice, they do not have to wait for delivery confirmation.

How delivery receipts work

participant Your Application participant Nexmo participant Carrier participant Handset Your Application->Nexmo: Send an SMS Nexmo->Carrier: SMS Carrier->Handset: SMS Handset->Carrier: Delivery Receipt Carrier->Nexmo: Delivery Receipt Nexmo->Your Application: Delivery Receipt Webhook

Delivery receipts are either:

  • Carrier - returned when the SMS is received by the telecommunications service providers.
  • Handset - returned when the message is received on your user's handset.

If your message is longer than a single SMS, carriers should send a DLR for each part of the concatenated SMS. Handset delivery receipts for a concatenated message are delayed. This is because each part of the concatenated message takes about 10 seconds to be processed by the handset.

In practice, some carriers either do not send the delivery receipt or send a fake. Depending on the country you are sending to, Nexmo cannot be 100% certain that a successfully delivered delivery receipt means that the message reached your user.

When sending messages using the Nexmo SMS API  or Nexmo client library, you can specify the client-ref parameter. This allows you to assign a customer-specific reference code to each SMS sent. This reference code will then be included in the delivery receipt so you can identify specific clients.

Country specific

Before you start your messaging campaign:

  1. Check the Country Specific Features for the countries you are sending to.
  2. If the country you are sending to does not supply reliable DLRs, use Conversion API so Nexmo has more data points to ensure the best routing.

Once Nexmo receives a delivery receipt, we will send it to you using a webhook.

Implementing a delivery receipt webhook

To get a delivery receipt, you will need to implement a webhook endpoint the API can send the payload to.

Implement a webhook

const app = require('express')()
const bodyParser = require('body-parser')

app.use(bodyParser.urlencoded({ extended: true }))


function handleDeliveryReceipt(request, response) {
  const params = Object.assign(request.query, request.body)


Run your server

Save this file to your machine and run it using the node command:

$ node app.js

Implement a webhook


get("/webhooks/delivery-receipt", (req, res) -> {
    for (String param : req.queryParams()) {
        System.out.printf("%s: %s\n", param, req.queryParams(param));
    return "";

post("/webhooks/delivery-receipt", (req, res) -> {
    // The body will be form-encoded or a JSON object:
    if (req.contentType().startsWith("application/x-www-form-urlencoded")) {
        for (String param : req.queryParams()) {
            System.out.printf("%s: %s\n", param, req.queryParams(param));
    } else {
        IncomingDlrPayload jsonPayload = IncomingDlrPayload.fromJson(req.bodyAsBytes());

    return "";

Run your server

Add the following dependencies to your project:

compile 'com.nexmo:client:3.1.0'
compile "com.sparkjava:spark-core:2.6.0"

Then run your class in whichever way you find easiest.

Implement a webhook

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require 'vendor/autoload.php';

$app = new \Slim\App;

$handler = function (Request $request, Response $response) {
    $params = $request->getParsedBody();

    // Fall back to query parameters if needed
    if (!count($params)){
        $params = $request->getQueryParams();

    error_log(print_r($params, true));

    return $response->withStatus(204);

$app->get('/webhooks/delivery-receipt', $handler);
$app->post('/webhooks/delivery-receipt', $handler);


Run your server

Save this file to your machine and run it using the php command:

$ php -t . -S localhost:3000

Implement a webhook

from flask import Flask, request, jsonify
from pprint import pprint

app = Flask(__name__)

@app.route('/webhooks/delivery-receipt', methods=['GET', 'POST'])
def delivery_receipt():
    if request.is_json:
        data = dict(request.form) or dict(request.args)
    return ('', 204)

Run your server

Save this file to your machine and run it using the python command:

$ python

Implement a webhook

require 'sinatra'
require 'sinatra/multi_route'
require 'json'

helpers do
  def parsed_body
     json? ? JSON.parse( : {}

  def json?
    request.content_type == 'application/json'

route :get, :post, '/webhooks/delivery-receipt' do
  puts params.merge(parsed_body)
  status 204

set :port, 3000

Run your server

Save this file to your machine and run it using the ruby command:

$ ruby app.rb

Make your machine publicly available

If you are running the code above on your desktop/laptop computer, you will need to make your computer publicly available so Nexmo can send the delivery receipt to it. We recommend you try ngrok  to do this quite easily.

See this blog post  for more details about how to use ngrok.

Tell Nexmo about your webhook endpoint

Now that your webhook endpoint is running, you need to tell Nexmo to send delivery receipts to this address.

The webhook URL is the forwarding address given to you by ngrok combined with /webhooks/delivery-receipt.

For instance:

Paste your webhook URL into the settings section of Nexmo Dashboard  in the field marked labelled "Webhook URL for Delivery Receipt" and press 'Save Changes'.


Send a message

We are now ready to send the message, you can do this with the Nexmo CLI as such:

$ nexmo sms 447700900000 "A text message sent using the Nexmo SMS API"

Shortly after your server should print the parameters:

  "msisdn": "447700900000",
  "to": "Nexmo CLI",
  "network-code": "12345",
  "messageId": "<REDACTED>",
  "price": "0.03330000",
  "status": "delivered",
  "scts": "1703291538",
  "err-code": "0",
  "message-timestamp": "2020-01-01 14:00:00"

See also

  • Webhooks Guide — a detailed guide to how to use webhooks with Nexmo's platform