https://a.storyblok.com/f/270183/1368x665/46b065123f/26sep-receive_an_sms_delivery_receipt_with_node-blog_r1.jpg

Receive an SMS Delivery Receipt With Node.js and the Messages API

Published on September 30, 2026

Time to read: 5 minutes

Introduction

When you send a text message using the Vonage APIs, the HTTP response tells you whether the message was accepted for sending. It doesn’t tell you whether it actually reached the recipient’s handset. 

To find that out, you need a delivery receipt. In this tutorial, you’ll learn how to receive SMS delivery receipts using the Vonage Messages API and Node.js. 

Prerequisites 

Before you begin, make sure you have the following: 

  • Node.js installed. This tutorial uses Node.js 18 or later. 

  • ngrok installed and a free account set up. You’ll use it to expose your local server to the internet so Vonage can reach your webhook. 

  • The Vonage CLI installed. Run npm install -g @vonage/cli to install it globally. 

How Delivery Receipts Work With the Messages API 

When a message is delivered, the mobile carrier returns a delivery receipt to Vonage. If you’ve configured a webhook, Vonage forwards that receipt to your endpoint as a POST request. 

The Messages API uses a Message Status webhook for this purpose. This is the Messages API equivalent of the Delivery Receipt (DLR) used by the SMS API. Rather than a single callback, you’ll typically receive two: one with a status of submitted when the message is accepted by the carrier, and a second with a status of delivered once it reaches the handset. 

The Messages API status webhook supports the following status values: 

  • submitted: the message has been accepted for delivery 

  • delivered: the message has been delivered to the handset 

  • rejected: the carrier refused to deliver the message 

  • undeliverable: Messages API was unable to connect to the messaging provider, may be due to a messaging provider outage or other incident 

See the Messages API Status Callbacks documentation for more details. 

Set Up ngrok 

ngrok is a cross-platform tool that creates a public URL pointing to a server running on your local machine. You’ll use it to expose your webhook so Vonage can send POST requests to it during development. 

Once ngrok is installed and you’re logged in, run the following command: 

ngrok http 3000 

After it starts, ngrok will display a Forwarding URL that looks something like this: 

Forwarding  https://abcd1234.ngrok-free.app -> http://localhost:3000 

Note that URL; you’ll need it in the next step. 

Note: On the free plan, the ngrok URL changes every time you restart the server. You’ll need to update your webhook URLs in the Vonage Dashboard whenever that happens. 

Configure Your Vonage Account 

Switch to the Messages API 

Sign in to your Vonage API Dashboard and go to API Settings. In the Messaging API type section, select Messages API and save your changes. 

This tells Vonage to use the Messages API format for all SMS webhooks on your account. 

Create a Vonage Application 

The Messages API uses application-level configuration, meaning your webhook URLs and authentication credentials are tied to a specific Vonage Application and override the account-level settings. 

In the Dashboard, go to Applications and click Create a new application. Give it a name, something like SMS Delivery Receipts works well. 

Click Generate public and private key. Your browser will download a private.key file. You’ll need to move it into your project directory once created, then add it to your .gitignore so it doesn’t end up in version control. 

Under Capabilities, enable Messages and fill in both webhook URLs using your ngrok Forwarding URL: 

Webhook 

URL 

Inbound URL 

https://YOUR_NGROK_URL/webhooks/inbound-message 

Status URL 

https://YOUR_NGROK_URL/webhooks/message-status 

 

Click Generate new application to save. On the application page, scroll to Link virtual numbers and link the Vonage number you’ll use to send SMS messages. 

Set Up the Node.js Project 

Open a terminal, create a new directory for your project, and initialise it: 

mkdir sms-delivery-receipt 
cd sms-delivery-receipt 
npm init -y 

Install Express and body-parser: 

npm install express body-parser --save 

You’ll use Express to handle incoming webhook requests and body-parser to parse the JSON payloads.  

Write the Webhook Handler 

Create a file called index.js and add the following code: 

