How to Send SMS Messages with Vonage API in Node.js: Complete Express Integration Guide
Build a production-ready SMS API with Vonage and Node.js Express. Complete tutorial with authentication, rate limiting, error handling, and deployment. Send SMS messages programmatically in minutes.
Meta Description: Build a production-ready SMS API with Vonage and Node.js Express. Complete tutorial with authentication, rate limiting, error handling, and deployment. Send SMS messages programmatically in minutes.
Build a production-ready Node.js application using Express to send SMS messages via the Vonage Messages API (formerly Nexmo). This comprehensive tutorial covers project setup, REST API development, security best practices, error handling, testing, and deployment to production environments.
You'll create a functional REST API endpoint that accepts phone numbers and message content, securely sends SMS messages using the Vonage Node.js SDK, and handles errors gracefully. This forms a solid foundation for integrating SMS capabilities into web applications, mobile backends, notification systems, two-factor authentication (2FA), one-time password (OTP) delivery, appointment reminders, and customer communication workflows.
Last Updated: January 15, 2025 Compatible With: Vonage Messages API v3, Node.js 18+, Express 4.x
What You'll Build: Production SMS API
Your Application:
A REST API built with Node.js and Express that exposes a /send-sms endpoint. This endpoint accepts a recipient phone number in E.164 format and message body, then uses the Vonage Messages API to deliver SMS messages to recipients worldwide.
Problem Solved:
Create a reusable, server-side component for programmatic SMS delivery. Abstract direct interaction with the Vonage REST API into a simple, authenticated API call within your own infrastructure. Enable SMS functionality without exposing Vonage credentials to client applications.
Technology Stack:
Node.js – JavaScript runtime for building server-side applications (v18+ recommended for Long-Term Support with native fetch API and improved performance).
Express.js – Minimal and flexible Node.js web framework for creating RESTful API servers and routes.
Vonage Node.js Server SDK – Official library (@vonage/server-sdk) for interacting with Vonage Communications APIs, specifically the Messages API for SMS delivery.
dotenv – Module to load environment variables from a .env file into process.env, keeping API keys and secrets secure.
Joi – Schema description language and data validator for JavaScript, ensuring request data integrity.
Helmet – Security middleware for Express that sets various HTTP headers.
Express Rate Limit – Rate limiting middleware to prevent API abuse.
Morgan – HTTP request logger middleware for Node.js.
System Architecture:
A client application (web frontend, mobile app, or microservice) sends an HTTP POST request with JSON payload to the /send-sms endpoint of your Node.js/Express API server. The API authenticates the request, validates input data, and uses the Vonage SDK (configured with API credentials from environment variables) to communicate with the Vonage Messages API over HTTPS. Vonage's carrier network then delivers the SMS message to the recipient's mobile device.
Prerequisites:
Node.js and npm (or yarn) – Install from nodejs.org. Version 18 LTS or later recommended for optimal stability and security patches.
Vonage API Account – Sign up for free at Vonage API Dashboard. New accounts receive complimentary credit for testing SMS delivery.
Vonage Virtual Number – Rent a Vonage phone number capable of sending SMS messages through the Vonage Dashboard. Number availability and pricing vary by country.
Basic JavaScript and REST API knowledge – Familiarity with asynchronous programming, HTTP methods, and JSON.
Text editor or IDE (e.g., Visual Studio Code, WebStorm, or Sublime Text).
(Optional) API Testing Tool – Postman, Insomnia, or curl for endpoint testing.
Final Outcome:
A running Node.js Express application with a secure, authenticated endpoint (/send-sms) that successfully sends SMS messages via Vonage when provided with a valid recipient phone number, message content, and correct API credentials. The application includes robust input validation, comprehensive error handling, request logging, rate limiting, and deployment-ready configuration.
1. Node.js Project Setup and Dependency Installation
Initialize your Node.js project and install the necessary packages for SMS API development.
1.1 Create Project Directory:
Open your terminal and create a new directory for the Vonage SMS project:
Bash
mkdir vonage-sms-sender
cd vonage-sms-sender
1.2 Initialize Node.js Project:
Create a package.json file to manage dependencies, scripts, and project metadata:
Bash
npm init -y
If using yarn: yarn init -y
This generates a basic package.json with default values. You can customize the name, version, description, and other fields later.
1.3 Install Required Dependencies:
Install Express web framework, the Vonage Server SDK, dotenv for environment variable management, and joi for request validation:
Bash
npm install express @vonage/server-sdk dotenv joi
If using yarn: yarn add express @vonage/server-sdk dotenv joi
Package Explanations:
express (v4.18+) – Web framework for building HTTP servers and REST APIs
@vonage/server-sdk (v3.12+) – Official Vonage library for SMS, voice, and messaging APIs
dotenv (v16.3+) – Loads environment variables from .env files
joi (v17.11+) – Schema-based validation library
1.4 Create Project Structure:
Create the basic files and folders for your SMS API application:
VONAGE_API_KEY – Your Vonage API key from the Vonage Dashboard. Found under "API Settings" or "Your API Keys".
VONAGE_API_SECRET – Your Vonage API secret from the dashboard. Keep this confidential.
VONAGE_FROM_NUMBER – Your Vonage virtual number in E.164 format without spaces or dashes (e.g., 14155551234 for a US number). This appears as the sender ID.
PORT – The port your Express server will listen on (default: 3000). Change for production or to avoid port conflicts.
NODE_ENV – Environment identifier (development, production, test). Used for conditional logic.
API_KEY – Custom API key for authenticating requests to your SMS endpoint. Generate a strong random string.
Security Best Practices:
Never hardcode credentials in source code
Use different .env files for development, staging, and production
Create a dedicated module to handle all Vonage SMS API interactions. This separation of concerns makes your code more maintainable, testable, and reusable.
2.1 Implement smsService.js:
Open smsService.js and add this code to initialize the Vonage client and create the SMS sending function:
JavaScript
// smsService.jsconst { Vonage } = require('@vonage/server-sdk');
require('dotenv').config();
// Initialize Vonage client with credentials from environment variablesconst vonage = newVonage({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
});
/**
* Send an SMS message via Vonage
* @param {string} to - Recipient phone number in E.164 format (e.g., "14155551234")
* @param {string} text - Message content
* @returns {Promise<object>} - Vonage API response
*/asyncfunctionsendSms(to, text) {
constfrom = process.env.VONAGE_FROM_NUMBER;
if (!from) {
thrownewError('VONAGE_FROM_NUMBER environment variable not set');
}
try {
// Send SMS using Vonage SDKconst response = await vonage.sms.send({
to,
from,
text,
});
// Check if message was sent successfullyif (response.messages[0].status === '0') {
console.log(`SMS sent successfully to ${to}. Message ID: ${response.messages[0]['message-id']}`);
return {
success: true,
messageId: response.messages[0]['message-id'],
to,
};
} else {
// Vonage returned an errorconst error = response.messages[0];
console.error(`SMS failed to send to ${to}. Error: ${error['error-text']}`);
thrownewError(`Vonage API Error: ${error['error-text']}`);
}
} catch (error) {
console.error('Error sending SMS:', error.message);
throw error;
}
}
module.exports = { sendSms };
How It Works:
Import Dependencies – Load the Vonage SDK and dotenv for environment variables.
Initialize Vonage Client – Create a Vonage instance with your API key and secret from environment variables. Authentication uses the API Key/Secret pair.
sendSms Function – Exports an async function that accepts recipient number and message text.
Configuration Validation – Checks if VONAGE_FROM_NUMBER is set before attempting to send. Throws descriptive error if missing.
Send SMS via Vonage API – Calls vonage.sms.send() with the to, from, and text parameters.
Response Handling – Vonage returns an array of message objects. Status code '0' indicates success. Other codes indicate various error conditions.
Success Response – Logs success message and returns structured response with messageId for tracking.
Error Handling – Catches API errors, network errors, and Vonage-specific error codes. Throws descriptive exceptions.
Key Points:
E.164 Phone Number Format – All phone numbers must use international format without spaces: country code + number (e.g., 14155551234 for US, 447700900000 for UK).
Vonage Status Codes – Status '0' means success. Codes 1-15 represent different error conditions (throttling, invalid params, invalid credentials, etc.).
Async/Await Pattern – Modern JavaScript syntax for handling asynchronous Vonage API calls.
Error Propagation – Throwing errors allows the calling code (Express route) to handle them appropriately.
3. Create the Express REST API Server
Build your Express server with a /send-sms endpoint that uses the SMS service module.
3.1 Implement index.js with API Endpoint:
Open index.js and add this code to create your REST API server with validation and error handling:
JavaScript
// index.jsconst express = require('express');
constJoi = require('joi');
const { sendSms } = require('./smsService');
require('dotenv').config();
const app = express();
constPORT = process.env.PORT || 3000;
// Middleware to parse JSON request bodies
app.use(express.json());
// Validation schema for SMS requestsconst smsSchema = Joi.object({
to: Joi.string()
.pattern(/^\d{10,15}$/)
.required()
.messages({
'string.pattern.base': 'Phone number must contain 10-15 digits in E.164 format',
'any.required': 'Recipient phone number is required',
}),
text: Joi.string()
.min(1)
.max(160)
.required()
.messages({
'string.min': 'Message cannot be empty',
'string.max': 'Message cannot exceed 160 characters',
'any.required': 'Message text is required',
}),
});
// POST /send-sms endpoint
app.post('/send-sms', async (req, res) => {
try {
// Validate request bodyconst { error, value } = smsSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
error: 'Validation failed',
details: error.details.map(detail => detail.message),
});
}
const { to, text } = value;
// Send SMS via Vonageconst result = awaitsendSms(to, text);
// Return success responsereturn res.status(200).json({
success: true,
message: 'SMS sent successfully',
messageId: result.messageId,
to: result.to,
});
} catch (error) {
console.error('Error in /send-sms endpoint:', error.message);
// Return error responsereturn res.status(500).json({
success: false,
error: 'Failed to send SMS',
details: error.message,
});
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({ status: 'OK', timestamp: newDate().toISOString() });
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`Send SMS: POST http://localhost:${PORT}/send-sms`);
});
How It Works:
Express Setup – Initialize Express app and configure JSON body parsing middleware.
Environment Variables – Load configuration from .env file using dotenv.
Status 1 – Throttled – You've exceeded your rate limit
Status 2 – Missing parameters – Required fields not provided
Status 3 – Invalid parameters – Malformed or invalid data
Status 4 – Invalid credentials – API key or secret incorrect
Status 5 – Internal error – Vonage service issue
Status 6 – Invalid message – Message content violates policies
Status 7 – Number barred – Recipient number blocked or invalid
Status 9 – Partner quota exceeded – Account credit exhausted
Status 15 – Invalid sender address – FROM number not authorized
This error mapping helps you quickly diagnose issues and provide meaningful feedback to API consumers.
7. Deploy Your SMS API to Production
Prepare your application for production deployment with deployment scripts and hosting platform configuration.
7.1 Add NPM Scripts:
Update package.json to include useful scripts for development and production:
JSON
{"name":"vonage-sms-sender","version":"1.0.0","description":"Production-ready SMS API using Vonage and Express","main":"index.js","scripts":{"start":"node index.js","dev":"nodemon index.js","test":"echo \"Error: no test specified\" && exit 1"},"keywords":["vonage","sms","api","express","nodejs"],"author":"Your Name","license":"MIT","dependencies":{"express":"^4.18.2","@vonage/server-sdk":"^3.12.0","dotenv":"^16.3.1","joi":"^17.11.0","helmet":"^7.1.0","cors":"^2.8.5","express-rate-limit":"^7.1.5","morgan":"^1.10.0"},"devDependencies":{"nodemon":"^3.0.2"},"engines":{"node":">=18.0.0","npm":">=9.0.0"}}
Install nodemon for automatic server restarts during development:
For VPS deployment (DigitalOcean, AWS EC2, Linode), use PM2 process manager:
Bash
# Install PM2 globally
npm install -g pm2
# Start application with PM2
pm2 start index.js --name vonage-sms-api
# Configure PM2 to start on system boot
pm2 startup
pm2 save
# Monitor application
pm2 status
pm2 logs vonage-sms-api
# Restart application
pm2 restart vonage-sms-api
PM2 provides automatic restarts on crashes, load balancing, and zero-downtime reloads.
Frequently Asked Questions (FAQ)
How much does it cost to send SMS messages with Vonage?
Vonage SMS pricing varies by destination country. US SMS typically costs $0.0075 per message (less than 1 cent). International rates range from $0.01 to $0.50+ per message depending on the country. Check current pricing at Vonage SMS Pricing. New accounts include free credit ($2) for testing. Volume discounts available for high-volume senders.
What phone number format should I use for international SMS?
Always use E.164 format – the international standard for phone numbers. Format: [country code][area code][local number] without spaces, dashes, or special characters. Examples: 14155551234 (US), 447700900000 (UK), 61412345678 (Australia), 919876543210 (India). Remove leading zeros and include the country code prefix.
Can I send SMS messages to international phone numbers?
Yes. Vonage supports SMS delivery to 200+ countries worldwide. Ensure your Vonage account has international SMS enabled (check Dashboard → Settings → SMS). Verify sufficient account credit for international delivery. Pricing varies significantly by country – some regions cost 10-50x more than US domestic SMS. Test with small volumes first.
How do I handle SMS delivery receipts and status updates?
Vonage can send delivery receipt callbacks (webhooks) to your server when SMS messages are delivered or fail. Configure webhook URL in Vonage Dashboard under Settings → SMS Settings → Delivery Receipts. Implement a POST endpoint (e.g., /webhooks/delivery-receipt) to receive status updates. Delivery receipts include message ID, status, timestamp, and error codes. Store these in your database for tracking and analytics.
What's the character limit for SMS messages?
Standard SMS supports 160 characters using GSM-7 encoding (basic Latin alphabet, numbers, common symbols). Messages using special characters, emojis, or non-Latin scripts use Unicode (UCS-2) encoding, which reduces the limit to 70 characters per segment. Vonage automatically concatenates longer messages into multiple segments (up to 6 segments = 918 GSM-7 characters or 402 Unicode characters). Each segment is billed separately.
How do I prevent abuse and spam using my SMS API?
Implement multiple security layers: (1) Require API key authentication (shown in Section 5), (2) Enable rate limiting to restrict requests per IP/user (10-100 requests per minute), (3) Validate phone numbers against allowlists or known patterns, (4) Monitor usage with logging and alerting, (5) Set spending limits in Vonage Dashboard to prevent unexpected charges, (6) Implement CAPTCHA for user-facing forms, (7) Add phone number verification (send confirmation code before allowing sends), (8) Block disposable/virtual numbers if needed.
What are the differences between Vonage Messages API and SMS API?
Vonage offers two SMS APIs: (1) SMS API (used in this tutorial) – Legacy, simpler API with straightforward REST endpoints, supports basic SMS sending and delivery receipts. (2) Messages API – Newer unified API supporting SMS, MMS, WhatsApp, Viber, Facebook Messenger, and more channels. Offers advanced features like message concatenation, Unicode support, and channel failover. For basic SMS needs, SMS API is sufficient and simpler. For multi-channel messaging, use Messages API.
How do I test SMS sending without spending money?
Vonage provides several testing options: (1) Use free credit from new account signup ($2-10 depending on promotion), (2) Test with Vonage virtual numbers in sandbox mode (check documentation), (3) Send messages to your own verified phone numbers only during development, (4) Use Vonage's test credentials if available for your account type, (5) Mock the Vonage SDK in unit tests to avoid actual API calls. Always set strict rate limits and spending alerts during development.
Frequently asked questions
Use the Vonage Messages API with the Node.js Server SDK and Express. Create an API endpoint that accepts the recipient's number and message, then uses the SDK to send the SMS through Vonage.
The Vonage Messages API lets you send SMS messages programmatically from your Node.js application. It handles the complexities of SMS delivery so you can focus on your application logic.
Dotenv helps manage sensitive credentials like API keys by loading them from a .env file into process.env. This keeps them out of your codebase, improving security.
For high-volume or high-reliability SMS sending, use a message queue like BullMQ or Agenda. This offloads sending to a background process to handle retries and maintain API responsiveness.
While this basic guide doesn't cover it, you can track delivery status by setting up webhooks in your Vonage application settings and extending the application to store status updates.
Initialize a Node.js project, install Express, the Vonage Server SDK, dotenv, and Joi. Create index.js, smsService.js, .env, and .gitignore files, and configure your project structure.
Joi provides robust data validation to ensure inputs like phone numbers and message content are in the correct format, preventing errors and security issues.
This separation follows best practices for organization and maintainability, keeping Vonage-specific code separate from the main application logic in index.js.
A database isn't essential for basic sending. However, you'll need it if you want to store message history, delivery statuses, user data, or manage contacts.
Implement input validation (Joi), rate limiting (express-rate-limit), authentication (API keys or JWT), secure credential storage (.env and secret managers), HTTPS, and regular dependency updates.
E.164 is an international standard phone number format (e.g., +14155550100). The Vonage API expects numbers in this format for reliable delivery.
Use the libphonenumber-js library to thoroughly parse, validate, and format phone numbers from various inputs, ensuring global compatibility.
The /health endpoint provides a simple way to check if the server is running and responding, useful for monitoring and uptime checks.
The smsService.js module includes a try...catch block to handle errors during SMS sending. Implement detailed logging and consider retry mechanisms or a message queue for robust error handling.
Meta Description: Build a production-ready SMS API with Vonage and Node.js Express. Complete tutorial with authentication, rate limiting, error handling, and deployment. Send SMS messages programmatically in minutes.
Build a production-ready Node.js application using Express to send SMS messages via the Vonage Messages API (formerly Nexmo). This comprehensive tutorial covers project setup, REST API development, security best practices, error handling, testing, and deployment to production environments.
You'll create a functional REST API endpoint that accepts phone numbers and message content, securely sends SMS messages using the Vonage Node.js SDK, and handles errors gracefully. This forms a solid foundation for integrating SMS capabilities into web applications, mobile backends, notification systems, two-factor authentication (2FA), one-time password (OTP) delivery, appointment reminders, and customer communication workflows.
Last Updated: January 15, 2025
Compatible With: Vonage Messages API v3, Node.js 18+, Express 4.x
What You'll Build: Production SMS API
Your Application:
A REST API built with Node.js and Express that exposes a
/send-smsendpoint. This endpoint accepts a recipient phone number in E.164 format and message body, then uses the Vonage Messages API to deliver SMS messages to recipients worldwide.Problem Solved:
Create a reusable, server-side component for programmatic SMS delivery. Abstract direct interaction with the Vonage REST API into a simple, authenticated API call within your own infrastructure. Enable SMS functionality without exposing Vonage credentials to client applications.
Technology Stack:
@vonage/server-sdk) for interacting with Vonage Communications APIs, specifically the Messages API for SMS delivery..envfile intoprocess.env, keeping API keys and secrets secure.System Architecture:
A client application (web frontend, mobile app, or microservice) sends an HTTP POST request with JSON payload to the
/send-smsendpoint of your Node.js/Express API server. The API authenticates the request, validates input data, and uses the Vonage SDK (configured with API credentials from environment variables) to communicate with the Vonage Messages API over HTTPS. Vonage's carrier network then delivers the SMS message to the recipient's mobile device.Prerequisites:
Final Outcome:
A running Node.js Express application with a secure, authenticated endpoint (
/send-sms) that successfully sends SMS messages via Vonage when provided with a valid recipient phone number, message content, and correct API credentials. The application includes robust input validation, comprehensive error handling, request logging, rate limiting, and deployment-ready configuration.1. Node.js Project Setup and Dependency Installation
Initialize your Node.js project and install the necessary packages for SMS API development.
1.1 Create Project Directory:
Open your terminal and create a new directory for the Vonage SMS project:
mkdir vonage-sms-sender cd vonage-sms-sender1.2 Initialize Node.js Project:
Create a
package.jsonfile to manage dependencies, scripts, and project metadata:If using yarn:
yarn init -yThis generates a basic
package.jsonwith default values. You can customize the name, version, description, and other fields later.1.3 Install Required Dependencies:
Install Express web framework, the Vonage Server SDK,
dotenvfor environment variable management, andjoifor request validation:If using yarn:
yarn add express @vonage/server-sdk dotenv joiPackage Explanations:
express(v4.18+) – Web framework for building HTTP servers and REST APIs@vonage/server-sdk(v3.12+) – Official Vonage library for SMS, voice, and messaging APIsdotenv(v16.3+) – Loads environment variables from.envfilesjoi(v17.11+) – Schema-based validation library1.4 Create Project Structure:
Create the basic files and folders for your SMS API application:
# Linux/macOS touch index.js .env .gitignore smsService.js # Windows (Command Prompt) type nul > index.js type nul > .env type nul > .gitignore type nul > smsService.js # Windows (PowerShell) New-Item index.js -ItemType File New-Item .env -ItemType File New-Item .gitignore -ItemType File New-Item smsService.js -ItemType FileFile Descriptions:
index.js– Main entry point for your Express application, defines routes and middlewaresmsService.js– Module dedicated to handling Vonage SMS API interactions.env– Stores sensitive configuration like API keys and secrets. Never commit to version control..gitignore– Specifies files that Git should ignore (credentials, dependencies, logs)1.5 Configure
.gitignore:Open
.gitignoreand add these lines to prevent committing sensitive files and unnecessary directories:This prevents accidentally exposing your Vonage API credentials or uploading large dependency folders to Git repositories.
1.6 Configure Environment Variables:
Open the
.envfile and add your Vonage API credentials and configuration:Environment Variable Descriptions:
VONAGE_API_KEY– Your Vonage API key from the Vonage Dashboard. Found under "API Settings" or "Your API Keys".VONAGE_API_SECRET– Your Vonage API secret from the dashboard. Keep this confidential.VONAGE_FROM_NUMBER– Your Vonage virtual number in E.164 format without spaces or dashes (e.g.,14155551234for a US number). This appears as the sender ID.PORT– The port your Express server will listen on (default:3000). Change for production or to avoid port conflicts.NODE_ENV– Environment identifier (development,production,test). Used for conditional logic.API_KEY– Custom API key for authenticating requests to your SMS endpoint. Generate a strong random string.Security Best Practices:
.envfiles for development, staging, and productionYour project structure now looks like this:
2. Build the SMS Service Module with Vonage SDK
Create a dedicated module to handle all Vonage SMS API interactions. This separation of concerns makes your code more maintainable, testable, and reusable.
2.1 Implement
smsService.js:Open
smsService.jsand add this code to initialize the Vonage client and create the SMS sending function:// smsService.js const { Vonage } = require('@vonage/server-sdk'); require('dotenv').config(); // Initialize Vonage client with credentials from environment variables const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, }); /** * Send an SMS message via Vonage * @param {string} to - Recipient phone number in E.164 format (e.g., "14155551234") * @param {string} text - Message content * @returns {Promise<object>} - Vonage API response */ async function sendSms(to, text) { const from = process.env.VONAGE_FROM_NUMBER; if (!from) { throw new Error('VONAGE_FROM_NUMBER environment variable not set'); } try { // Send SMS using Vonage SDK const response = await vonage.sms.send({ to, from, text, }); // Check if message was sent successfully if (response.messages[0].status === '0') { console.log(`SMS sent successfully to ${to}. Message ID: ${response.messages[0]['message-id']}`); return { success: true, messageId: response.messages[0]['message-id'], to, }; } else { // Vonage returned an error const error = response.messages[0]; console.error(`SMS failed to send to ${to}. Error: ${error['error-text']}`); throw new Error(`Vonage API Error: ${error['error-text']}`); } } catch (error) { console.error('Error sending SMS:', error.message); throw error; } } module.exports = { sendSms };How It Works:
sendSmsFunction – Exports an async function that accepts recipient number and message text.VONAGE_FROM_NUMBERis set before attempting to send. Throws descriptive error if missing.vonage.sms.send()with theto,from, andtextparameters.'0'indicates success. Other codes indicate various error conditions.messageIdfor tracking.Key Points:
14155551234for US,447700900000for UK).'0'means success. Codes1-15represent different error conditions (throttling, invalid params, invalid credentials, etc.).3. Create the Express REST API Server
Build your Express server with a
/send-smsendpoint that uses the SMS service module.3.1 Implement
index.jswith API Endpoint:Open
index.jsand add this code to create your REST API server with validation and error handling:// index.js const express = require('express'); const Joi = require('joi'); const { sendSms } = require('./smsService'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3000; // Middleware to parse JSON request bodies app.use(express.json()); // Validation schema for SMS requests const smsSchema = Joi.object({ to: Joi.string() .pattern(/^\d{10,15}$/) .required() .messages({ 'string.pattern.base': 'Phone number must contain 10-15 digits in E.164 format', 'any.required': 'Recipient phone number is required', }), text: Joi.string() .min(1) .max(160) .required() .messages({ 'string.min': 'Message cannot be empty', 'string.max': 'Message cannot exceed 160 characters', 'any.required': 'Message text is required', }), }); // POST /send-sms endpoint app.post('/send-sms', async (req, res) => { try { // Validate request body const { error, value } = smsSchema.validate(req.body); if (error) { return res.status(400).json({ success: false, error: 'Validation failed', details: error.details.map(detail => detail.message), }); } const { to, text } = value; // Send SMS via Vonage const result = await sendSms(to, text); // Return success response return res.status(200).json({ success: true, message: 'SMS sent successfully', messageId: result.messageId, to: result.to, }); } catch (error) { console.error('Error in /send-sms endpoint:', error.message); // Return error response return res.status(500).json({ success: false, error: 'Failed to send SMS', details: error.message, }); } }); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() }); }); // Start server app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); console.log(`Send SMS: POST http://localhost:${PORT}/send-sms`); });How It Works:
.envfile using dotenv.to– Phone number with 10-15 digits, E.164 format validationtext– Message content, 1-160 characters (standard SMS length)POST /send-smsEndpoint:sendSms()from your SMS service moduleGET /healthreturns server status, useful for monitoring and load balancers.API Request/Response Examples:
Successful Request:
POST /send-sms Content-Type: application/json { "to": "14155551234", "text": "Your verification code is 123456" }Success Response (HTTP 200):
{ "success": true, "message": "SMS sent successfully", "messageId": "0B00000123456789", "to": "14155551234" }Validation Error Response (HTTP 400):
{ "success": false, "error": "Validation failed", "details": [ "Phone number must contain 10-15 digits in E.164 format" ] }Key Features:
4. Test Your SMS API Endpoint
Verify your SMS endpoint works correctly using curl, Postman, or other API testing tools.
4.1 Start the Development Server:
Run your Node.js application:
You should see console output:
4.2 Test SMS Sending with curl:
Send a test SMS using curl in a new terminal window:
curl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "to": "14155551234", "text": "Hello from Vonage SMS API!" }'Expected Success Response (HTTP 200):
{ "success": true, "message": "SMS sent successfully", "messageId": "0B00000123456789", "to": "14155551234" }Check the recipient's phone – they should receive the SMS message (ensure you have Vonage credit and a valid number).
4.3 Test Input Validation:
Send an invalid request missing the phone number:
curl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "text": "Test message without phone number" }'Expected Validation Error (HTTP 400):
{ "success": false, "error": "Validation failed", "details": [ "Recipient phone number is required" ] }Test with an invalid phone number format:
curl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "to": "555-1234", "text": "Invalid format test" }'Expected Response (HTTP 400):
{ "success": false, "error": "Validation failed", "details": [ "Phone number must contain 10-15 digits in E.164 format" ] }4.4 Test with Postman:
http://localhost:3000/send-sms.Content-Type: application/json.{ "to": "14155551234", "text": "Testing Vonage SMS from Postman" }You should receive a success response, and the recipient phone should receive the SMS message.
4.5 Test Health Check Endpoint:
Expected Response (HTTP 200):
{ "status": "OK", "timestamp": "2025-01-15T10:30:00.000Z" }Use this endpoint for application monitoring, load balancer health checks, and uptime verification.
5. Add Production Security Features
Enhance your SMS API for production deployment with authentication, rate limiting, security headers, and request logging.
5.1 Install Security Dependencies:
Install Helmet for security headers, CORS for cross-origin requests, and Express Rate Limit for rate limiting:
If using yarn:
yarn add helmet cors express-rate-limit morgan5.2 Implement Security Middleware:
Update
index.jsto include comprehensive security middleware:// index.js (updated with security features) const express = require('express'); const helmet = require('helmet'); const cors = require('cors'); const rateLimit = require('express-rate-limit'); const morgan = require('morgan'); const Joi = require('joi'); const { sendSms } = require('./smsService'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3000; const API_KEY = process.env.API_KEY || 'your-secret-api-key'; // Validate required environment variables at startup const requiredEnvVars = ['VONAGE_API_KEY', 'VONAGE_API_SECRET', 'VONAGE_FROM_NUMBER', 'API_KEY']; requiredEnvVars.forEach(varName => { if (!process.env[varName]) { console.error(`Error: Missing required environment variable ${varName}`); process.exit(1); } }); // Security middleware app.use(helmet()); // Sets security-related HTTP headers app.use(cors()); // Enable Cross-Origin Resource Sharing app.use(express.json()); // Parse JSON request bodies app.use(morgan('combined')); // Log all HTTP requests // Rate limiting middleware – prevents API abuse const limiter = rateLimit({ windowMs: 60 * 1000, // 1 minute window max: 10, // Max 10 requests per minute per IP message: { success: false, error: 'Too many requests. Please try again later.', }, standardHeaders: true, // Return rate limit info in headers legacyHeaders: false, }); // Apply rate limiter to SMS endpoint app.use('/send-sms', limiter); // API Key authentication middleware function authenticateApiKey(req, res, next) { const apiKey = req.headers['x-api-key']; if (!apiKey || apiKey !== API_KEY) { return res.status(401).json({ success: false, error: 'Unauthorized. Invalid or missing API key.', hint: 'Include X-API-Key header with your request', }); } next(); } // Validation schema const smsSchema = Joi.object({ to: Joi.string() .pattern(/^\d{10,15}$/) .required() .messages({ 'string.pattern.base': 'Phone number must contain 10-15 digits in E.164 format', 'any.required': 'Recipient phone number is required', }), text: Joi.string() .min(1) .max(160) .required() .messages({ 'string.min': 'Message cannot be empty', 'string.max': 'Message cannot exceed 160 characters', 'any.required': 'Message text is required', }), }); // POST /send-sms endpoint with authentication app.post('/send-sms', authenticateApiKey, async (req, res) => { try { // Validate request body const { error, value } = smsSchema.validate(req.body); if (error) { return res.status(400).json({ success: false, error: 'Validation failed', details: error.details.map(detail => detail.message), }); } const { to, text } = value; // Send SMS via Vonage const result = await sendSms(to, text); // Return success response return res.status(200).json({ success: true, message: 'SMS sent successfully', messageId: result.messageId, to: result.to, }); } catch (error) { console.error('Error in /send-sms endpoint:', error.message); // Return error response return res.status(500).json({ success: false, error: 'Failed to send SMS', details: error.message, }); } }); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), }); }); // Global error handler app.use((err, req, res, next) => { console.error('Unhandled error:', err); res.status(err.status || 500).json({ success: false, error: err.message || 'Internal server error', }); }); // 404 handler for unknown routes app.use((req, res) => { res.status(404).json({ success: false, error: 'Route not found', availableEndpoints: [ 'POST /send-sms', 'GET /health', ], }); }); // Start server app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); console.log(`Send SMS: POST http://localhost:${PORT}/send-sms`); console.log(`Environment: ${process.env.NODE_ENV}`); });Security Features Explained:
X-API-Keyheader matching your secret key. Prevents unauthorized access.Test with Authentication:
curl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -H "X-API-Key: your-secure-api-key-here" \ -d '{ "to": "14155551234", "text": "Authenticated SMS test" }'Without the API key header, you'll receive a 401 Unauthorized error.
6. Implement Advanced Error Handling
Enhance error handling for production reliability with specific error messages and Vonage error code mapping.
6.1 Enhanced SMS Service Error Handling:
Update
smsService.jswith comprehensive error handling and Vonage error code mapping:async function sendSms(to, text) { const from = process.env.VONAGE_FROM_NUMBER; if (!from) { throw new Error('Server configuration error: VONAGE_FROM_NUMBER not set'); } try { const response = await vonage.sms.send({ to, from, text }); if (response.messages[0].status === '0') { console.log(`SMS sent successfully to ${to}. Message ID: ${response.messages[0]['message-id']}`); return { success: true, messageId: response.messages[0]['message-id'], to, }; } else { const error = response.messages[0]; // Map Vonage error codes to user-friendly messages const errorMessages = { '1': 'Message throttled – too many requests', '2': 'Missing parameters', '3': 'Invalid parameters', '4': 'Invalid credentials', '5': 'Internal error', '6': 'Invalid message', '7': 'Number barred', '8': 'Partner account barred', '9': 'Partner quota exceeded', '15': 'Invalid sender address', }; const userMessage = errorMessages[error.status] || error['error-text']; throw new Error(`SMS failed: ${userMessage}`); } } catch (error) { if (error.code === 'ECONNREFUSED') { throw new Error('Cannot connect to Vonage API. Check your network connection.'); } else if (error.code === 'ETIMEDOUT') { throw new Error('Vonage API request timed out. Please try again.'); } throw error; } }Vonage Error Code Mapping:
This error mapping helps you quickly diagnose issues and provide meaningful feedback to API consumers.
7. Deploy Your SMS API to Production
Prepare your application for production deployment with deployment scripts and hosting platform configuration.
7.1 Add NPM Scripts:
Update
package.jsonto include useful scripts for development and production:{ "name": "vonage-sms-sender", "version": "1.0.0", "description": "Production-ready SMS API using Vonage and Express", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": ["vonage", "sms", "api", "express", "nodejs"], "author": "Your Name", "license": "MIT", "dependencies": { "express": "^4.18.2", "@vonage/server-sdk": "^3.12.0", "dotenv": "^16.3.1", "joi": "^17.11.0", "helmet": "^7.1.0", "cors": "^2.8.5", "express-rate-limit": "^7.1.5", "morgan": "^1.10.0" }, "devDependencies": { "nodemon": "^3.0.2" }, "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" } }Install nodemon for automatic server restarts during development:
Run in development mode with auto-restart:
7.2 Deploy to Heroku:
Deploy your SMS API to Heroku's cloud platform:
Install Heroku CLI – Download from Heroku CLI.
Login to Heroku:
Create Heroku App:
Set Environment Variables:
heroku config:set VONAGE_API_KEY=your_api_key heroku config:set VONAGE_API_SECRET=your_api_secret heroku config:set VONAGE_FROM_NUMBER=your_from_number heroku config:set API_KEY=your_secure_api_key heroku config:set NODE_ENV=productionDeploy Application:
git init git add . git commit -m "Initial commit – Vonage SMS API" git push heroku mainTest Deployed API:
curl -X POST https://your-vonage-sms-api.herokuapp.com/send-sms \ -H "Content-Type: application/json" \ -H "X-API-Key: your_secure_api_key" \ -d '{"to": "14155551234", "text": "Hello from Heroku!"}'7.3 Deploy to Railway:
Railway offers simple Node.js deployment with automatic builds:
7.4 Deploy to Render:
Render provides free tier for Node.js applications:
npm installnpm start7.5 Deploy with PM2 (VPS/Cloud Server):
For VPS deployment (DigitalOcean, AWS EC2, Linode), use PM2 process manager:
# Install PM2 globally npm install -g pm2 # Start application with PM2 pm2 start index.js --name vonage-sms-api # Configure PM2 to start on system boot pm2 startup pm2 save # Monitor application pm2 status pm2 logs vonage-sms-api # Restart application pm2 restart vonage-sms-apiPM2 provides automatic restarts on crashes, load balancing, and zero-downtime reloads.
Frequently Asked Questions (FAQ)
How much does it cost to send SMS messages with Vonage?
Vonage SMS pricing varies by destination country. US SMS typically costs $0.0075 per message (less than 1 cent). International rates range from $0.01 to $0.50+ per message depending on the country. Check current pricing at Vonage SMS Pricing. New accounts include free credit ($2) for testing. Volume discounts available for high-volume senders.
What phone number format should I use for international SMS?
Always use E.164 format – the international standard for phone numbers. Format:
[country code][area code][local number]without spaces, dashes, or special characters. Examples:14155551234(US),447700900000(UK),61412345678(Australia),919876543210(India). Remove leading zeros and include the country code prefix.Can I send SMS messages to international phone numbers?
Yes. Vonage supports SMS delivery to 200+ countries worldwide. Ensure your Vonage account has international SMS enabled (check Dashboard → Settings → SMS). Verify sufficient account credit for international delivery. Pricing varies significantly by country – some regions cost 10-50x more than US domestic SMS. Test with small volumes first.
How do I handle SMS delivery receipts and status updates?
Vonage can send delivery receipt callbacks (webhooks) to your server when SMS messages are delivered or fail. Configure webhook URL in Vonage Dashboard under Settings → SMS Settings → Delivery Receipts. Implement a
POSTendpoint (e.g.,/webhooks/delivery-receipt) to receive status updates. Delivery receipts include message ID, status, timestamp, and error codes. Store these in your database for tracking and analytics.What's the character limit for SMS messages?
Standard SMS supports 160 characters using GSM-7 encoding (basic Latin alphabet, numbers, common symbols). Messages using special characters, emojis, or non-Latin scripts use Unicode (UCS-2) encoding, which reduces the limit to 70 characters per segment. Vonage automatically concatenates longer messages into multiple segments (up to 6 segments = 918 GSM-7 characters or 402 Unicode characters). Each segment is billed separately.
How do I prevent abuse and spam using my SMS API?
Implement multiple security layers: (1) Require API key authentication (shown in Section 5), (2) Enable rate limiting to restrict requests per IP/user (10-100 requests per minute), (3) Validate phone numbers against allowlists or known patterns, (4) Monitor usage with logging and alerting, (5) Set spending limits in Vonage Dashboard to prevent unexpected charges, (6) Implement CAPTCHA for user-facing forms, (7) Add phone number verification (send confirmation code before allowing sends), (8) Block disposable/virtual numbers if needed.
What are the differences between Vonage Messages API and SMS API?
Vonage offers two SMS APIs: (1) SMS API (used in this tutorial) – Legacy, simpler API with straightforward REST endpoints, supports basic SMS sending and delivery receipts. (2) Messages API – Newer unified API supporting SMS, MMS, WhatsApp, Viber, Facebook Messenger, and more channels. Offers advanced features like message concatenation, Unicode support, and channel failover. For basic SMS needs, SMS API is sufficient and simpler. For multi-channel messaging, use Messages API.
How do I test SMS sending without spending money?
Vonage provides several testing options: (1) Use free credit from new account signup ($2-10 depending on promotion), (2) Test with Vonage virtual numbers in sandbox mode (check documentation), (3) Send messages to your own verified phone numbers only during development, (4) Use Vonage's test credentials if available for your account type, (5) Mock the Vonage SDK in unit tests to avoid actual API calls. Always set strict rate limits and spending alerts during development.