code examples
code examples
How to Send SMS with Vonage API in Node.js and Express (2025 Guide)
Learn how to send SMS programmatically using Vonage API with Node.js and Express. Step-by-step tutorial covering setup, authentication, error handling, and production deployment.
Send SMS with Vonage API in Node.js: Complete Express Tutorial
Last Updated: January 2025
Framework Note: This tutorial demonstrates SMS implementation using Node.js with Express. The Vonage SMS API works with any Node.js framework. For framework-specific implementations like Next.js App Router, Server Actions, or database integration with Supabase, you can adapt the core SMS sending logic shown here to your preferred stack.
Build a production-ready Node.js application using Express to send SMS messages programmatically via the Vonage SMS API (formerly Nexmo). This comprehensive tutorial walks you through everything from initial project setup with the @vonage/server-sdk to implementing essential features like authentication, error handling, security best practices, and production deployment.
By the end of this guide, you'll have a working Express REST API endpoint that sends SMS messages to any phone number worldwide. This foundation is perfect for building SMS-enabled features like two-factor authentication (2FA), transactional notifications, delivery alerts, appointment reminders, or verification codes.
Project Overview and Goals
-
Goal: Create a REST API endpoint using Node.js and Express that sends SMS messages via the Vonage SMS API.
-
Problem Solved: Enable programmatic SMS sending without managing complex telecom infrastructure or carrier integrations.
-
Technologies:
- Node.js: Asynchronous JavaScript runtime environment.
- Express: Minimalist web framework for Node.js, used to build the API endpoint.
- Vonage Node.js SDK (
@vonage/server-sdk): Official SDK that simplifies interaction with the Vonage SMS API and other Vonage services. dotenv: Manages environment variables for configuration and secrets.
-
Architecture: A client (like
curl, Postman, or another application) sends an HTTP POST request to your Express API. The Express application validates the request, uses the Vonage SDK (configured with API credentials) to interact with the Vonage SMS API, which then delivers the message to the recipient's mobile device.+--------+ +-----------------+ +------------+ +----------+ | Client | -----> | Express API | -----> | Vonage API | -----> | Recipient| | (curl/ | | (Node.js) | | (SMS) | | (Phone) | | App) | | - Validate | +------------+ +----------+ +--------+ | - Use SDK | | - Handle Response | +-----------------+ | v +-----------------+ | Vonage SDK | | (@vonage/server-| | sdk) | +-----------------+(Note: For published documentation, consider replacing this ASCII diagram with a graphical version like SVG or PNG for better clarity.)
-
Prerequisites:
- Node.js and npm (or yarn) installed. Recommended: Node.js v20.x LTS or v22.x LTS for optimal compatibility and long-term support. Node.js v18 reached end-of-life on April 30, 2025. Download Node.js
- A Vonage API account. Sign up for free (comes with free credit for testing).
- A text editor or IDE (e.g., VS Code).
- A tool for making HTTP requests (e.g.,
curlor Postman). - Current Package Versions (as of January 2025):
@vonage/server-sdk: v3.25.x (latest version: 3.25.1, released September 2025)express: v4.19.x, v4.20.x, or v5.1.0 (released March 31, 2025)dotenv: v16.4.x
1. Set Up Your Node.js Project
Initialize your Node.js project and install the Vonage SDK and required dependencies.
-
Create Your Project Directory: Open your terminal and create a new directory for the project, then navigate into it.
bashmkdir vonage-sms-guide cd vonage-sms-guide -
Initialize Your Node.js Project: Create a
package.jsonfile to manage your project dependencies and scripts.bashnpm init -y -
Install Your Dependencies: Install Express for the web server, the Vonage SDK to interact with the API, and
dotenvto handle environment variables securely. Runningnpm installwithout specific versions will install the latest stable releases.bashnpm install express @vonage/server-sdk dotenvFor production applications, pin to specific versions for reproducibility:
bashnpm install express@^4.19.2 @vonage/server-sdk@^3.25.0 dotenv@^16.4.5express: The web framework.@vonage/server-sdk: The official Vonage SDK for Node.js.dotenv: Loads environment variables from a.envfile intoprocess.env.
Version Compatibility Note: The
@vonage/server-sdkv3.x requires Node.js v14 or higher. For production use, prefer Node.js v20 or v22 LTS versions. -
Enable ES Modules (Optional but Recommended): Use ES Modules (
import/export) for modern JavaScript practice. Open yourpackage.jsonfile and add the following line:json{ "name": "vonage-sms-guide", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@vonage/server-sdk": "^3.25.0", "dotenv": "^16.4.5", "express": "^4.19.2" }, "type": "module" } -
Create Your
.gitignoreFile: Prevent sensitive files (like.envandnode_modules) from being committed to version control. Create a file named.gitignorein the root directory:text# .gitignore # Dependencies node_modules/ # Environment variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* # Optional Editor directories/files .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.sublime-workspace -
Create Your Project Files: Create the main application file and the environment configuration file.
bashtouch index.js .envYour project structure should now look like this:
vonage-sms-guide/ ├── .gitignore ├── .env ├── index.js ├── package.json ├── package-lock.json (or yarn.lock) └── node_modules/
2. Implement Core SMS Sending Functionality
Configure the Vonage SDK client and create the core function to send SMS messages programmatically.
-
Configure Your Environment Variables: Open the
.envfile. Replace the placeholder values (YOUR_API_KEY,YOUR_API_SECRET,YOUR_VONAGE_VIRTUAL_NUMBER,MyApp) with your actual credentials and chosen sender ID obtained from your Vonage Dashboard (see Section 4).dotenv# .env # Vonage API Credentials - REPLACE with your actual credentials from Vonage Dashboard VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Sender Information (Use EITHER a purchased Vonage number OR an Alphanumeric Sender ID) # Option 1: Use a Vonage Number (Recommended for reliability & two-way) # Uncomment and replace YOUR_VONAGE_VIRTUAL_NUMBER with your purchased number in E.164 format (e.g., 14155550100) # VONAGE_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER # Option 2: Use an Alphanumeric Sender ID (e.g., "MyApp", check country regulations) # Replace "MyApp" with your desired brand name (up to 11 chars) or comment this out if using VONAGE_NUMBER VONAGE_BRAND_NAME=MyAppVONAGE_API_KEY/VONAGE_API_SECRET: Found in your Vonage Dashboard (see Section 4). You must replace the placeholders.VONAGE_NUMBERORVONAGE_BRAND_NAME: This is the 'from' identifier for your SMS.- Using a purchased
VONAGE_NUMBER(in E.164 format, e.g.,14155550100) is more reliable for SMS delivery, enables two-way messaging, and is required in some regions like the United States. ReplaceYOUR_VONAGE_VIRTUAL_NUMBERif using this option. - Using a
VONAGE_BRAND_NAME(Alphanumeric Sender ID, up to 11 characters) is possible in some countries but often requires pre-registration and doesn't support replies. ReplaceMyAppwith your desired sender name if using this option. Be aware of limitations and country-specific rules (see Section 8).
- Using a purchased
-
Initialize Vonage SDK and Create Send Function: Open
index.jsand add the following code:javascript// index.js import express from 'express'; import { Vonage } from '@vonage/server-sdk'; // Imports 'dotenv' and immediately loads variables from .env into process.env import 'dotenv/config'; // --- Vonage Client Initialization --- // Ensure API Key and Secret are loaded from environment variables if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET) { console.error('__ Missing Vonage API Key or Secret. Please check your .env file or environment variables.'); process.exit(1); // Exit if credentials are missing } const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, }); // Determine the sender ('from') based on environment variables const fromNumber = process.env.VONAGE_NUMBER || process.env.VONAGE_BRAND_NAME; if (!fromNumber) { console.error('__ Missing VONAGE_NUMBER or VONAGE_BRAND_NAME in .env file or environment variables. Please set one.'); process.exit(1); } console.log(`__ SMS will be sent from: ${fromNumber}`); // --- Core SMS Sending Function --- async function sendSms(recipient, message) { console.log(`Attempting to send SMS to ${recipient}...`); try { // Note: This uses the legacy SMS API endpoint via the SDK (`vonage.sms.send()`). // For newer features (like combining SMS/WhatsApp/etc.), explore the Messages API (`vonage.messages.send()`). const responseData = await vonage.sms.send({ to: recipient, from: fromNumber, text: message, }); // Check the status directly from the first message in the response array if (responseData.messages[0].status === '0') { console.log(`_ Message sent successfully to ${recipient}. Message ID: ${responseData.messages[0]['message-id']}`); return { success: true, data: responseData.messages[0] }; } else { // Handle errors returned by the Vonage API const errorCode = responseData.messages[0].status; const errorText = responseData.messages[0]['error-text']; console.error(`_ Message failed with error code ${errorCode}: ${errorText}`); // Provide more specific user-friendly messages for common errors let userMessage = `Message failed: ${errorText}`; if (errorCode === '15') { // Non-Whitelisted Destination userMessage = 'Sending failed: The destination number is not on the allowed list for this trial account. Please add it via the Vonage Dashboard settings.'; } else if (errorCode === '2') { // Missing parameters userMessage = 'Sending failed: Missing required parameters in the request to Vonage.'; } else if (errorCode === '9') { // Partner quota exceeded userMessage = 'Sending failed: Insufficient account balance.'; } // For a complete list of possible status codes, refer to the Vonage SMS API documentation: // https://developer.vonage.com/en/messaging/sms/guides/troubleshooting-sms return { success: false, message: userMessage, errorDetails: responseData.messages[0] }; } } catch (err) { // Handle potential network errors or exceptions thrown by the SDK console.error('_ Error sending SMS via Vonage SDK:', err); return { success: false, message: 'An unexpected error occurred while trying to send the SMS.', errorDetails: err }; } } // --- (Express API Layer will be added below) ---- We import necessary modules.
import 'dotenv/config'automatically loads variables from.envintoprocess.env. - We initialize the
Vonageclient using the API key and secret fromprocess.env. Crucially, we add checks to ensure these variables are present. - We determine the
fromNumberbased on which environment variable (VONAGE_NUMBERorVONAGE_BRAND_NAME) is set, prioritizing the number if both are present. - The
sendSmsfunction takes therecipientnumber andmessagetext. - It uses
vonage.sms.send(), part of the SDK interacting with Vonage's standard SMS API. - It uses
async/awaitfor cleaner handling of the promise returned by the SDK. - It checks the
statusin the response:'0'means success. Any other status indicates an error reported by the Vonage API per the official Vonage troubleshooting documentation. - We log success or failure details and return a structured object
{ success: boolean, data/message: ..., errorDetails: ... }. We include specific user-friendly messages for common errors and link to the official documentation for all codes. - A
try...catchblock handles potential network errors or SDK-level exceptions.
- We import necessary modules.
3. Build Your Express API Layer
Create the Express server and configure the REST API endpoint for sending SMS messages.
Add the following code to the bottom of index.js:
// index.js (continued)
// --- Express API Setup ---
const app = express();
const PORT = process.env.PORT || 3000; // Use port from environment or default to 3000
// Middleware to parse JSON and URL-encoded request bodies
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// --- API Endpoint: POST /send-sms ---
app.post('/send-sms', async (req, res) => {
console.log('Received request body:', req.body); // Log incoming request for debugging
// Basic Input Validation
const { to, text } = req.body;
if (!to || !text) {
console.log('Validation failed: Missing "to" or "text" in request body.');
return res.status(400).json({ success: false, message: 'Missing required fields: "to" (recipient phone number) and "text" (message content).' });
}
// Phone Number Format Check (Basic - Improve for Production)
// WARNING: This is a very basic regex and NOT suitable for production.
// Use a robust library like 'libphonenumber-js' for proper E.164 validation and parsing.
if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
console.log(`Validation failed: Invalid phone number format for "${to}".`);
// Recommend E.164 format (+countrycode)(number) e.g., +1234567890
return res.status(400).json({ success: false, message: 'Invalid recipient phone number format. Please use E.164 format (e.g., +1234567890).' });
}
// Call the core sending function
const result = await sendSms(to, text);
// Send Response based on the outcome of sendSms
if (result.success) {
// Success: Return 200 OK with Vonage response data
res.status(200).json(result);
} else {
// Failure: Determine appropriate status code
// 400 for client-side errors identifiable from Vonage response (like non-whitelisted number)
// 500 for server-side errors or unexpected issues
const statusCode = result.errorDetails?.status === '15' ? 400 : 500;
res.status(statusCode).json(result);
}
});
// --- Health Check Endpoint ---
app.get('/health', (req, res) => {
// Simple endpoint to check if the service is running
res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() });
});
// --- Start the Server ---
app.listen(PORT, () => {
console.log(`__ Server listening at http://localhost:${PORT}`);
console.log(` Send SMS endpoint: POST http://localhost:${PORT}/send-sms`);
console.log(` Health check: GET http://localhost:${PORT}/health`);
// Log partial key for confirmation that environment variables are loaded (avoid logging full secret!)
console.log(` Using API Key: ${process.env.VONAGE_API_KEY?.substring(0, 4)}...`);
});- We initialize the Express application.
- We define the
PORT(defaulting to 3000 if not set in.env). - We add
express.json()andexpress.urlencoded()middleware to parse incoming request bodies. - The
POST /send-smsroute is defined:- It performs basic validation to ensure
toandtextfields are present in the JSON request body. - It includes a rudimentary regex check for phone number format, strongly recommending E.164 and advising a proper library like
libphonenumber-jsfor production use. - It calls our
sendSmsfunction with the validated data. - It sends a JSON response back to the client based on the
successproperty of the result fromsendSms, setting appropriate HTTP status codes (200 for success, 400 for specific client errors like invalid number, 500 for other failures).
- It performs basic validation to ensure
- A simple
/healthendpoint is added – useful for monitoring. - Finally,
app.listenstarts the server and logs helpful information, including a partial API key to confirm loading.
4. Configure Vonage API Credentials
Obtain your Vonage API credentials and configure your SMS sender ID for message delivery.
-
Sign Up or Log In: Go to the Vonage API Dashboard and sign up or log in.
-
Find Your API Key and Secret: Once logged in, your API Key and API Secret are usually displayed prominently on the main dashboard page or within the "API settings" section. Look for values labeled
API keyandAPI secret. -
Update Your
.envFile: Paste the copied key and secret into your.envfile, replacing theYOUR_API_KEYandYOUR_API_SECRETplaceholders.dotenv# .env VONAGE_API_KEY=abc123def456 # <-- Paste your Key here VONAGE_API_SECRET=XYZ789ghijk # <-- Paste your Secret here # ... rest of the file (VONAGE_NUMBER or VONAGE_BRAND_NAME)Save the file after pasting your actual credentials.
-
Configure Your Sender ID (
VONAGE_BRAND_NAMEorVONAGE_NUMBER): Decide how your messages will appear to recipients and configure the corresponding line in.env:-
If using
VONAGE_BRAND_NAME: Ensure the value in.envis appropriate (e.g.,MyApp, replacing the default placeholder). Be aware of country restrictions and potential registration needs (see Section 8). -
If using
VONAGE_NUMBER:- You need to purchase a Vonage virtual number. In the dashboard, navigate to "Numbers" > "Buy numbers". Search for and purchase a number with SMS capabilities in your desired country.
- Once purchased, go to "Numbers" > "Your numbers". Copy the number shown, making sure it's in E.164 format (e.g.,
14155550100). - Uncomment the
VONAGE_NUMBERline in your.envfile and paste the purchased number there, replacingYOUR_VONAGE_VIRTUAL_NUMBER. Comment out theVONAGE_BRAND_NAMEline.
dotenv# .env # ... API Key/Secret ... VONAGE_NUMBER=14155550100 # <-- Paste your purchased number here # VONAGE_BRAND_NAME=MyApp
-
-
(CRITICAL for Trial Accounts) Add Test Numbers:
- Important for trial accounts: Free trial Vonage accounts can only send SMS to verified test numbers. You must add and verify recipient phone numbers in your dashboard before testing. Attempting to send to non-verified numbers will result in a "Non-Whitelisted Destination" error (Status Code
29per Vonage SMS documentation). - To add test numbers:
- In the Vonage Dashboard, navigate to your user menu (often top right) → Settings.
- Scroll down to find the "Test Numbers" section.
- Click "Add test number".
- Enter the phone number you want to send test messages to (e.g., your own mobile number) in E.164 format (including the
+and country code). - Vonage will send a verification code via SMS or voice call to that number. Enter the code in the dashboard to confirm ownership.
- Repeat for any other numbers you need to test with during the trial period.
- Important for trial accounts: Free trial Vonage accounts can only send SMS to verified test numbers. You must add and verify recipient phone numbers in your dashboard before testing. Attempting to send to non-verified numbers will result in a "Non-Whitelisted Destination" error (Status Code
5. Handle Errors, Logging, and Retries
Enhance error handling and logging for production readiness.
-
Error Handling:
- The
sendSmsfunction usestry...catchto handle SDK/network errors. - It checks the
statuscode from the Vonage API response for specific sending failures. According to Vonage's official SMS API error documentation, status code0indicates success, while non-zero values indicate errors such as:1- Throttled2- Missing parameters3- Invalid parameters4- Invalid credentials9- Partner quota violation (insufficient funds)15- Invalid sender address (non-whitelisted destination in trial accounts)29- Non-whitelisted destination
- User-friendly messages are generated for common, actionable errors.
- The API endpoint returns appropriate HTTP status codes (200, 400, 500) based on the outcome.
- The
-
Logging:
- We use
console.logandconsole.errorfor basic operational logging. - Recommendation: For production, use a dedicated logging library like Winston or Pino. These allow for:
- Structured Logging (JSON): Easier parsing and analysis by log management systems.
- Log Levels: Differentiating between debug, info, warning, and error messages.
- Log Destinations: Outputting logs to files, databases, or external logging services (e.g., Datadog, Splunk, ELK Stack).
javascript// Conceptual example using a logger (replace console.log/error) // import logger from './logger'; // Assuming logger setup elsewhere // logger.info(`Attempting to send SMS to ${recipient}`, { recipient }); // logger.error(`Message failed with error code ${errorCode}: ${errorText}`, { errorCode, errorText, details: responseData.messages[0] }); - We use
-
Retry Mechanisms:
- Vonage generally handles retries for delivering the SMS to the carrier network. Retrying the API call from your application needs careful consideration.
- When to Retry: Consider retrying only for transient errors like network timeouts or temporary Vonage service unavailability (indicated by specific 5xx error codes, if documented by Vonage).
- When NOT to Retry: Do not retry on definitive failures like 'Invalid Credentials' (4), 'Insufficient Funds' (9), 'Non-Whitelisted Destination' (15, 29), or invalid input parameters (2, 3, 6). Retrying these will not succeed and will waste resources/API calls.
6. Database Integration for SMS Logging (Optional)
Learn when and how to add database functionality for SMS audit logging and tracking.
- Database Integration:
- This specific application, designed solely as an API endpoint to trigger SMS sending, does not inherently require a database.
- However, in a more comprehensive application, you would likely integrate this SMS sending capability and use a database for:
- Audit Logging: Storing a record of every SMS sent (recipient, sender, message body, timestamp, Vonage message ID, final status, cost) for tracking, debugging, and compliance.
- User Association: Linking sent messages to specific users within your application.
- Message Templating: Storing reusable message templates.
- Queueing: For high-volume sending, messages might be added to a database queue (or a dedicated message queue like RabbitMQ/Redis) and processed by background workers to avoid overwhelming the API or delaying web requests.
- If a database is needed:
- Choose Database: Select a suitable database (e.g., PostgreSQL, MySQL, MongoDB).
- Design Schema: Define tables/collections (e.g.,
sms_logswith columns likeid,recipient_number,sender_id,message_body,vonage_message_id,status_code,status_text,cost,sent_at,updated_at). - Select Data Access Tool: Use an ORM (Object-Relational Mapper) like Prisma, Sequelize (SQL), or an ODM (Object-Document Mapper) like Mongoose (MongoDB) to simplify database interactions in Node.js.
- Implement Logic: Write functions within your application to create, read, update, and delete records related to SMS messages.
7. Add Security Features
Secure your API endpoint and credentials.
- Input Validation and Sanitization:
-
We implemented basic presence checks for
toandtext. -
Robust Phone Number Validation: Crucially, replace the basic regex check with a dedicated library like
libphonenumber-js. This library, a JavaScript port of Google's libphonenumber, can parse, format, and validate phone numbers for different regions, ensuring they conform to the E.164 standard expected by Vonage.javascript// Example with libphonenumber-js: import { parsePhoneNumber } from 'libphonenumber-js'; try { const phoneNumber = parsePhoneNumber(to, { defaultCountry: 'US' }); if (!phoneNumber || !phoneNumber.isValid()) { return res.status(400).json({ success: false, message: 'Invalid phone number.' }); } // Use phoneNumber.number for E.164 format } catch (error) { return res.status(400).json({ success: false, message: 'Unable to parse phone number.' }); } -
Message Content (
text) Sanitization: While the risk of code injection directly via SMS is low compared to web contexts (SMS doesn't execute scripts), consider sanitization if:- The
textoriginates from untrusted user input. - The
textmight be stored and later displayed in a web interface (prevent XSS). - Basic sanitization might involve trimming leading/trailing whitespace. Complex HTML sanitization (e.g., using
dompurifyif rendering in HTML) is usually unnecessary unless the message content has downstream uses beyond simple SMS delivery.
- The
-
Length Validation: Enforce a maximum length for the
textfield to prevent abuse and manage costs associated with multi-part messages. Standard SMS using GSM-7 encoding supports 160 characters; Unicode messages support 70 characters per segment.
-
-
Secrets Management:
.envFile: Use.envfor local development only. Never commit the.envfile to version control (Git). Ensure.envis listed in your.gitignorefile.- Production Environment: Do not deploy the
.envfile. Use the environment variable management system provided by your deployment platform (e.g., Heroku Config Vars, AWS Secrets Manager, Google Secret Manager, Azure Key Vault, Docker environment variables). These services provide secure storage and injection of secrets into your application environment.
-
Rate Limiting:
- Protect your
/send-smsendpoint from abuse (e.g., denial-of-service attacks, spamming attempts) by limiting the number of requests a single client (IP address) can make within a certain time window. - Use middleware like
express-rate-limit.
bashnpm install express-rate-limitjavascript// index.js (near the top, after app = express()) import rateLimit from 'express-rate-limit'; // Configure rate limiter const smsLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes window max: 50, // Limit each IP to 50 requests per windowMs standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers message: { success: false, message: 'Too many SMS requests created from this IP, please try again after 15 minutes.' }, // Apply specifically to the send-sms route }); // Apply the middleware to the /send-sms route app.use('/send-sms', smsLimiter); - Protect your
-
HTTPS:
- Always use HTTPS in production. This encrypts the data (including recipient numbers and message content) between the client and your API server. Your deployment platform or a reverse proxy (like Nginx) typically handles TLS/SSL certificate management and termination.
-
Authentication/Authorization (If Necessary):
- The current example creates a public endpoint. If this API should only be used by specific clients (e.g., other internal services, authenticated users), implement an authentication mechanism:
- API Keys: Generate unique keys for client applications and require them in a header (e.g.,
X-API-Key). Validate the key on the server. - JWT (JSON Web Tokens): If users trigger the SMS, protect the endpoint using JWTs issued upon user login.
- OAuth: For third-party integrations.
- API Keys: Generate unique keys for client applications and require them in a header (e.g.,
- The current example creates a public endpoint. If this API should only be used by specific clients (e.g., other internal services, authenticated users), implement an authentication mechanism:
8. Handle Special Cases
Understand SMS delivery nuances and regional considerations.
- SMS Character Limits & Encoding (GSM-7 vs. Unicode):
- Standard SMS using GSM-7 encoding supports up to 160 characters per message.
- Unicode (UCS-2) encoding is required for emojis and non-Latin characters, reducing the limit to 70 characters per segment.
- Messages exceeding these limits are automatically split into multiple segments (concatenated SMS). Each segment is billed separately, so monitor message length to control SMS costs and optimize delivery.
- International Numbers & E.164 Formatting:
- Always strive to validate, store, and send phone numbers in the international E.164 format:
+followed by country code and the subscriber number (e.g.,+447700900000for UK,+12125550100for US). - Using a library like
libphonenumber-jshelps parse various input formats into E.164. Using this standard format ensures correct routing by Vonage and carriers.
- Always strive to validate, store, and send phone numbers in the international E.164 format:
- Sender ID Restrictions (
VONAGE_BRAND_NAMEvs.VONAGE_NUMBER):- Alphanumeric Sender IDs (
VONAGE_BRAND_NAME): Support varies greatly by country.- Many countries require pre-registration per Vonage's Global Sender ID Registration Guide.
- According to Vonage's United States SMS Features documentation, alphanumeric sender IDs are not allowed in the US and Canada for application-to-person (A2P) messaging. These regions require registered 10DLC (10-digit long code), Toll-Free, or Short Code numbers instead.
- Messages sent with unsupported Sender IDs may be blocked or have the ID replaced by a generic number.
- Virtual Numbers (
VONAGE_NUMBER): Using a Vonage-provided number (Long Code, Toll-Free) is generally the most reliable and compatible method globally, especially for two-way communication and delivery to regions like North America. - 10DLC Registration (US): As of January 2025, all 10-digit long codes used for A2P messaging in the US must be registered with The Campaign Registry (TCR). This registration includes brand and campaign registration requirements.
- Action: Always consult Vonage's country-specific SMS documentation regarding Sender ID regulations before choosing
VONAGE_BRAND_NAME.
- Alphanumeric Sender IDs (
- Delivery Receipts (DLRs):
- This guide focuses only on sending an SMS (submitting it to Vonage). Vonage then attempts delivery to the carrier.
- To get confirmation of whether the message was actually delivered to the recipient's handset (or if it failed), you need to implement Delivery Receipt (DLR) webhooks.
- DLR error codes are documented in Vonage's SMS Delivery Error Codes guide, which lists codes from 0 (delivered) to 99 (general error), including specific failures like absent subscriber, anti-spam rejection, and network errors.
- This involves:
- Setting a webhook URL in your Vonage account settings.
- Creating another endpoint in your Express application (e.g.,
POST /sms-status) to receive HTTP POST requests from Vonage containing status updates (e.g.,delivered,failed,expired) for messages you sent. - Processing these updates (e.g., updating your database log).
- Opt-Out Handling (Compliance):
- Messaging regulations (like TCPA in the US, GDPR in Europe) require providing recipients with a way to opt-out (e.g., replying STOP).
- If using a Vonage number capable of receiving SMS (
VONAGE_NUMBER), you must:- Configure an inbound message webhook in Vonage to point to another endpoint in your application (e.g.,
POST /inbound-sms). - Implement logic in that endpoint to detect keywords like STOP, UNSUBSCRIBE, CANCEL.
- Maintain an opt-out list (e.g., in your database) and check against it before sending any future messages to prevent sending to opted-out users. Vonage may offer some built-in subscription management features as well.
- Configure an inbound message webhook in Vonage to point to another endpoint in your application (e.g.,
9. Optimize Performance
Scale your SMS sending application effectively.
- Asynchronous Nature: Node.js is non-blocking by default. Using
async/awaitwith the Vonage SDK's promise-based methods ensures your API call doesn't block the server while waiting for Vonage's response. This is crucial for handling concurrent requests efficiently. - Vonage API Latency: The primary factor influencing the response time of your
/send-smsendpoint will be the network latency and processing time of the Vonage API itself. This is external to your application. - SDK Connection Management: The
@vonage/server-sdkhandles underlying HTTP(S) connections. Modern SDKs typically use connection pooling to reuse connections, reducing the overhead of establishing new connections for each request. - Caching: Caching is generally not applicable for the act of sending unique SMS messages. You might cache configuration or user data if retrieved from a database, but not the SMS sending operation itself.
- Load Testing: If you anticipate high traffic, use load testing tools (e.g.,
k6,artillery,ApacheBench (ab)) to simulate concurrent users hitting your/send-smsendpoint.- Monitor response times (average, p95, p99).
- Track error rates (4xx, 5xx).
- Check resource utilization (CPU, memory) on your server.
- Horizontal Scaling: If a single server instance cannot handle the load, deploy multiple instances of your application behind a load balancer. Ensure your application is stateless or manages state externally (e.g., using Redis for sessions if needed) to work correctly in a scaled environment.
- Queueing for High Throughput: For sending very large volumes of SMS messages rapidly, directly calling the Vonage API within the HTTP request handler might become a bottleneck or lead to timeouts. Implement a queueing system:
- The
/send-smsendpoint quickly validates the request and adds the message details (recipient, text) to a message queue (e.g., RabbitMQ, Redis Streams, AWS SQS). - Separate background worker processes read messages from the queue and call the
sendSmsfunction to interact with the Vonage API. - This decouples the API request from the actual sending process, improving API responsiveness and resilience.
- The
Related Resources
- Understanding E.164 Phone Number Format - Learn about international phone number formatting standards
- SMS Character Encoding Guide - Optimize message length and reduce costs
- 10DLC Registration for US SMS - Required registration for US business messaging
Frequently Asked Questions
Q: What's the difference between Vonage SMS API and Messages API?
A: The SMS API is designed specifically for SMS/MMS messaging, while the Messages API supports multiple channels (SMS, WhatsApp, Facebook Messenger, Viber). This tutorial uses the SMS API via vonage.sms.send(). For multi-channel messaging, explore vonage.messages.send().
Q: How much does it cost to send SMS with Vonage? A: Vonage SMS pricing varies by destination country. Most messages cost between $0.01-$0.10 per SMS segment. Check the Vonage pricing page for specific rates. New accounts receive free credit for testing.
Q: Can I send SMS to international numbers? A: Yes, Vonage supports SMS delivery to over 200 countries. Ensure phone numbers are in E.164 format with the correct country code. Pricing and delivery rates vary by country.
Q: Why am I getting "Non-Whitelisted Destination" errors? A: Trial accounts can only send SMS to verified test numbers. Add and verify recipient numbers in your Vonage Dashboard under Settings > Test Numbers before testing.
Q: How do I receive SMS replies with Node.js?
A: Configure an inbound webhook URL in your Vonage Dashboard, then create a POST endpoint (e.g., /webhook/inbound) in your Express app to receive incoming SMS data from Vonage.
Frequently Asked Questions
How to send SMS with Node.js and Express?
Use the Vonage Node.js SDK and Express to create an API endpoint. This endpoint receives recipient details and message content, then leverages the Vonage SMS API to send the message. The tutorial provides a detailed setup guide and code examples using the '@vonage/server-sdk' library.
What is the Vonage Node.js SDK?
The Vonage Node.js SDK ('@vonage/server-sdk') simplifies interaction with Vonage APIs within Node.js applications. It provides methods for various Vonage services, including sending SMS messages, making voice calls, and managing other communication channels.
Why use dotenv for Vonage API credentials?
Dotenv is used for managing environment variables. This keeps sensitive information like API keys and secrets out of your codebase. It loads these from a '.env' file, improving security and portability.
When should I use a Vonage number for sending SMS?
Using a Vonage virtual number as the sender is generally recommended for better reliability, two-way communication, and compliance with regulations in regions like North America. It's crucial for receiving replies and essential in certain countries.
Can I use an alphanumeric sender ID with Vonage?
Yes, in some countries. This uses a brand name (up to 11 characters) as the sender. However, country-specific regulations vary significantly. Many require pre-registration or disallow it entirely for application-to-person (A2P) messaging.
How to handle errors when sending SMS with Vonage?
The tutorial demonstrates error handling using try-catch blocks and status code checking from the Vonage API response. User-friendly messages should be provided for common error scenarios, along with specific HTTP status codes for various failures.
What is the rate limit for Vonage SMS API?
The tutorial doesn't specify Vonage's default rate limits. You can implement your own rate limiting using middleware like 'express-rate-limit' to protect your API from abuse and manage costs, customizing limits as needed.
How to validate phone numbers for Vonage SMS?
A basic regex is provided in the tutorial, but for production, use a library like 'libphonenumber-js'. This ensures proper E.164 formatting and handles international number variations reliably.
Why is HTTPS essential for a Vonage SMS API?
HTTPS encrypts communication between clients and your API. This protects sensitive data such as recipient phone numbers and message content. Always use HTTPS in production for security.
How to set up Vonage test numbers?
If you're on a Vonage trial account, go to your Dashboard settings, find "Test Numbers," and add the numbers you'll send test messages to. Verify ownership by entering the code Vonage sends to the added number.
What database schema is recommended for SMS logs?
The tutorial suggests an 'sms_logs' table with fields like 'id', 'recipient_number', 'sender_id', 'message_body', 'vonage_message_id', 'status_code', 'status_text', 'cost', 'sent_at', and 'updated_at' for comprehensive tracking.
How to receive SMS delivery receipts with Vonage?
Set up a webhook URL in your Vonage account settings, then create an endpoint in your Express app to receive POST requests from Vonage containing status updates (e.g., 'delivered', 'failed'). Process these updates in your app, such as by logging them in your database.
How to handle SMS opt-outs for compliance?
Configure an inbound message webhook in your Vonage settings to point to your app. Implement logic to identify keywords like STOP, UNSUBSCRIBE, and maintain a list of opted-out users to comply with regulations.
How to optimize performance of the Node.js SMS API?
The tutorial emphasizes the asynchronous nature of Node.js for efficient request handling. Load testing, horizontal scaling, and queuing are recommended for higher throughput, especially with large volumes of messages.