const express = require('express'); 
const bodyParser = require('body-parser'); 
const app = express(); 
 
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: true })); 
 
// Receives delivery status updates from the Messages API 
app.post('/webhooks/message-status', (req, res) => { 
  console.log(req.body); 
  res.status(200).end(); 
}); 
 
// Required — must return 200 to prevent callback queuing 
app.post('/webhooks/inbound-message', (req, res) => { 
  console.log(req.body); 
  res.status(200).end(); 
}); 
 
app.listen(process.env.PORT || 3000, () => { 
  console.log('Server listening on port 3000'); 
}); 

The /webhooks/message-status endpoint is where Vonage will send delivery status updates. The handler logs the request body to the console and returns a 200 response. 

The /webhooks/inbound-message endpoint handles incoming SMS messages. You don’t need to do anything with inbound messages for this tutorial, but the endpoint must exist and return 200. Without it, Vonage will keep retrying inbound message callbacks and may queue up a backlog. 

Run the Application 

Start the server with the following command: 

node index.js 

You should see the server listening on port 3000 in your terminal. 

Now send a text message from your Vonage virtual number to your mobile phone. You can do this using the Vonage CLI or a curl request: 

curl -X POST https://api.nexmo.com/v1/messages \ 
  -H 'Authorization: Bearer $JWT' \ 
  -H 'Content-Type: application/json' \ 
  -H 'Accept: application/json' \ 
  -d '{ 
    "message_type": "text", 
    "text": "Testing delivery receipts with the Messages API", 
    "to": "YOUR_PHONE_NUMBER", 
    "from": "YOUR_VONAGE_NUMBER", 
    "channel": "sms" 
  }' 

Replace YOUR_PHONE_NUMBER with your personal number (including country code, no leading +), YOUR_VONAGE_NUMBER with your Vonage virtual number, and $JWT with a JWT generated for your application. 

What to Expect 

If the message is delivered successfully, you’ll see two callbacks in your terminal. The first arrives shortly after sending: 

{ 

  to: '447700900000', 

  from: '18339999999', 

  channel: 'sms', 

  message_uuid: '91da7001-2db2-4ecb-a5b1-f0cdba08a262', 

  timestamp: '2026-08-10T09:21:02Z', 

  usage: { price: '0.0484', currency: 'EUR' }, 

  sms: { count_total: '1' }, 

  status: 'submitted', 

  destination: { network_code: '23431' } 

} 

A few seconds later, you’ll receive the delivery confirmation: 

{ 

  to: '447700900000', 

  from: '18339999999', 

  channel: 'sms', 

  message_uuid: '91da7001-2db2-4ecb-a5b1-f0cdba08a262', 

  timestamp: '2026-08-10T09:21:09Z', 

  sms: { count_total: '1' }, 

  status: 'delivered', 

  destination: { network_code: '23431' } 

} 

The message_uuid field links both callbacks to the same message. You can use it to track delivery status in your application. For example, you can store it in a database when you send the message and update the record when the status callback arrives. 

Conclusion 

You’ve set up a Node.js application that receives SMS delivery receipts using the Vonage Messages API. Along the way, you configured a Vonage Application with a Message Status webhook, wrote handlers for both the status and inbound message endpoints, and tested the full delivery flow. 

From here, you could extend this project to: 

  • Store delivery statuses in a database and build a dashboard to track them 

  • Send alerts when a message fails to deliver 

  • Explore other channels supported by the Messages API, such as WhatsApp or RCS, using the same webhook setup 

For more details, have a look at the following resources: 

Have a question or want to share what you're building?

Stay connected and keep up with the latest developer news, tips, and events.

Share:

https://a.storyblok.com/f/270183/372x373/36054b72d0/julia-biro.png
Julia BiroDeveloper Advocate

Julia is committed to empowering fellow developers by creating tutorials, guides, and practical resources. With a background in outreach and education, she aims to make technology more accessible and enhance the overall developer experience. You can often find her at local community events.