code examples
code examples
How to Send SMS with Node.js and Vonage API: Complete Tutorial
Learn how to build a Node.js REST API to send SMS messages using the Vonage SMS API. Complete guide with Express setup, code examples, error handling, and deployment.
How to Send SMS with Node.js and Vonage API: Complete Tutorial
This guide provides a complete walkthrough for building a Node.js and Express application that sends SMS messages using the Vonage SMS API. You'll learn everything from project setup to deployment and troubleshooting.
By the end, you'll have a functional REST API endpoint that accepts a phone number and message text, then uses the Vonage API to programmatically send SMS messages. This serves as a foundation for applications requiring SMS notifications, two-factor authentication, user verification, or other communication features.
Project Overview and Goals
What You're Building:
A minimal REST API built with Node.js and Express. This API exposes a single endpoint (POST /send-sms) that takes a recipient phone number and message body, and uses the Vonage Node.js SDK to send the SMS via the Vonage SMS API. This guide focuses on core functionality; production deployments require additional security, error handling, and scalability enhancements (discussed later).
Problem Solved:
Provides a straightforward, server-side mechanism to programmatically send SMS messages, abstracting direct Vonage API interaction into a simple API call.
Technologies Used:
- Node.js: JavaScript runtime for server-side code
- Express: Minimal Node.js web application framework for creating API endpoints
- Vonage Node.js SDK (
@vonage/server-sdk): Simplifies Vonage API interaction, handling authentication and request formatting (GitHub repo) - Vonage SMS API: Underlying service that dispatches SMS messages globally (General Availability status)
- dotenv: Module to load environment variables from a
.envfile intoprocess.env, keeping credentials secure
System Architecture:
An HTTP client (like curl or a web application) sends a POST request to the /send-sms endpoint of your Node.js/Express server. The server uses the Vonage SDK, configured with API credentials, to request the Vonage SMS API. Vonage then sends the SMS to the recipient's phone.
Prerequisites:
- Node.js and npm: Install from nodejs.org. Requires Node.js v18.11.0+ for built-in watch mode.
- Vonage API Account: Sign up for free at Vonage API Dashboard. You'll receive free credit for testing.
- Trial Account Limitation: New Vonage accounts start on trial. You must whitelist recipient phone numbers in the Vonage Dashboard before sending SMS during trial. See the Troubleshooting section for details.
- Vonage API Key and Secret: Found on your Vonage API Dashboard main page after signup
- Vonage Virtual Number: Rent a Vonage phone number capable of sending SMS from the Dashboard under "Numbers" > "Buy numbers". Trial accounts may have destination limitations.
- (Optional) HTTP Client: Tools like
curlor Postman for testing
Expected Outcome:
A running Node.js server with a /send-sms endpoint. Sending a POST request with valid to and text parameters results in an SMS sent via Vonage.
1. Setting Up Your Node.js SMS Project
Initialize your Node.js project and install dependencies.
-
Create Project Directory:
bashmkdir vonage-sms-guide cd vonage-sms-guide -
Initialize npm:
bashnpm init -yThis creates
package.jsonto manage dependencies and scripts. -
Install Dependencies:
bashnpm install express @vonage/server-sdk dotenv -
Create Project Files:
bashtouch index.js .env .gitignore -
Configure
.gitignore: Add these lines to prevent committing dependencies and sensitive credentials:text# .gitignore node_modules/ .env -
Configure Environment Variables (
.env): Replace placeholder values with your actual credentials from the Vonage Dashboard:dotenv# .env # Found on your Vonage API Dashboard VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Your Vonage virtual number capable of sending SMS VONAGE_VIRTUAL_NUMBER=YOUR_VONAGE_NUMBER # Port for the Express server PORT=3000VONAGE_API_KEY/VONAGE_API_SECRET: SDK uses these to authenticate requestsVONAGE_VIRTUAL_NUMBER: The "From" number appearing on recipient's phone (must be associated with your account)PORT: Local port for your Express server
Project Structure:
vonage-sms-guide/
├── node_modules/
├── .env # API keys and secrets (DO NOT COMMIT)
├── .gitignore # Untracked files
├── index.js # Main application code
├── package-lock.json # Exact dependency versions
└── package.json # Project metadata and dependencies
2. Implementing Core SMS Functionality
Write the code in index.js to set up the Express server, initialize the Vonage SDK, and create the SMS sending function.
// index.js
'use strict';
// 1. Import necessary modules
require('dotenv').config(); // Load environment variables from .env
const express = require('express');
const { Vonage } = require('@vonage/server-sdk');
// 2. Initialize Vonage SDK
// API Key and Secret are loaded from environment variables
const vonage = new Vonage({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET
});
// 3. Initialize Express app
const app = express();
app.use(express.json()); // Parse JSON request bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded data
// 4. Define the SMS sending function
async function sendSms(recipient, message) {
const from = process.env.VONAGE_VIRTUAL_NUMBER;
const to = recipient;
const text = message;
try {
// Uses Vonage's SMS API (General Availability)
// Reference: https://developer.vonage.com/en/messaging/sms/overview
const responseData = await vonage.sms.send({ to, from, text });
console.log(`Message sent successfully to ${to}. Message ID: ${responseData.messages[0]['message-id']}`);
// Status '0' indicates success per Vonage API spec
if (responseData.messages[0]['status'] === '0') {
return { success: true, data: responseData };
} else {
const errorText = responseData.messages[0]['error-text'];
console.error(`Message failed with error: ${errorText}`);
return { success: false, message: `Message failed: ${errorText}` };
}
} catch (err) {
// Handle network issues or SDK errors
console.error("Error sending SMS:", err);
return { success: false, message: 'Failed to send SMS due to an internal error.' };
}
}
// 5. Define the API endpoint (Covered in next section)
// ... Endpoint code will go here ...
// 6. Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port} on ${new Date().toLocaleString()}`);
console.log(`Using Vonage Number: ${process.env.VONAGE_VIRTUAL_NUMBER}`);
// Check for API credentials
if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET || !process.env.VONAGE_VIRTUAL_NUMBER) {
console.warn("***********************************************************************");
console.warn("WARNING: Vonage API credentials or virtual number missing in .env file!");
console.warn("SMS sending will likely fail. Check your .env configuration.");
console.warn("***********************************************************************");
}
});Explanation:
- Imports: Load
dotenvfor credentials,expressfor the server, and theVonageclass from the SDK - Vonage Initialization: Create a
Vonageinstance with API key and secret fromprocess.env. The SDK handles authentication using these credentials (SDK documentation) - Express Initialization: Create Express app and apply middleware to parse request bodies
sendSmsFunction:asyncfunction encapsulating SMS logic- Retrieves
fromnumber from environment variables - Uses
vonage.sms.send()withto,from, andtextparameters - Checks the
statusproperty inresponseData.messages[0]['status']. Status'0'indicates success according to Vonage API documentation - Handles both successful sends and API-level errors (invalid numbers, insufficient funds, etc.)
- Includes
try...catchfor network errors or SDK exceptions - Returns object with
success: true/falseand relevant data or error message
- Server Start: Retrieves
PORTfrom.env(defaults to 3000), starts Express server, logs confirmation and warnings if credentials are missing
3. Building the REST API Endpoint
Add the API endpoint to index.js before the app.listen call (where the "// 5. Define the API endpoint" comment is).
// index.js (Add before app.listen)
// 5. Define the API endpoint
app.post('/send-sms', async (req, res) => {
// Basic Input Validation
const { to, text } = req.body;
if (!to || !text) {
console.warn('Received invalid request: Missing "to" or "text" field.');
return res.status(400).json({
success: false,
message: 'Missing required fields: "to" (recipient phone number) and "text" (message body).'
});
}
// E.164 format check (international phone number standard)
const phoneRegex = /^\+[1-9]\d{1,14}$/;
if (!phoneRegex.test(to)) {
console.warn(`Received invalid phone number format: ${to}`);
return res.status(400).json({
success: false,
message: 'Invalid phone number format. Use E.164 format (e.g., +14155552671).'
});
// Note: Production validation should use libraries like google-libphonenumber
// for comprehensive country-specific rules
}
console.log(`Received request to send SMS to: ${to}`);
// Call the SMS sending function
const result = await sendSms(to, text);
// Send response based on result
if (result.success) {
res.status(200).json({
success: true,
message: 'SMS sent successfully.', // Final delivery is asynchronous
details: result.data // Include Vonage response details
});
} else {
// 500 for internal errors; could map specific Vonage errors to 400/502
res.status(500).json({
success: false,
message: result.message
});
}
});Explanation:
- Route Definition:
app.post('/send-sms', ...)listens for HTTP POST requests on/send-sms - Input Validation:
- Extracts
to(recipient) andtext(message) fromreq.body - Checks both fields are present; returns
400 Bad Requestif missing - Regex validates E.164 phone number format (international standard). Production apps should use robust libraries like
google-libphonenumber
- Extracts
- Call
sendSms: Invokes the function with validated parameters - Response Handling:
- On success (
200 OK): Returns success message and Vonage response data - On failure (
500 Internal Server Error): Returns error message. More refined implementations might map specific Vonage errors to different HTTP status codes
- On success (
4. Vonage API Integration Setup
Integration occurs during setup and initialization:
- Credentials: Stored securely in
.env(VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_VIRTUAL_NUMBER) - SDK Initialization:
VonageSDK instance created with these credentials inindex.js - API Call:
vonage.sms.send()handles communication with Vonage API, including authentication headers - Dashboard Configuration (Critical for Trial Accounts):
- Log in to Vonage API Dashboard
- Navigate to "Numbers" > "Your numbers" to confirm you have a virtual number capable of sending SMS
- Trial accounts: You must add and verify recipient phone numbers. Look for profile/settings or "Test Numbers" section. Sending to unverified numbers fails. Upgrade by adding payment details to remove restrictions (common beginner issue)
- API Settings: Check "API settings" in dashboard. The "Default SMS Setting" should be appropriate. This guide uses the SMS API (
vonage.sms.send). For receiving messages, this setting changes webhook formats
5. Error Handling, Logging, and Retry Logic
- Error Handling:
sendSmsfunction usestry...catchfor network or SDK-level errors- Inspects Vonage API response (
responseData.messages[0].statusanderror-text) to detect API-specific errors (invalid number, insufficient balance) - API endpoint returns appropriate HTTP status codes (400 for bad input, 500 for server/API errors, 200 for success)
- Logging:
- Basic
console.log,console.warn, andconsole.errorprovide visibility - Production recommendation: Use robust logging libraries (Winston or Pino) for structured JSON logs to files or logging services. Log: request receipt, validation failures, Vonage API calls, success/error responses, caught exceptions
- Basic
- Retry Mechanisms:
- This guide doesn't implement automatic retries
- Production recommendation: For transient network errors or rate-limiting (status code
1), implement retry strategy with exponential backoff using libraries likeasync-retry. Wrapvonage.sms.send()in a retry loop with progressive waits. Don't retry permanent errors (invalid API key, invalid recipient)
6. Database Schema
Not applicable for this simple SMS API. For tracking sent messages, storing templates, or managing users, add a database (PostgreSQL, MongoDB) and data layer (ORM like Prisma or Sequelize).
7. Security Best Practices for SMS APIs
- Secrets Management: Use
.envand.gitignore. In production, use environment variables from deployment platform or dedicated secrets manager - Input Validation: Basic validation for
toandtextpresence and format included. Use libraries likejoiorexpress-validatorfor complex validation in production - Rate Limiting: Not implemented. Use middleware like
express-rate-limitto prevent API abuse (limit requests per IP) - Authentication/Authorization: Endpoint is currently open. In production, protect with API keys, JWT tokens, or session authentication
8. Handling International SMS and Special Cases
- International Numbers: E.164 format (
+followed by country code and number) is standard. Vonage routes based on this. Ensure your account/number is enabled for destination countries - Character Limits & Encoding:
- Standard SMS: 160 GSM-7 characters or 70 UCS-2 (Unicode) characters per message (Vonage encoding guide)
- Longer messages split into concatenated SMS (charged per segment)
- Characters from GSM extended table require 2 bytes each
- Special characters (Chinese, Japanese, Korean, Arabic, Cyrillic) force UCS-2 encoding, reducing limit to 70 characters
- Vonage accepts up to 3,200 characters, but best practice is max 6 SMS parts
- Delivery Reports (DLRs): Not covered in this guide. Configure webhook URL in Vonage settings to receive delivery status updates (
delivered,failed,rejected). Requires adding another endpoint to your Express app for callbacks
9. Performance Optimization for High-Volume SMS
Not critical for single SMS messages. For high volumes, consider:
- Asynchronous Processing: Push SMS tasks to background job queue (e.g., BullMQ with Redis) and return immediate
202 Acceptedresponse. Separate worker process handles sending - SDK Client Reuse: The
vonageclient is initialized once and reused (already efficient)
10. Monitoring and Analytics for SMS Delivery
- Logging: Use structured logging (see Section 5)
- Health Checks: Add a
/healthendpoint returning200 OKfor load balancers or monitoring systems - Metrics: Track request counts, error rates, and latency for
/send-smsusing monitoring tools (Prometheus/Grafana, Datadog). Track Vonage API call latency and success/error rates - Error Tracking: Use services like Sentry or Bugsnag to capture and aggregate runtime errors
11. Common Issues and Troubleshooting
-
Error: 401 Unauthorized (
Authentication failed)- Cause: Incorrect
VONAGE_API_KEYorVONAGE_API_SECRETin.env - Solution: Verify credentials in
.envagainst Vonage Dashboard. Ensurerequire('dotenv').config();is called early. Check for extra spaces or characters
- Cause: Incorrect
-
Error:
Invalid 'From' number- Cause:
VONAGE_VIRTUAL_NUMBERis incorrect, not owned by your account, or not SMS-capable - Solution: Verify number in Dashboard under "Numbers" > "Your numbers". Ensure correct E.164 format
- Cause:
-
Error:
Non-Whitelisted Destination(Status code15)- Cause: Trial account sending to non-whitelisted phone number
- Solution: Add recipient to "Test Numbers" list in Dashboard, or upgrade account by adding billing details
-
Error:
Missing Mandatory Field(Status code4or5)- Cause: Missing or invalid
to,from, ortextparameter - Solution: Check
sendSmsfunction and/send-smsendpoint logic. Review logs for validation errors
- Cause: Missing or invalid
-
SMS Not Received, but API shows Success (
status: '0')- Causes: Carrier filtering, incorrect recipient number (valid format but wrong number), network issues, destination country restrictions
- Solutions:
- Verify recipient number is correct and active
- Check Dashboard logs ("Logs" > "SMS") for detailed delivery status
- Test with different number or carrier
- Verify account allows sending to specific country
- Set up DLR webhooks for detailed delivery status
-
Caveat: Environment Variables Not Loading
- Cause:
require('dotenv').config();not called, called too late, or.envfile in wrong location - Solution: Ensure
require('dotenv').config();is first line inindex.js. Verify.envlocation (project root wherenoderuns)
- Cause:
-
Caveat: Port Conflict (
EADDRINUSE)- Cause: Another application using the port
- Solution: Stop other application or change
PORTin.env
12. Production Deployment and CI/CD
-
Deployment Platforms: Deploy to Heroku, Render, AWS Elastic Beanstalk, Google Cloud Run, or VPS
-
Environment Variables: Never include
.envin deployment or Git commits. Configure variables (VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_VIRTUAL_NUMBER,PORT,NODE_ENV=production) in deployment platform's settings -
package.jsonScripts:json{ "scripts": { "start": "node index.js", "dev": "node --watch index.js" } }Note:
devscript requires Node.js v18.11.0+. For broader compatibility, usenodemon index.js(requiresnpm install --save-dev nodemon). Most platforms automatically runnpm start -
CI/CD Pipeline (GitHub Actions example):
- Push code to Git repository (GitHub, GitLab)
- Set repository secrets for Vonage credentials and deployment keys
- Create workflow file (
.github/workflows/deploy.yml) that:- Checks out code
- Sets up Node.js
- Installs dependencies (
npm ci– preferred in CI for usingpackage-lock.json) - (Optional) Runs tests (
npm test) - Deploys to platform using CLI or GitHub Actions (e.g.,
actions/heroku-deploy,google-github-actions/deploy-cloudrun) - Passes environment variables securely during deployment
-
Rollback: Most platforms offer mechanisms to redeploy previous versions (e.g., Heroku's
heroku rollback, AWS CodeDeploy strategies)
13. Testing Your SMS API
-
Start the Server Locally: Ensure
.envis correctly populated:bashnode index.jsYou should see "Server listening..." in the console.
-
Manual Testing (using
curl): Open a new terminal. Replace+14155550100with valid test number (whitelisted for trial accounts):bashcurl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "to": "+14155550100", "text": "Hello from your Node.js Vonage App!" }' -
Check Expected Output:
- Terminal running
curl:- Success:
json
{ "success": true, "message": "SMS sent successfully.", "details": { "message-count": "1", "messages": [ { "to": "14155550100", "message-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "status": "0", "remaining-balance": "x.xxxx", "message-price": "x.xxxx", "network": "xxxxx" } ] } } - Failure (validation):
json
{ "success": false, "message": "Missing required fields: \"to\" (recipient phone number) and \"text\" (message body)." } - Failure (API error):
json
{ "success": false, "message": "Message failed: Invalid Sender Address - rejected" }
- Success:
- Terminal running Node.js server: Check console logs for "Received request...", "Message sent successfully...", or error details
- Recipient Phone: Should receive the SMS
- Vonage Dashboard: Check "Logs" > "SMS" for message record and status
- Terminal running
-
Automated Testing:
- Unit Tests: Use Jest or Mocha/Chai to test
sendSmsin isolation. Mock@vonage/server-sdkto avoid actual API calls. Test different inputs and scenarios based on mocked responses - Integration Tests: Use
supertestto make HTTP requests to Express app. Test/send-smsendpoint, validating inputs, responses, and status codes
- Unit Tests: Use Jest or Mocha/Chai to test
Frequently Asked Questions
How do I send SMS messages from Node.js?
To send SMS from Node.js, install the Vonage SDK (npm install @vonage/server-sdk), initialize it with your API credentials, and call vonage.sms.send() with the recipient number, sender number, and message text. This guide provides a complete implementation with Express.
What is the best SMS API for Node.js?
Vonage SMS API (formerly Nexmo) is one of the most popular choices for Node.js due to its comprehensive SDK, reliable delivery, and competitive pricing. Other options include Twilio, AWS SNS, and MessageBird. The choice depends on your requirements for features, pricing, and geographic coverage.
How much does it cost to send SMS with Vonage?
Vonage SMS pricing varies by destination country. Most messages cost between $0.0045-0.02 USD per SMS segment. New accounts receive free credit for testing. Check the Vonage Pricing page for specific rates.
What is E.164 phone number format?
E.164 is the international phone number format required by most SMS APIs. It consists of a plus sign (+) followed by the country code and phone number, with no spaces or special characters (e.g., +14155552671 for a US number).
How many characters can I send in an SMS?
Standard SMS supports 160 GSM-7 characters or 70 Unicode characters per message. Longer messages are automatically split into concatenated SMS segments, with each part counting as a separate billable message. Characters from the GSM extended table use 2 bytes each.
Can I send SMS to international numbers?
Yes, Vonage supports SMS delivery to over 200 countries. Ensure your phone numbers use E.164 format with the correct country code. Note that trial accounts may have restrictions on international destinations until you add billing details.
How do I handle SMS delivery failures?
Implement comprehensive error handling by checking the status code in Vonage's response. Status '0' indicates success, while other codes indicate specific errors (invalid number, insufficient balance, etc.). Set up delivery receipt webhooks for real-time delivery confirmation.
What's the difference between Vonage SMS API and Messages API?
The SMS API (used in this guide) is optimized for simple text messaging and is in General Availability. The Messages API is a newer, more feature-rich platform supporting SMS, MMS, WhatsApp, Facebook Messenger, and Viber through a unified interface.
Next Steps
After completing this tutorial, consider these enhancements:
- Add delivery receipts by configuring webhook endpoints to track message delivery status
- Implement message templates for common use cases like verification codes or notifications
- Add rate limiting to prevent abuse and manage API costs
- Integrate with a database to log sent messages and track delivery metrics
- Explore the Messages API for multi-channel communication (SMS, WhatsApp, etc.)
- Set up monitoring with tools like Datadog or New Relic for production reliability
References
- Vonage SMS API Overview – Official SMS API documentation
- Vonage Node.js SDK (GitHub) – SDK source code and documentation
- SMS Concatenation & Encoding Guide – Character limits and encoding specifications
- Vonage API Dashboard – Account management and configuration
This guide provides a solid foundation for sending SMS messages with Node.js, Express, and Vonage. Adapt error handling, security, logging, and deployment strategies to fit your production requirements.
Frequently Asked Questions
How to send SMS with Node.js and Express?
Use the Vonage SMS API with the Node.js SDK and Express. Create an API endpoint that accepts recipient number and message text, then uses the SDK to send the SMS via Vonage.
What is the Vonage Node.js SDK used for?
The Vonage Node.js SDK (`@vonage/server-sdk`) simplifies interaction with the Vonage APIs. It handles authentication and request formatting, making it easier to send SMS messages from your Node.js application.
Why does Vonage require whitelisting numbers?
For trial accounts, Vonage requires whitelisting recipient numbers in the dashboard for security and to prevent abuse. This restriction is lifted once the account is upgraded with billing details.
When should I use the Vonage Messages API?
While this guide uses the simpler `vonage.sms.send()` method, Vonage's Messages API offers more advanced features. Consider it for richer messaging needs beyond basic SMS.
Can I send SMS to international numbers with Vonage?
Yes, use the E.164 format (+[country code][number]). Ensure your Vonage account and number are enabled for the destination countries and that you comply with any applicable regulations.
How to set up a Node.js project for sending SMS?
Initialize a Node project with `npm init`, install `express`, `@vonage/server-sdk`, and `dotenv`. Create `index.js`, `.env`, and `.gitignore`. Add API keys and virtual number to `.env` and implement the SMS sending logic.
What is the purpose of .env file in Vonage SMS project?
The `.env` file stores sensitive information like your Vonage API key, secret, and virtual number. It's crucial for security and should never be committed to version control.
How to handle errors when sending SMS with Vonage?
Use `try...catch` blocks for network/SDK errors. Check the Vonage API response status for success ('0') or specific error codes. Implement robust logging for easier debugging and monitoring in production.
How to test my Vonage SMS API endpoint?
Start your server locally with `node index.js`. Use a tool like `curl` or Postman to send POST requests to your `/send-sms` endpoint with test data, ensuring whitelisted recipient numbers in trial accounts. Check responses and logs to verify successful SMS delivery.
When to implement retry mechanisms for sending SMS messages?
In production, use retry mechanisms with exponential backoff for handling transient errors like network issues or rate limiting. Avoid retrying permanent errors like invalid API keys.
How to secure my Vonage SMS application?
Use a secrets manager for API keys, implement robust input validation, rate limiting, and appropriate authentication/authorization for the `/send-sms` endpoint.
What are SMS character limits with Vonage?
Standard SMS messages are limited to 160 characters (GSM-7 encoding). Longer messages may be split, incurring costs for multiple segments. Special characters might force UCS-2, further reducing per-segment limits.
How can I get delivery reports for my Vonage SMS?
Configure a webhook URL in your Vonage Dashboard settings to receive delivery reports. This will notify your application when a message is delivered, failed, or rejected.
How to troubleshoot 'Authentication failed' error with Vonage?
Double-check your `VONAGE_API_KEY` and `VONAGE_API_SECRET` in the `.env` file and your Vonage Dashboard. Ensure the `.env` file is loaded correctly early in your application.
Why is my Vonage SMS not received despite success status?
Several reasons include carrier issues, incorrect recipient number, temporary network problems, or country-specific restrictions. Verify the recipient number, consult Vonage Dashboard logs, and consider setting up delivery receipts.