code examples
code examples
Build Two-Way SMS Messaging with Node.js and Express using Vonage API
Complete guide to building a production-ready Node.js Express application for SMS messaging with Vonage API. Includes setup, authentication, error handling, security, rate limiting, and deployment strategies.
⚠️ CRITICAL NOTICE: Title-Content Mismatch
The filename of this article references "Twilio + Next.js + NextAuth + Inbound Two-Way Messaging" but the content demonstrates a Vonage + Express + Outbound SMS implementation.
Key Differences:
- API Provider: This guide uses Vonage API (not Twilio API)
- Framework: This guide uses Express (not Next.js with NextAuth)
- Functionality: This guide covers outbound SMS sending (not inbound two-way messaging with webhooks)
For Twilio + Next.js + NextAuth + Inbound Two-Way Messaging:
- Twilio uses different SDK (
twilionpm package) and authentication (Account SID + Auth Token)- Next.js uses API routes (
/pages/apior/app/api) instead of Express routes- NextAuth.js provides authentication middleware for protecting API routes
- Inbound two-way messaging requires webhook endpoints that receive POST requests from Twilio and return TwiML responses
- Twilio webhooks send data as
application/x-www-form-urlencoded, requiringbody-parseror Next.jsurlencodedparsingIf you need the Twilio + Next.js + NextAuth + Inbound implementation, refer to:
- Twilio's official docs: https://www.twilio.com/docs/messaging/tutorials/how-to-receive-and-reply/node-js
- Next.js API routes: https://nextjs.org/docs/api-routes/introduction
- NextAuth.js: https://next-auth.js.org/
This guide provides a complete walkthrough for building a production-ready Node.js application using the Express framework to send SMS messages via the Vonage API. You'll learn everything from project setup and configuration to implementing core functionality, error handling, security, and testing.
By the end of this guide, you'll have a simple but robust API endpoint capable of accepting a destination phone number and a message, then using Vonage to deliver that message as an SMS.
1. Node.js Project Setup: Installing Vonage SDK and Express Dependencies
Initialize your Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for the project, then navigate into it.
bashmkdir vonage-sms-sender cd vonage-sms-sender -
Initialize Node.js Project: Initialize the project using npm. The
-yflag accepts the default settings.bashnpm init -yThis creates a
package.jsonfile. -
Install Dependencies: Install Express, the Vonage Server SDK, and dotenv.
bashnpm install express @vonage/server-sdk dotenv -
Enable ES Module Syntax (Optional but Recommended): To use modern
importsyntax instead ofrequire, open yourpackage.jsonfile and add the following line at the top level:json// package.json { "name": "vonage-sms-sender", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@vonage/server-sdk": "^3.x.x", "dotenv": "^16.x.x", "express": "^4.x.x" } }Why
type: module?: It enables the use of standard ES6import/exportsyntax, which is common in modern JavaScript development. Note: The versions listed (e.g.,^3.x.x) indicate compatibility with that major version. Always check for and use the latest stable versions of these packages for security and feature updates. -
Create Project Structure: Create the main application file and a file for Vonage-related logic.
bashtouch index.js vonageService.jsYour project structure should now look like this:
textvonage-sms-sender/ node_modules/ .env .gitignore index.js package.json package-lock.json vonageService.js -
Create
.envFile: This file will store your sensitive API credentials. Create a file named.envin the root of your project. Never commit this file to version control.env# .env PORT=3000 # Vonage Credentials VONAGE_API_KEY=YOUR_VONAGE_API_KEY VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET VONAGE_FROM_NUMBER=YOUR_VONAGE_NUMBER_OR_SENDER_IDPurpose of
.env: Separates configuration and secrets from code, enhancing security and making it easier to manage different environments (development, production). -
Create
.gitignoreFile: Prevent sensitive files and unnecessary directories from being tracked by Git. Create a file named.gitignore.text# .gitignore node_modules/ .env *.log
2. Vonage API Configuration: Authentication and SDK Integration
Configure the Vonage SDK and set up the logic to send SMS messages.
-
Obtain Vonage API Credentials:
- Log in to your Vonage API Dashboard.
- On the main dashboard page ("Getting started"), you'll find your API key and API secret at the top.
- Copy these values.
-
Configure
.env:- Open the
.envfile you created earlier. - Replace
YOUR_VONAGE_API_KEYwith your actual API key. - Replace
YOUR_VONAGE_API_SECRETwith your actual API secret.
- Open the
-
Set the 'From' Number:
- In the Vonage Dashboard, navigate to Numbers > Your numbers.
- If you don't have a number, you may need to buy one (check pricing). Alternatively, for some destinations, you can use an Alphanumeric Sender ID (e.g., "MyApp"). Note that Sender ID support varies by country and carrier.
- Copy your purchased Vonage virtual number (including the country code, e.g.,
15551234567) or your desired Sender ID. - Replace
YOUR_VONAGE_NUMBER_OR_SENDER_IDin your.envfile with this value.
-
Whitelist Test Numbers (Trial Accounts Only):
- Crucial: If you're using a trial Vonage account, you can only send SMS messages to numbers you've verified and added to your test list.
- In the Vonage Dashboard, navigate to the Sandbox & Test Numbers section (often accessible via a prompt on the dashboard or under your account settings/profile).
- Add the phone number(s) you intend to send test messages to. You'll typically need to verify ownership by entering a code sent via SMS or voice call.
- Why?: This prevents abuse of the free trial credits. Sending to a non-whitelisted number from a trial account will result in an error (see Troubleshooting).
-
Implement Vonage Service Logic: Open
vonageService.jsand add the following code to initialize the SDK and create the sending function.javascript// vonageService.js import { Vonage } from '@vonage/server-sdk'; import 'dotenv/config'; // Load .env variables // Initialize Vonage client with API key and secret const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, }); const fromNumber = process.env.VONAGE_FROM_NUMBER; /** * Sends an SMS message using the Vonage API. * Uses the vonage.sms.send() method from the SDK v3. * * @param {string} to - The recipient's phone number in E.164 format (e.g., +15551234567). * @param {string} text - The content of the SMS message. * @returns {Promise<object>} - A promise that resolves with the Vonage API response. * @throws {Error} - Throws an error if the API call fails. */ export async function sendSms(to, text) { console.log(`Attempting to send SMS from ${fromNumber} to ${to}`); try { const response = await vonage.sms.send({ to, from: fromNumber, text }); console.log('SMS submitted successfully:', response); // Note: Success here means the message was *accepted* by Vonage, not necessarily delivered. // Delivery status comes via webhooks (not covered in this basic guide). // Check the status of the first (and likely only) message part if (response.messages[0].status === '0') { console.log(`Message to ${to} submitted successfully with ID: ${response.messages[0]['message-id']}`); return response; // Return the full response object on success } else { const errorCode = response.messages[0].status; const errorText = response.messages[0]['error-text']; console.error(`Message failed with error code ${errorCode}: ${errorText}`); // Throw a more specific error based on the Vonage response throw new Error(`Vonage API Error ${errorCode}: ${errorText}`); } } catch (error) { console.error('Error sending SMS:', error); // Re-throw the error to be caught by the calling function (API endpoint) throw error; } }Code Explanation:
- We import
Vonageand load.envvariables. - We initialize the
Vonageclient using the API key and secret fromprocess.env. - The
sendSmsfunction is anasyncfunction that takes the recipient (to) and message (text). - It calls
vonage.sms.send(), passing an object withto,from(read from.env), andtext. Note: Thevonage.sms.send()method is part of the newer V3 SDK syntax, preferred over the oldervonage.message.sendSms()seen in some research links. - Basic logging is included for success and errors.
- We check the
statusfield in the response. A status of'0'indicates success. Other statuses indicate errors, and we extract the error message. - Errors are caught and re-thrown to be handled by the API layer.
- We import
Understanding Vonage SMS API Status Codes
When you send an SMS via the Vonage API, the response contains a status field that indicates whether the message was accepted. Status code '0' means success; any non-zero value indicates an error.
Important: There are two types of error codes in Vonage SMS:
- Submit Status Codes: Returned immediately when you submit the API request (indicates whether Vonage accepted your request)
- Delivery Receipt (DLR) Error Codes: Returned via webhook after delivery attempt (indicates final delivery status from the carrier)
Common Submit Status Codes:
| Status Code | Meaning | Troubleshooting |
|---|---|---|
| 0 | Success | Message accepted by Vonage for delivery |
| 1 | Throttled | You are sending messages too quickly. Implement rate limiting or request rate increase from Vonage |
| 2 | Missing params | Required parameter missing (e.g., to, from, text). Check request payload |
| 3 | Invalid params | Parameter value invalid (e.g., malformed phone number). Validate E.164 format |
| 4 | Invalid credentials | VONAGE_API_KEY or VONAGE_API_SECRET incorrect. Verify credentials in Vonage dashboard |
| 5 | Internal error | Vonage server error. Retry with exponential backoff |
| 6 | Invalid message | Message content rejected (e.g., exceeds length, invalid encoding) |
| 9 | Partner quota exceeded | Insufficient account balance or daily quota reached. Add credit or upgrade account |
| 10 | Non-whitelisted destination | Trial accounts only: Recipient not in approved test numbers. Add to whitelist in dashboard |
| 15 | Illegal sender address | VONAGE_FROM_NUMBER invalid or not owned by your account. Verify in dashboard under Numbers |
Delivery Receipt Status: All DLRs with a non-zero err-code indicate failed delivery (SMS did not reach recipient). Set up a webhook endpoint to receive DLR callbacks and track final delivery status.
References:
- Vonage SMS Delivery Error Codes: https://api.support.vonage.com/hc/en-us/articles/204014733
- SMS API Reference: https://developer.vonage.com/en/api/sms
3. Building the Express API Endpoint for SMS Sending
Create the Express server and the API endpoint that will use your sendSms function.
-
Set up Express Server: Open
index.jsand add the following code:javascript// index.js import express from 'express'; import 'dotenv/config'; import { sendSms } from './vonageService.js'; // Import our SMS function const app = express(); const port = process.env.PORT || 3000; // Use port from .env or default to 3000 // Middleware to parse JSON request bodies app.use(express.json()); // Middleware to parse URL-encoded request bodies app.use(express.urlencoded({ extended: true })); // Simple root route for health check / welcome message app.get('/', (req, res) => { res.send('Vonage SMS Sender API is running!'); }); // --- API Endpoint to Send SMS --- app.post('/api/send-sms', async (req, res) => { // 1. Basic Input Validation const { to, text } = req.body; // Destructure from request body if (!to || typeof to !== 'string') { return res.status(400).json({ success: false, error: 'Missing or invalid ""to"" field (string required).' }); } if (!text || typeof text !== 'string') { return res.status(400).json({ success: false, error: 'Missing or invalid ""text"" field (string required).' }); } // Basic E.164 format check (allows optional '+'). Vonage recommends E.164 (e.g., +15551234567). if (!/^\+?[1-9]\d{1,14}$/.test(to)) { console.warn(`Received potentially invalid ""to"" number format: ${to}. Attempting to send anyway.`); // For production, consider stricter validation that *rejects* non-compliant formats based on your requirements. // Example stricter rejection: // return res.status(400).json({ success: false, error: 'Invalid ""to"" number format. Use E.164 format (e.g., +1234567890).' }); } try { // 2. Call the Vonage service function console.log(`Received request to send SMS to: ${to}`); const vonageResponse = await sendSms(to, text); // 3. Send Success Response // Include Vonage message ID for potential tracking const messageId = vonageResponse.messages[0]['message-id']; res.status(200).json({ success: true, message: `SMS submitted successfully to ${to}`, messageId: messageId, // Optionally include the full vonage response for debugging // vonageDetails: vonageResponse }); } catch (error) { // 4. Send Error Response console.error(`Failed to send SMS to ${to}:`, error); // Determine appropriate status code based on error if possible // For now, use 500 for server/Vonage errors, 400 was handled above let statusCode = 500; let errorMessage = 'Failed to send SMS due to an internal error.'; // Check if it's a Vonage API error from our service function if (error.message.startsWith('Vonage API Error')) { errorMessage = error.message; // Potentially map Vonage error codes to HTTP status codes if needed // e.g., code '2' (Missing params) -> 400, '9' (Partner quota exceeded) -> 503 } else if (error instanceof Error) { // Handle generic errors differently if needed errorMessage = error.message || errorMessage; } res.status(statusCode).json({ success: false, error: errorMessage, // Optionally include error details during development // errorDetails: error.toString() }); } }); // Start the server app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); console.log(`API Endpoint: POST http://localhost:${port}/api/send-sms`); });Code Explanation:
- Imports
express, loads.env, and imports oursendSmsfunction. - Initializes the Express app and sets the port.
- Uses
express.json()andexpress.urlencoded()middleware to parse incoming request bodies. - Defines a
POSTroute at/api/send-sms. - Input Validation: It checks if
toandtextare present in the request body and are strings. It also includes a basic regex check for the 'to' number format, logging a warning but allowing the request to proceed for flexibility (with comments suggesting stricter validation for production). - Service Call: It calls the
sendSmsfunction within atry...catchblock. - Success Response: If
sendSmsresolves successfully, it sends a200 OKJSON response including the Vonage message ID. - Error Response: If
sendSmsthrows an error, it catches it, logs the error, and sends an appropriate error JSON response (usually500 Internal Server Errorfor API/server issues, but could be refined).
- Imports
4. Error Handling and Logging Best Practices for Vonage SMS
You've already incorporated basic error handling and logging:
vonageService.js:- Logs attempt details.
- Uses
try...catcharound thevonage.sms.send()call. - Logs success responses from Vonage.
- Checks the Vonage status code (
messages[0].status) and throws a specific error message if not '0'. - Logs caught errors before re-throwing.
index.js(API Endpoint):- Uses
try...catcharound thesendSms()call. - Handles validation errors explicitly with
400 Bad Request. - Logs received requests and failures.
- Returns standardized JSON responses for success (
{ success: true, ... }) and failure ({ success: false, error: '...' }).
- Uses
Further Improvements (Production Considerations):
- Structured Logging: Use a dedicated logging library like
PinoorWinstonfor structured JSON logs, log levels (info, warn, error), and easier log management/analysis. - Centralized Error Handling: Implement Express error-handling middleware for a cleaner way to manage errors across different routes.
- Detailed Vonage Error Mapping: Map specific Vonage error codes (returned in
messages[0].statusormessages[0]['error-text']) to more specific HTTP status codes and user-friendly error messages. Refer to the Vonage SMS API Error Codes documentation. - Retry Mechanisms: For transient network errors or specific Vonage errors (like rate limits), implement a retry strategy (e.g., exponential backoff) using libraries like
async-retry. Vonage itself has some retry logic for webhooks, but not necessarily for API calls from your end.
5. Security Features: Rate Limiting, 10DLC Compliance, and API Protection
Security is crucial for any API, especially one handling communication.
- Environment Variables: We are already using
.envto keep API keys out of the code and.gitignoreto prevent committing them. This is the most critical security step. - 10DLC Compliance (US SMS): If sending SMS to US numbers, you must comply with 10DLC (10-Digit Long Code) registration requirements. As of January 2025, Reseller ID requirements are mandatory. Unregistered traffic may be filtered or blocked by carriers. Register your brand and campaign through the Vonage dashboard under Messaging > 10DLC. Note: Brand vetting can take 1-3 business days; budget accordingly.
- Brand Registration: Submit business information and EIN/Tax ID
- Campaign Registration: Describe your use case (e.g., "2FA", "Marketing", "Customer Care")
- Required for: All A2P (Application-to-Person) messaging on US 10-digit long codes
- Reference: https://api.support.vonage.com/hc/en-us/articles/5702726960028-Key-Dates-Upcoming-changes
- Input Validation: Basic validation in
index.jsprevents malformed requests and potential issues downstream. The current phone number validation is lenient; consider enforcing strict E.164 format in production.- Improvement: Use a dedicated validation library like
Joiorexpress-validatorfor more complex and robust validation schemas. Sanitize inputs if they were being stored or reflected (though less critical here as they only go to Vonage).
- Improvement: Use a dedicated validation library like
- Rate Limiting: Protect your API endpoint (and your Vonage budget) from abuse or simple loops by adding rate limiting.
-
Install the library:
bashnpm install express-rate-limit -
Apply it in
index.js:javascript// index.js import express from 'express'; import 'dotenv/config'; import { sendSms } from './vonageService.js'; import rateLimit from 'express-rate-limit'; // Import rate-limiter const app = express(); // ... other setup ... app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Apply rate limiting to the SMS endpoint const smsLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: { success: false, error: 'Too many requests, please try again after 15 minutes.' }, standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers }); app.use('/api/send-sms', smsLimiter); // Apply middleware specifically to this route // ... your root route ... app.get('/', (req, res) => { /* ... */ }); // ... your route definition ... app.post('/api/send-sms', async (req, res) => { // ... existing route logic ... }); // ... start server ... const port = process.env.PORT || 3000; app.listen(port, () => { /* ... */ });
-
- HTTPS: In a production environment, always run your Node.js application behind a reverse proxy (like Nginx or Caddy) or use a platform (like Heroku, Vercel, AWS) that terminates SSL/TLS, ensuring all communication is encrypted over HTTPS. Do not handle TLS directly in Node.js unless necessary.
- Authentication/Authorization (Beyond Scope): This guide creates a public endpoint. For internal or protected use, you would need to implement authentication (e.g., API keys specific to your clients, JWT tokens) to ensure only authorized services can trigger SMS sends.
6. Common Vonage SMS Errors: Troubleshooting and Solutions
Non-Whitelisted DestinationError: This is the most common issue for trial accounts.- Cause: Sending an SMS to a phone number not added to your approved test numbers list in the Vonage dashboard.
- Solution: Log in to the Vonage dashboard, navigate to ""Sandbox & Test Numbers"" (or similar section), and add/verify the recipient's phone number.
- Invalid Credentials (Error Code 4):
- Cause:
VONAGE_API_KEYorVONAGE_API_SECRETin your.envfile are incorrect or missing. - Solution: Double-check the credentials in your
.envfile against the Vonage dashboard. Ensure the.envfile is in the project root and being loaded correctly (dotenv/config). Restart your server after changes.
- Cause:
- Invalid 'From' Number (Error Code 15: Illegal Sender Address):
- Cause: The
VONAGE_FROM_NUMBERis not a valid Vonage number associated with your account or an invalid/unsupported Alphanumeric Sender ID. - Solution: Verify the number/Sender ID in the Vonage dashboard (""Numbers"" > ""Your numbers""). Ensure it's entered correctly in
.env. Check Sender ID regulations for the destination country if using one.
- Cause: The
- Insufficient Funds (Error Code 9):
- Cause: Your Vonage account balance is too low to send the message.
- Solution: Add credit to your Vonage account.
- Invalid 'To' Number Format: While our basic validation allows some flexibility, Vonage strongly prefers the E.164 format (e.g.,
+14155552671). Incorrectly formatted numbers might fail, even if our current code attempts to send them. Stricter validation is recommended. - Carrier Filtering: Sometimes, messages might be blocked by the recipient's mobile carrier as spam, even if Vonage accepts the message. This is harder to debug and might require checking Vonage logs or contacting support if persistent. Using registered Sender IDs or Toll-Free Numbers can sometimes improve deliverability.
- SDK Version: Ensure you are using a compatible version of
@vonage/server-sdk. This guide assumes v3.x (currently v3.24.1 as of January 2025). Key differences in v3:- All function callbacks removed – must use async/await or
.then/.catch - Functions use parameter objects instead of positional arguments (e.g.,
vonage.sms.send({to, from, text})) - Uses TypeScript for better code completion and type safety
- Check the official Vonage Node SDK documentation for the latest methods and syntax: https://developer.vonage.com/en/changelogs/server_sdk/vonage-node-sdk
- All function callbacks removed – must use async/await or
- Multi-Channel Messaging: For applications requiring SMS fallback with WhatsApp, RCS, or MMS, consider migrating to the Vonage Messages API (
vonage.messages.send()), which supports channel failover and returns aworkflow_idfor tracking message sequences across multiple channels.
7. Production Deployment Strategies for Node.js SMS Applications
Deploy this application by running the Node.js process on a server and managing environment variables securely.
- Platforms:
- PaaS (Platform as a Service): Heroku, Vercel, Render, Google App Engine. These often simplify deployment. You typically push your code, and the platform builds and runs it. Environment variables are configured through their dashboards or CLI tools.
- IaaS (Infrastructure as a Service): AWS EC2, Google Compute Engine, DigitalOcean Droplets. Requires more setup (OS, Node.js installation, process manager like PM2, potentially a reverse proxy like Nginx). Environment variables can be set directly on the server or managed via deployment scripts.
- Serverless: AWS Lambda + API Gateway, Google Cloud Functions. Your code runs in response to triggers (like API Gateway requests). Requires packaging the code and dependencies. Environment variables are managed within the function configuration.
- Process Manager: Use a process manager like
PM2to keep your Node.js application running reliably in production (handles crashes, restarts, clustering).bashnpm install pm2 -g # Install globally pm2 start index.js --name vonage-sms-api # Start the app pm2 save # Save the process list to restart on server reboot pm2 logs # View logs - Environment Variables: Crucially, never hardcode API keys/secrets. Use the environment variable mechanisms provided by your chosen hosting platform. Do not upload your
.envfile to the server. - CI/CD (Continuous Integration / Continuous Deployment): Set up pipelines using tools like GitHub Actions, GitLab CI, Jenkins, or Bitbucket Pipelines to automate testing, building, and deploying your application whenever you push changes to your repository.
8. Testing Your Vonage SMS API: Verification and Debugging
Test the endpoint thoroughly to ensure it works as expected.
-
Start the Server:
bashnpm startYou should see
Server listening at http://localhost:3000and the API endpoint URL logged. -
Test with
curl: Open a new terminal window. Replace+15551234567with a whitelisted recipient number (if on a trial account) and adjust the message text.bashcurl -X POST http://localhost:3000/api/send-sms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+15551234567"", ""text"": ""Hello from the Vonage Express API!"" }'-
Expected Success Response (200 OK):
json{ ""success"": true, ""message"": ""SMS submitted successfully to +15551234567"", ""messageId"": ""some-vonage-message-id-12345"" }You should also receive the SMS on the target phone shortly after.
-
Expected Validation Error Response (400 Bad Request - Missing 'text'):
bashcurl -X POST http://localhost:3000/api/send-sms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+15551234567"" }'json{ ""success"": false, ""error"": ""Missing or invalid \""text\"" field (string required)."" } -
Expected Rate Limit Error Response (429 Too Many Requests - after many rapid requests):
json{ ""success"": false, ""error"": ""Too many requests, please try again after 15 minutes."" }
-
-
Test with Postman:
- Create a new request.
- Set the method to
POST. - Enter the URL:
http://localhost:3000/api/send-sms. - Go to the ""Body"" tab, select ""raw"", and choose ""JSON"" from the dropdown.
- Enter the JSON payload:
json
{ ""to"": ""+15551234567"", ""text"": ""Test message from Postman via Vonage API"" } - Click ""Send"".
- Observe the response body, status code, and check the target phone for the SMS. Test error cases by omitting fields or sending rapid requests.
-
Check Vonage Dashboard Logs:
- Log in to the Vonage Dashboard.
- Navigate to Logs > Messages API (or potentially SMS logs depending on account configuration).
- You should see records of your sent messages, their status (e.g.,
submitted,delivered,failed), the recipient, and any error codes if they failed. This is useful for confirming Vonage received the request and for debugging delivery issues.
Verification Checklist:
- Project initializes correctly (
npm install). - Server starts without errors (
npm start). -
.envfile is correctly configured with Vonage credentials and 'From' number. - Sending to a valid, whitelisted (if trial) number results in a
200 OKresponse and an SMS delivery. - Response JSON includes
success: trueand amessageId. - Sending without the
tofield results in a400 Bad Requestresponse with an appropriate error message. - Sending without the
textfield results in a400 Bad Requestresponse with an appropriate error message. - Sending with invalid Vonage credentials results in a
500 Internal Server Error(or similar) response detailing the Vonage error. - Rapidly sending requests eventually triggers a
429 Too Many Requestsresponse (if rate limiting is enabled). - Sent messages appear in the Vonage Dashboard logs.
Frequently Asked Questions (FAQ)
What's the difference between Vonage SMS API and Twilio SMS API?
Vonage uses API Key + API Secret authentication and the @vonage/server-sdk npm package with vonage.sms.send() method. Twilio uses Account SID + Auth Token authentication and the twilio npm package with client.messages.create() method. Both require E.164 phone number formatting. Vonage has a newer Messages API for multi-channel support (SMS, WhatsApp, RCS), while Twilio has Programmable Messaging with similar capabilities. Choose based on pricing, regional coverage, and existing infrastructure.
Why does my Vonage trial account show "Non-Whitelisted Destination" errors?
Trial accounts can only send SMS to verified/whitelisted numbers. Log into the Vonage dashboard, navigate to Sandbox & Test Numbers (or similar section), and add the recipient's phone number. You'll need to verify ownership via SMS or voice code. This restriction prevents abuse of free trial credits. Upgrade to a paid account to send to any valid phone number.
What does Vonage SMS API status code '0' mean?
Status code '0' indicates success – Vonage accepted your SMS for delivery. Any non-zero status code is an error. Common errors include: 4 (invalid credentials), 9 (insufficient balance), 10 (non-whitelisted destination on trial), 15 (invalid sender address). Check the error-text field in the response for details. Note: Status '0' means accepted, not delivered. Use Delivery Receipt (DLR) webhooks to track final delivery status.
How do I format phone numbers for Vonage SMS API?
Vonage requires E.164 format: +[country code][number] (e.g., +14155552671 for US, +442071838750 for UK). Include the + symbol and remove spaces, dashes, or parentheses. The regex /^\+?[1-9]\d{1,14}$/ validates E.164 format. Invalid formats may result in status code 3 (Invalid params). Always validate phone numbers before sending.
What is 10DLC and do I need it for US SMS?
10DLC (10-Digit Long Code) is a US regulatory requirement for Application-to-Person (A2P) SMS messaging. As of January 2025, Reseller ID requirements are mandatory. Register your Brand (business info + EIN) and Campaign (use case like "2FA" or "Marketing") through the Vonage dashboard under Messaging > 10DLC. Unregistered traffic may be filtered or blocked by US carriers. Brand vetting takes 1-3 business days. This applies to all US 10-digit long codes, not toll-free or short codes.
How do I receive inbound SMS messages with Vonage?
Set up a webhook endpoint in your Express app (e.g., app.post('/inbound-sms', ...)) that accepts POST requests from Vonage. Configure the webhook URL in the Vonage dashboard under Numbers > Your numbers > select number > Inbound Webhook URL. Vonage sends inbound message data (sender, text, timestamp) as URL-encoded POST. Parse with express.urlencoded() middleware. For local development, use ngrok to create a public URL that forwards to localhost.
What's the difference between Vonage SMS API and Messages API?
SMS API (vonage.sms.send()) is the legacy API for SMS-only messaging, simpler but limited to SMS/MMS. Messages API (vonage.messages.send()) supports multi-channel messaging (SMS, MMS, WhatsApp, Viber, Facebook Messenger, RCS) with automatic failover between channels. Messages API returns a workflow_id to track message sequences. Use Messages API for modern applications requiring channel flexibility and better deliverability through fallback chains.
How do I handle Vonage API rate limits?
Vonage returns status code 1 (Throttled) when you exceed rate limits. Implement rate limiting on your API endpoint using express-rate-limit (100 requests per 15 minutes recommended). For Vonage rate limit errors, implement exponential backoff retry using libraries like async-retry. Contact Vonage support to request higher rate limits for production use. Trial accounts have stricter limits than paid accounts.
Can I use Vonage Server SDK v3 with TypeScript?
Yes, v3.x is written in TypeScript and provides full type definitions out of the box. Import types from @vonage/server-sdk: import { Vonage, Auth } from '@vonage/server-sdk'. All methods support TypeScript auto-completion. Use async/await syntax (callbacks removed in v3). Parameter objects provide better type safety than positional arguments used in older versions.
How do I deploy a Vonage Express app to production?
Use a PaaS like Heroku, Render, or Vercel (configure environment variables via dashboard), or IaaS like AWS EC2 (use PM2 process manager, Nginx reverse proxy). Store VONAGE_API_KEY, VONAGE_API_SECRET, and VONAGE_FROM_NUMBER as environment variables (never hardcode). Enable HTTPS via reverse proxy or platform SSL termination. Set up webhook URLs with public HTTPS endpoints for delivery receipts and inbound messages. Use CI/CD pipelines (GitHub Actions, GitLab CI) to automate testing and deployment.
What Node.js version does Vonage Server SDK v3 require?
Node.js v14 or higher is required for modern JavaScript features (async/await, optional chaining, nullish coalescing). As of 2025, Node.js v18+ LTS is recommended for production deployments. The SDK uses ES modules (import/export), so add "type": "module" to package.json or use .mjs file extensions. Check compatibility at https://github.com/Vonage/vonage-node-sdk.
How do I test Vonage SMS locally during development?
- Get trial account with free credits from vonage.com
- Whitelist test numbers in Vonage dashboard (Sandbox & Test Numbers)
- Use ngrok for webhook testing:
ngrok http 3000creates public URL forlocalhost:3000 - Test with curl or Postman:
curl -X POST http://localhost:3000/api/send-sms -H "Content-Type: application/json" -d '{"to":"+15551234567","text":"Test"}' - Check Vonage logs in dashboard under Logs > Messages API to verify submissions and track delivery
This guide provides a solid foundation for sending SMS messages using Node.js, Express, and Vonage. You can build upon this by adding more features like delivery receipt webhooks, handling incoming messages, integrating with databases, or implementing more sophisticated error handling and monitoring.
Frequently Asked Questions
How to send SMS messages with Node.js and Express
Use the Vonage Server SDK for Node.js along with Express. Create an API endpoint that accepts recipient and message details, then leverages the SDK to send the SMS through the Vonage API. This allows you to programmatically integrate SMS capabilities into your applications.
What is the Vonage Server SDK for Node.js?
It's the official library for interacting with Vonage APIs, simplifying communication with their platform for sending SMS messages and other services. The SDK handles the complex details of API calls, letting you focus on application logic.
Why use dotenv with Node.js and Express?
Dotenv loads environment variables from a .env file into process.env. This is crucial for securely managing API credentials, such as your Vonage API key and secret, outside of your codebase, preventing them from being exposed publicly.
When should I whitelist test numbers in Vonage?
Whitelisting is mandatory for trial Vonage accounts. You can only send SMS to verified numbers added to your test list. This prevents abuse of free trial credits and ensures compliance with Vonage's terms of service.
Can I use an Alphanumeric Sender ID with Vonage?
Yes, for some destinations, you can use a Sender ID (like "MyApp") instead of a phone number. However, Sender ID support varies by country and carrier, so check Vonage's documentation for compatibility.
How to set up a Node.js project for sending SMS
Initialize a Node.js project with npm init, install Express, the Vonage Server SDK, and dotenv. Create a .env file to store your Vonage API credentials (key, secret, and 'from' number) and a .gitignore to exclude this file from version control.
What is the purpose of the .env file?
The .env file stores sensitive data, especially your Vonage API key and secret. It keeps configuration and credentials outside of your main code, enhancing security and portability across different environments (e.g., development, production).
How to integrate Vonage API into a Node.js app?
First, obtain your API key and secret from the Vonage dashboard. Then, install the Vonage Server SDK for Node.js and initialize a Vonage client object using your credentials within your application code. This client enables you to send SMS through their API.
What is the correct format for phone numbers with Vonage?
Vonage strongly recommends the E.164 format, which includes a plus sign and the country code (e.g., +15551234567). While the provided code example performs a lenient check, stricter enforcement of E.164 is advisable for production environments to avoid potential issues.
How to handle Vonage API errors in Node.js?
Use try-catch blocks around calls to the sendSms function and examine the error details from the API response. The example code demonstrates how to check message status and extract error text, which you should use to provide more specific error responses to clients.
Why does the "Non-Whitelisted Destination" error occur with Vonage?
Trial Vonage accounts can only send messages to pre-verified numbers in your test list. Add the recipient number to your whitelisted numbers in the Vonage dashboard's "Sandbox & Test Numbers" section.
How to implement rate limiting for the SMS endpoint
Use the express-rate-limit library. Configure it to limit the number of requests from a specific IP address within a time window (e.g., 100 requests every 15 minutes). This protects your application from abuse and helps manage Vonage API costs.
What are best practices for securing a Vonage SMS API integration
Crucially, store API keys and secrets in environment variables (via .env) and never commit them to version control. Implement input validation, rate limiting using express-rate-limit, and always use HTTPS in production.
How to test the Vonage SMS API endpoint
Use tools like curl or Postman to send POST requests to your /api/send-sms endpoint. Include the 'to' and 'text' parameters in the JSON request body. Verify success by receiving the SMS and checking the response for 'success: true' and a message ID. Also, test error cases like invalid inputs.