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.envfile 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_TOKEN1. 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
-yflag accepts default settings.This creates a
package.jsonfile.Install Your Dependencies: Install Express for the web server, the Vonage Node SDK for API interaction, and
dotenvfor 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.envand the private key are excluded.Define Your Environment Variables (
.env.example): List the variables you need. Copy this content into both.envand.env.example. Fill.envwith 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.keyfile. Save this in your project root, replacing the placeholderprivate.keyfile. Vonage stores the public key. These keys enable JWT authentication for Messages API requests.ngrokcomes 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).ngrokprovidesForwardingURLs (http and https). Copy thehttpsURL – looks likehttps://random-subdomain.ngrok.io.Configure Your Webhook URLs:
ngrokhttps URL into the webhook fields, appending specific paths:YOUR_NGROK_HTTPS_URL/webhooks/inboundYOUR_NGROK_HTTPS_URL/webhooks/statusCopy Your Application ID: After creation, you'll see the application details page. Copy the Application ID – you need this for your
.envfile.Link Your Vonage Number:
Update Your
.envFile: Open your.envfile (the one without.example) and fill in the values you obtained:Security:
.envcontains secrets. Keep it in your.gitignoreand 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.jsCode Breakdown:
dotenv,express,Vonage SDK,path).dotenv.config()loads variables from.envJSONandURL-encodedrequest bodies – crucial for handling webhook payloadsapplicationIdandprivateKeypath from environment variables. Includes error checking for missing variables. Usespath.resolveto ensure the path toprivate.keyis correct regardless of where you run the script/send-smsRoute: APOSTendpoint to send SMS. Expectsto(recipient number) andtext(message content) in the JSON body. Validates input and callsvonage.messages.send()using thesmschannel. Returns success/error responses with helpful messages. Includes a warning about production protection/webhooks/inboundRoute: APOSTendpoint matching your Inbound URL in the Vonage Application. Vonage sends data here when your virtual number receives SMS. Logs the payload (req.body), includes aTODOfor your logic, and immediately sends200 OK. Send the 200 OK quickly before heavy processing./webhooks/statusRoute: APOSTendpoint matching your Status URL. Vonage sends delivery status updates here for messages you sent. Logs the payload, includes aTODO, and sends200 OK/healthRoute: 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 aboutngrokconfiguration during developmentappinstance 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
.envfile 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
ngrokone), run:You should see "Server listening at http://localhost:3000" and the
ngrokreminder messages.Test Sending SMS: Use
curlor Postman to send a POST request to your local/send-smsendpoint. ReplaceYOUR_PHONE_NUMBERwith your mobile number in E.164 format (e.g.,14155550101).node src/app.jslogs "Attempting to send SMS…" then "Message sent successfully: …" with amessage_uuidcurlcommand receives JSON:{"message_uuid":"...","status":"Message submitted"}Test Receiving Status Updates:
node src/app.jslogs "--- Delivery Status Update Received ---" followed by the JSON payload from Vonage. Look for themessage_uuidmatching your sent message and astatusfield (e.g.,delivered,accepted,buffered) – exact status depends on carrier supportTest Receiving Inbound SMS:
node src/app.jslogs "--- 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...catchblock aroundvonage.messages.send()handles specific API call errors. Inspect theerrobject (especiallyerr.response.dataorerr.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...catchblocks. Log errors but still send a200 OKresponse 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) orwinstonin production instead ofconsole.logmessage_uuid), configuration issuesRetries:
200 OKwithin ~30 seconds. Respond quickly with200 OK5xxerror from Vonage), implement retry strategy with exponential backoff. Use libraries likeasync-retry200 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
messagestable: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_uuidand update itsstatus,vonage_timestamp,error_code(if present), andupdated_at. Handle cases where themessage_uuidmight 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:
httpsURLs for webhooks (required by Vonage as of October 2025)/webhooks/inboundand/webhooks/statusare reasonably specificManage Your Secrets:
.envlocally, platform-provided secrets in production). Don't commit.envorprivate.keyValidate 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-validatororjoidompurify(if rendering in HTML)Implement Rate Limiting:
/send-smsif exposed) from abuse. Use middleware likeexpress-rate-limitUse HTTPS:
ngrokprovides 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:
+14155550101or14155550101without spaces) when sendingtonumbers to Vonage. Vonage providesfromnumbers in webhooks in similar formatUnderstand Character Encoding & Concatenation:
Handle Opt-Outs (STOP):
Interpret Delivery Statuses:
deliveredstatus updates (DLRs – Delivery Receipt Reports)submitted,accepted,buffered,delivered,failed,rejected,expiredfailedorrejectedstatuses 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 OKresponse 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:
/healthendpoint 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:
sendcalls)prom-clientfor 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:
ngrokrunning? Free URLs expire after 2-8 hours – restart ngrok and update Vonage Application webhook URLsYour App Receives Webhooks but Errors Occur:
req.bodycorrectly? 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_IDand the path/content ofVONAGE_PRIVATE_KEY_PATHin.envVONAGE_NUMBERcorrectly set in.envand linked to the Vonage Application used for initialization?errobject from thecatchblock in/send-sms–err.response.dataoften contains detailed error codes and messages from VonageNo Delivery Status Updates (
/webhooks/statusnot 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.keyfile 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
pm2or 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: