Frequently Asked Questions
Use the Vonage Messages API and Node.js SDK. After setting up a Vonage application and linking a virtual number, you can send SMS messages programmatically via the /send-sms
endpoint, providing the recipient's number and the message text in the request body. The Vonage SDK simplifies the API interaction, handling authentication and request formatting.
The Vonage Messages API is a unified platform for sending and receiving messages across multiple channels, including SMS, MMS, WhatsApp, and more. This guide focuses on using the API for sending SMS messages and receiving delivery status updates, enabling reliable two-way communication.
Ngrok creates a secure tunnel from your local development environment to the public internet. This allows Vonage's webhooks (inbound SMS and delivery receipts) to reach your local server during development, as these webhooks require a publicly accessible URL.
Database integration is highly recommended for production applications. A database (like PostgreSQL or MongoDB) helps persist message data (inbound, outbound, delivery statuses), enabling tracking, analytics, and features like message history or user opt-out management.
Yes, you can receive inbound SMS messages to your Vonage virtual number. By configuring the inbound webhook URL in your Vonage Application settings, Vonage will forward incoming messages to your application as POST requests, allowing you to process and respond to them.
Vonage sends delivery status updates (DLRs) via webhooks to the Status URL configured in your Vonage Application. The payload contains the message_uuid
and status
(e.g., 'delivered', 'failed'). You can use this information to update message status in your database and implement logic based on delivery outcomes.
A Vonage Application acts as a container for your communication configuration, including API keys, webhook URLs, and linked virtual numbers. It centralizes the settings for your project and simplifies integration with Vonage services.
Key security measures include using HTTPS for all communication, securing your .env
file containing API credentials, validating input to prevent vulnerabilities, and implementing rate limiting on public endpoints to protect against abuse.
Implement try...catch
blocks around vonage.messages.send()
to handle API errors. Inspect the err
object for details. For temporary failures (e.g., network issues), consider implementing retry logic with exponential backoff using a library like async-retry
.
Log key events (app start, SMS sent/received, status updates), errors with stack traces, message_uuid
for tracking, and any configuration issues. Use a dedicated logging library (like pino
or winston
) for structured logging and advanced features.
Verify ngrok
is running (if in development) and forwarding to the correct port. Ensure the webhook URLs in your Vonage Application settings match the ngrok
URL and paths (/webhooks/inbound
, /webhooks/status
). Check your firewall and application logs for errors.
Confirm the Status URL is correctly set in your Vonage Application. Note that delivery receipts (DLRs) are carrier-dependent and not always reliable. Some networks/countries might not provide delivered
status. The message might still be delivered even if the status is not 'delivered'.
E.164 is an international telephone number format (e.g., +14155550101). Always use this format for recipient numbers (to
) when sending SMS with Vonage. This ensures correct routing and avoids issues with regional number variations.
Vonage handles concatenation of long messages exceeding the standard SMS character limit. Be aware that longer messages are split into segments, and you are billed per segment. Consider message length and costs when designing your application.
Note: This filename references "messagebird" and "next-js" but the content covers Vonage (formerly Nexmo) with Express framework. The technical content and code examples are accurate for Vonage Messages API.
Last Updated: October 5, 2025 | Vonage Messages API Status: General Availability | SDK Version: @vonage/server-sdk v3.x+
Build a production-ready Node.js application with Express to send SMS messages, receive inbound SMS, and handle delivery status updates (callbacks) via the Vonage Messages API. This guide covers everything from project setup and core implementation to error handling, security, deployment, and testing.
Technologies: Node.js, Express, Vonage Messages API, Vonage Node SDK (@vonage/server-sdk v3.x+), ngrok (for development)
Official Documentation: Vonage Messages API | Vonage Node SDK
What You'll Build
Create a Node.js application using Express that can:
Why Build This: Create reliable, two-way SMS communication in your Node.js environment. Send messages, confirm their delivery status, and react to user messages – enabling interactive SMS applications, notification systems with delivery confirmation, and more.
Technologies You'll Use:
@vonage/server-sdk
): Simplifies interaction with Vonage APIs in Node.js.env
file intoprocess.env
– keeps sensitive credentials out of source codeSystem Architecture:
What You'll Have at the End: A functional Node.js Express application that sends SMS, receives SMS, and tracks delivery statuses – structured with security, error handling, and deployment best practices.
Before You Start:
ngrok config add-authtoken YOUR_TOKEN
1. Set Up Your Project
Initialize your Node.js project and install the necessary dependencies.
Create Your Project Directory: Open your terminal and create a new directory for your project.
Initialize Your Node.js Project: Initialize with npm (or yarn). The
-y
flag accepts default settings.This creates a
package.json
file.Install Your Dependencies: Install Express for the web server, the Vonage Node SDK for API interaction, and
dotenv
for environment variable management. Install v3.x or higher of @vonage/server-sdk for Messages API support.Current Versions (October 2025):
express
: v4.18.x or higher@vonage/server-sdk
: v3.15.x or higherdotenv
: v16.x or higherCreate Your Project Structure: Build a basic structure for clarity.
src/app.js
: Your main application codeconfig/
: (Optional) For complex configurations later.env
: Stores sensitive credentials (API keys, etc.) – never commit this file.env.example
: Template showing required environment variables – commit this file.gitignore
: Specifies files/directories Git should ignoreprivate.key
: Placeholder for your Vonage Application private key – you'll replace this with the downloaded file from VonageConfigure Your
.gitignore
: Add common Node.js ignores and ensure.env
and the private key are excluded.Define Your Environment Variables (
.env.example
): List the variables you need. Copy this content into both.env
and.env.example
. Fill.env
with actual values later.Why Use
.env
? Store credentials and configuration separate from code – crucial for security and flexibility across environments (development, staging, production).2. Configure Vonage for SMS and Webhooks
Set up your Vonage account and application to enable SMS sending and receiving with callbacks.
Reference: Vonage Applications Documentation
Log in to Your Vonage Dashboard: Access your account at dashboard.nexmo.com.
Set Your Default SMS API to "Messages API":
Create Your Vonage Application: Vonage Applications contain your communication configurations, including keys and webhooks.
private.key
file. Save this in your project root, replacing the placeholderprivate.key
file. Vonage stores the public key. These keys enable JWT authentication for Messages API requests.ngrok
comes in)Start ngrok: Open a new terminal window in your project directory and start
ngrok
, forwarding traffic to the port your Express app will use (defaulting to 3000 from.env.example
).ngrok
providesForwarding
URLs (http and https). Copy thehttps
URL – looks likehttps://random-subdomain.ngrok.io
.Configure Your Webhook URLs:
ngrok
https URL into the webhook fields, appending specific paths:YOUR_NGROK_HTTPS_URL/webhooks/inbound
YOUR_NGROK_HTTPS_URL/webhooks/status
Copy Your Application ID: After creation, you'll see the application details page. Copy the Application ID – you need this for your
.env
file.Link Your Vonage Number:
Update Your
.env
File: Open your.env
file (the one without.example
) and fill in the values you obtained:Security:
.env
contains secrets. Keep it in your.gitignore
and never commit to version control. Use environment variable management from your deployment platform in production.3. Write Your Core SMS Functionality
Build the Node.js/Express code to send and receive SMS.
File:
src/app.js
Code Breakdown:
dotenv
,express
,Vonage SDK
,path
).dotenv.config()
loads variables from.env
JSON
andURL-encoded
request bodies – crucial for handling webhook payloadsapplicationId
andprivateKey
path from environment variables. Includes error checking for missing variables. Usespath.resolve
to ensure the path toprivate.key
is correct regardless of where you run the script/send-sms
Route: APOST
endpoint to send SMS. Expectsto
(recipient number) andtext
(message content) in the JSON body. Validates input and callsvonage.messages.send()
using thesms
channel. Returns success/error responses with helpful messages. Includes a warning about production protection/webhooks/inbound
Route: APOST
endpoint matching your Inbound URL in the Vonage Application. Vonage sends data here when your virtual number receives SMS. Logs the payload (req.body
), includes aTODO
for your logic, and immediately sends200 OK
. Send the 200 OK quickly before heavy processing./webhooks/status
Route: APOST
endpoint matching your Status URL. Vonage sends delivery status updates here for messages you sent. Logs the payload, includes aTODO
, and sends200 OK
/health
Route: Simple endpoint for monitoring systems to check if your application is runningAPP_PORT
(defaulting to 3000) and starts the Express server only if you run the script directly (usingif (require.main === module)
). Includes helpful reminders aboutngrok
configuration during developmentapp
instance for testing frameworks like Supertest4. Run and Test Locally
Ensure ngrok is running (from Step 2.4) and forwarding to port 3000. Verify the HTTPS URL matches the one in your Vonage Application webhook settings.
Verify your
.env
file contains your Vonage Application ID, number, and the correct path toprivate.key
.Start Your Node.js Application: In the terminal where you created the project (not the
ngrok
one), run:You should see "Server listening at http://localhost:3000" and the
ngrok
reminder messages.Test Sending SMS: Use
curl
or Postman to send a POST request to your local/send-sms
endpoint. ReplaceYOUR_PHONE_NUMBER
with your mobile number in E.164 format (e.g.,14155550101
).node src/app.js
logs "Attempting to send SMS…" then "Message sent successfully: …" with amessage_uuid
curl
command receives JSON:{"message_uuid":"...","status":"Message submitted"}
Test Receiving Status Updates:
node src/app.js
logs "--- Delivery Status Update Received ---" followed by the JSON payload from Vonage. Look for themessage_uuid
matching your sent message and astatus
field (e.g.,delivered
,accepted
,buffered
) – exact status depends on carrier supportTest Receiving Inbound SMS:
node src/app.js
logs "--- Inbound SMS Received ---" followed by the JSON payload containing message details (from
,text
, etc.)5. Implement Error Handling, Logging, and Retries
Build production-ready error handling and logging.
Best Practices (October 2025):
Error Handling:
try...catch
block aroundvonage.messages.send()
handles specific API call errors. Inspect theerr
object (especiallyerr.response.data
orerr.message
) for details from the Vonage API401
(authentication failed) – Check your VONAGE_APPLICATION_ID and private.key file403
(forbidden) – Verify permissions and account status422
(validation error) – Ensure phone numbers are in E.164 format429
(rate limit exceeded) – Implement exponential backoff retries500
(server error) – Retry with exponential backoff/webhooks/inbound
,/webhooks/status
) intry...catch
blocks. Log errors but still send a200 OK
response to Vonage to prevent retries (unless the request is fundamentally invalid)app.use((err, req, res, next) => {...})
) catches errors not caught in specific routesLogging:
pino
(recommended for performance) orwinston
in production instead ofconsole.log
message_uuid
), configuration issuesRetries:
200 OK
within ~30 seconds. Respond quickly with200 OK
5xx
error from Vonage), implement retry strategy with exponential backoff. Use libraries likeasync-retry
200 OK
(e.g., database write fails), log the error and use a background job queue (BullMQ, Kue, RabbitMQ) to retry the processing task independently6. Add Database Integration (Optional but Recommended)
Integrate a database for persistence and tracking.
Recommended ORMs/ODMs (October 2025):
Design Your Schema: Create a
messages
table:message_uuid
(VARCHAR/TEXT, PRIMARY KEY/UNIQUE) - From Vonage response/webhooksdirection
(ENUM/VARCHAR: 'outbound', 'inbound')to_number
(VARCHAR)from_number
(VARCHAR)body
(TEXT)status
(VARCHAR, e.g., 'submitted', 'delivered', 'failed', 'read') - Updated via status webhookvonage_timestamp
(TIMESTAMP WITH TIME ZONE) - From webhook payloaderror_code
(VARCHAR, nullable) - From status webhook if failed/rejectedcreated_at
(TIMESTAMP WITH TIME ZONE)updated_at
(TIMESTAMP WITH TIME ZONE)Implement Your Database Logic:
vonage.messages.send()
, insert a record withmessage_uuid
,direction='outbound'
,to
,from
,text
,status='submitted'
, and timestampsreq.body.message_uuid
and update itsstatus
,vonage_timestamp
,error_code
(if present), andupdated_at
. Handle cases where themessage_uuid
might not be found (log an error)direction='inbound'
, details fromreq.body
,status='received'
, and timestamps7. Secure Your Application
Protect your application and user data.
Secure Your Webhooks:
https
URLs for webhooks (required by Vonage as of October 2025)/webhooks/inbound
and/webhooks/status
are reasonably specificManage Your Secrets:
.env
locally, platform-provided secrets in production). Don't commit.env
orprivate.key
Validate Your Input:
/send-sms
). Ensure required fields are present, types are correct, and formats match (e.g., E.164 for phone numbers). Use libraries likeexpress-validator
orjoi
dompurify
(if rendering in HTML)Implement Rate Limiting:
/send-sms
if exposed) from abuse. Use middleware likeexpress-rate-limit
Use HTTPS:
ngrok
provides this locally. Use a load balancer or reverse proxy (Nginx, Caddy, or platform services like AWS ALB/Cloudflare) to handle SSL termination in production8. Handle Special SMS Cases
Address real-world SMS nuances.
Format Your Numbers:
+14155550101
or14155550101
without spaces) when sendingto
numbers to Vonage. Vonage providesfrom
numbers in webhooks in similar formatUnderstand Character Encoding & Concatenation:
Handle Opt-Outs (STOP):
Interpret Delivery Statuses:
delivered
status updates (DLRs – Delivery Receipt Reports)submitted
,accepted
,buffered
,delivered
,failed
,rejected
,expired
failed
orrejected
statuses reliably indicate non-deliveryManage Time Zones:
timestamp
) are in UTC (ISO 8601 format:YYYY-MM-DDTHH:MM:SS.sssZ
)9. Optimize Your Performance
Ensure your webhook handlers are fast and efficient.
Respond Quickly:
200 OK
response within 30 seconds (Vonage timeout). Aim for <1 second response time – this is your most critical optimizationProcess in the Background:
Index Your Database:
message_uuid
(PRIMARY KEY or UNIQUE index)status
(if you query by status frequently)created_at
(for time-range queries)Cache Frequently Accessed Data:
10. Monitor Your Application
Understand how your application performs and diagnose issues.
Implement Health Checks:
/health
endpoint is your starting point. Production monitoring systems (AWS CloudWatch, Datadog, Prometheus/Grafana, UptimeRobot) can ping this endpoint to verify your app is liveTrack Your Metrics:
send
calls)prom-client
for Prometheus or integrate with APM tools (Datadog APM, New Relic)Track Your Errors:
Aggregate Your Logs:
Create Your Dashboards:
11. Troubleshoot Common Issues
Solve common problems and their solutions.
Webhooks Not Reaching Your App:
ngrok
running? Free URLs expire after 2-8 hours – restart ngrok and update Vonage Application webhook URLsYour App Receives Webhooks but Errors Occur:
req.body
correctly? Ensureexpress.json()
middleware is usedres.status(200).end()
reliably and quickly? Check the Vonage Dashboard (Logs section) for webhook retry attemptsSMS Sending Fails:
VONAGE_APPLICATION_ID
and the path/content ofVONAGE_PRIVATE_KEY_PATH
in.env
VONAGE_NUMBER
correctly set in.env
and linked to the Vonage Application used for initialization?err
object from thecatch
block in/send-sms
–err.response.data
often contains detailed error codes and messages from VonageNo Delivery Status Updates (
/webhooks/status
not hit):Messages API vs. Legacy SMS API:
vonage.messages.send()
(Messages API) vs.vonage.sms.send()
(legacy SMS API)12. Deploy Your Application
Move from local development to production.
Choose Your Deployment Platform (October 2025):
Serverless/PaaS Options:
Container/VM Options:
Handle Your Private Key Securely: Provide the
private.key
file to your production environment securely:VONAGE_PRIVATE_KEY_BASE64
, decode at runtimeRun Your Build Process: If using TypeScript or a build step, ensure it runs before deployment
Manage Your Process: Use a process manager like
pm2
or rely on the platform's built-in service management (e.g., systemd, Heroku dynos) to keep your Node.js app running, handle restarts, and manage logsConfigure HTTPS: Ensure your deployment platform handles HTTPS termination (most PaaS/Serverless platforms do) or configure a reverse proxy (Nginx, Caddy) if deploying to a VM
Set Up Your CI/CD Pipeline: Modern CI/CD tools: