code examples
code examples
Vonage SMS API Tutorial: Send SMS with Node.js & Express (2025)
Learn how to integrate Vonage SMS API with Node.js v22 and Express v5. Step-by-step guide with code examples, E.164 formatting, error handling, webhooks, and production deployment best practices.
Send SMS with Node.js, Express, and Vonage
Learn how to build a production-ready Node.js application using the Express framework to send SMS messages via the Vonage SMS API. This comprehensive tutorial covers project setup, API integration, error handling, phone number formatting, and deployment strategies.
By the end of this tutorial, you'll have a functional Express API endpoint that accepts a phone number and message text, then uses Vonage to deliver that message as an SMS.
Project Overview and Goals
Goal: Create a backend service that exposes an API endpoint to send SMS messages reliably using Vonage.
Problem Solved: This provides a foundational component for applications needing programmatic SMS capabilities, such as sending notifications, verification codes, or alerts.
Technologies Used:
- Node.js: A JavaScript runtime environment ideal for building scalable network applications.
- Express: A minimal and flexible Node.js web application framework, perfect for creating APIs. Express v5 was released October 15, 2024, with improved async error handling and security enhancements.
- Vonage SMS API: A powerful API for sending and receiving SMS messages globally. You'll use the
@vonage/server-sdkv3.24.1 for Node.js (latest as of 2025). - dotenv: A module to load environment variables from a
.envfile intoprocess.env, keeping sensitive credentials out of source code.
System Architecture:
A client sends a POST request with recipient (to) and message (text) to the Express API /send-sms endpoint. The Express app validates the input and uses the Vonage Node SDK (initialized with API credentials) to make an API call to the Vonage Cloud SMS API. Vonage then attempts to deliver the SMS to the recipient's phone. The Express app returns a success or error JSON response to the client based on the outcome of the Vonage API call.
Prerequisites:
- Node.js and npm (or yarn): Node.js v20 or later required (Node.js v18 reached EOL on April 30, 2025). Recommend v22 LTS for active support through April 2027. (Download Node.js)
- Vonage API Account: A free trial account is sufficient to start. You'll need your API Key and API Secret. (Sign up for Vonage)
- Vonage Phone Number: A virtual number rented from Vonage or the ability to register and verify test numbers (required for trial accounts).
- Basic understanding of JavaScript and Node.js concepts.
- A tool for making API requests: Such as
curl, Postman, or Insomnia. - Critical API Format Note: Vonage SMS API requires phone numbers in E.164 format without the leading '+' sign (e.g.,
15551234567, not+15551234567). This differs from standard E.164 notation and is essential for successful API calls. (Source: Vonage API Support documentation, effective 2025)
1. Setting up the project
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir vonage-sms-sender cd vonage-sms-sender -
Initialize Node.js Project: Use npm to create a
package.jsonfile. The-yflag accepts the default settings.bashnpm init -y -
Install Dependencies: We need Express for the web server, the Vonage SDK to interact with the API, and
dotenvfor managing environment variables.bashnpm install express @vonage/server-sdk dotenv --saveexpress: The web framework.@vonage/server-sdk: The official Vonage Node.js library.dotenv: To load environment variables from a.envfile.
-
Enable ES Modules: For using modern
import/exportsyntax, open yourpackage.jsonfile and add the following line at the top level:json{ "name": "vonage-sms-sender", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@vonage/server-sdk": "^3.11.0", "dotenv": "^16.3.1", "express": "^4.18.2" } }"type": "module"enables ES Module syntax.- The
"start"script provides a standard way to run the application.
-
Create
.gitignore: Prevent sensitive files and unnecessary directories from being committed to version control. Create a file named.gitignorein the project root:text# .gitignore # Dependencies node_modules/ # Environment variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # OS generated files .DS_Store Thumbs.db -
Create Environment File (
.env): This file will store your Vonage credentials and sender number. Never commit this file to Git. Create a file named.envin the project root:dotenv# .env - Store your Vonage Credentials Here # DO NOT COMMIT THIS FILE # Vonage API Credentials (Get from Vonage Dashboard -> API Settings) VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Vonage Virtual Number or Sender ID (Must be owned or registered) VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID # Server Port PORT=3000- Replace placeholders with your actual credentials later.
-
Project Structure: Our basic structure will look like this:
vonage-sms-sender/ ├── node_modules/ ├── .env ├── .gitignore ├── index.js # Main application file (Express server, API endpoint) ├── vonageClient.js # Initializes the Vonage SDK client ├── smsService.js # Contains the function to send SMS ├── package.json └── package-lock.json
2. Implementing Core Functionality (Vonage Integration)
We'll create separate modules for initializing the Vonage client and for the SMS sending logic. This promotes better organization and testability.
-
Initialize Vonage Client (
vonageClient.js): Create a file namedvonageClient.js. This module initializes the Vonage SDK using credentials from environment variables.javascript// vonageClient.js import { Vonage } from '@vonage/server-sdk'; import 'dotenv/config'; // Load .env variables into process.env // Ensure required environment variables are set if (!process.env.VONAGE_API_KEY || !process.env.VONAGE_API_SECRET) { console.error('Error: VONAGE_API_KEY and VONAGE_API_SECRET must be set in .env file.'); process.exit(1); // Exit if credentials are missing } const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET }); console.log('Vonage client initialized successfully.'); export default vonage; // Export the initialized client instance- We import
Vonagefrom the SDK anddotenv/configto load the.envfile immediately. - We add a check to ensure the API key and secret are present, exiting if not.
- We instantiate
Vonagewith the credentials. - We export the
vonageinstance for use in other modules.
- We import
-
Create SMS Sending Service (
smsService.js): Create a file namedsmsService.js. This module defines the function responsible for actually sending the SMS using the initialized Vonage client.javascript// smsService.js import vonage from './vonageClient.js'; // Import the initialized client import 'dotenv/config'; // Ensure sender number is set if (!process.env.VONAGE_SENDER_NUMBER) { console.error('Error: VONAGE_SENDER_NUMBER must be set in .env file.'); process.exit(1); } const from = process.env.VONAGE_SENDER_NUMBER; /** * Sends an SMS message using the Vonage API. * @param {string} recipient - The recipient's phone number (E.164 format recommended, e.g., +15551234567). * @param {string} messageText - The text content of the SMS message. * @returns {Promise<object>} - A promise that resolves with the Vonage API response data on success. * @throws {Error} - Throws an error if the SMS sending fails. */ export async function sendSms(recipient, messageText) { console.log(`Attempting to send SMS from ${from} to ${recipient}`); try { // Note: We use vonage.sms.send (part of the older SMS API binding within the SDK) // as it directly maps to the simple Key/Secret authentication used here. // The vonage.messages.send method often requires Application ID/Private Key auth. const responseData = await vonage.sms.send({ to: recipient, from: from, text: messageText }); // Check the status of the first (and usually only) message response if (responseData.messages[0]['status'] === '0') { console.log(`Message sent successfully to ${recipient}. Message ID: ${responseData.messages[0]['message-id']}`); return responseData; // Resolve with the full response } else { const errorCode = responseData.messages[0]['status']; const errorText = responseData.messages[0]['error-text']; console.error(`Message failed with error code ${errorCode}: ${errorText}`); // Throw a specific error for easier catching throw new Error(`Vonage API Error ${errorCode}: ${errorText}`); } } catch (err) { // Handle SDK-level errors (e.g., network issues, invalid credentials before API call) console.error('Error sending SMS:', err); // Re-throw the error to be handled by the caller (API route) throw err; } }- We import the initialized
vonageclient. - We retrieve the
VONAGE_SENDER_NUMBERfrom.env. - The
sendSmsfunction takes the recipient number (to) andmessageTextas arguments. - It calls
vonage.sms.send(), which is suitable for API Key/Secret authentication for basic SMS. - It checks the
statusfield in the Vonage response. Status'0'means success. - If successful, it logs the success and returns the response.
- If unsuccessful, it logs the error details provided by Vonage and throws a new
Errorcontaining the Vonage error message. - A
try...catchblock handles potential errors during the SDK call itself (like network errors).
- We import the initialized
3. Building the API Layer
Now, let's create the Express server and the API endpoint that will use our smsService.
-
Create Express Server (
index.js): Create the main application file,index.js.javascript// index.js import express from 'express'; import 'dotenv/config'; import { sendSms } from './smsService.js'; // Import our SMS sending function const app = express(); const port = process.env.PORT || 3000; // Use port from .env or default to 3000 // Middleware to parse JSON request bodies app.use(express.json()); // Middleware to parse URL-encoded request bodies app.use(express.urlencoded({ extended: true })); // Simple Root Route for Health Check / Basic Info app.get('/', (req, res) => { res.status(200).send('Vonage SMS Sender API is running!'); }); // --- API Endpoint to Send SMS --- app.post('/send-sms', async (req, res) => { // 1. Input Validation const { to, text } = req.body; if (!to || !text) { console.log('Validation Error: 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).', }); } // Basic check for phone number format (can be enhanced) // E.164 format (e.g., +15551234567) is generally recommended for Vonage if (!/^\+?[1-9]\d{1,14}$/.test(to)) { console.log(`Validation Error: Invalid phone number format for "to": ${to}`); return res.status(400).json({ success: false, message: 'Invalid phone number format. Please use E.164 format (e.g., +15551234567).', }); } try { // 2. Call the SMS Service console.log(`Received request to send SMS to: ${to}`); const result = await sendSms(to, text); // 3. Send Success Response // Optionally, filter the result to return only essential info like message ID const messageId = result.messages[0]['message-id']; res.status(200).json({ success: true, message: 'SMS sent successfully.', messageId: messageId, // You might include remaining balance if needed, from result.messages[0]['remaining-balance'] }); } catch (error) { // 4. Send Error Response console.error(`Failed to send SMS via API endpoint: ${error.message}`); // Check for specific Vonage errors if needed, e.g., non-whitelisted number // The error message from smsService already contains Vonage details let statusCode = 500; // Default to Internal Server Error if (error.message.includes("Non-Whitelisted Destination")) { statusCode = 403; // Forbidden - specific issue } else if (error.message.includes("Invalid Credentials")) { statusCode = 401; // Unauthorized } // Add more specific error code mappings as needed res.status(statusCode).json({ success: false, message: `Failed to send SMS: ${error.message}`, }); } }); // --- Start the Server --- app.listen(port, () => { console.log(`Server listening on http://localhost:${port}`); console.log(`SMS Send Endpoint: POST http://localhost:${port}/send-sms`); });- We import
expressand oursendSmsfunction. - We set up the Express app and configure middleware (
express.json,express.urlencoded) to handle incoming request bodies. - A basic
/route is added for simple health checks. - The
POST /send-smsroute is defined asasyncto useawait. - Input Validation: It first checks if
toandtextare present in the request body (req.body). If not, it returns a400 Bad Requesterror. A basic phone number format check is also included. - Service Call: It calls
await sendSms(to, text)inside atry...catchblock. - Success Response: If
sendSmsresolves successfully, it sends a200 OKJSON response indicating success and includes themessageIdfrom the Vonage result. - Error Handling: If
sendSmsthrows an error (either from the SDK or our explicit checks), thecatchblock executes. It logs the error and sends an appropriate error status code (e.g., 400, 403, 500) with a JSON response detailing the failure. We map specific error messages to HTTP status codes. - Finally,
app.listenstarts the server on the configured port.
- We import
4. Integrating with Vonage API (Configuration and Setup)
This section details how to get the necessary credentials and configure your Vonage account for SMS messaging.
-
Sign Up/Log In: Go to the Vonage API Dashboard and sign up or log in.
-
Find API Key and Secret:
- Navigate to your Dashboard homepage.
- Your API key and API secret are displayed prominently near the top under the "API keys" section.
- Dashboard Path:
Vonage Dashboard->API Settings(usually visible on the main dashboard page). - Action: Copy these values.
-
Update
.envFile: Paste your copied API Key and Secret into your.envfile:dotenv# .env VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE # ... other variables -
Get a Vonage Sender Number/ID:
- Paid Accounts: You can purchase virtual numbers. Navigate to
Numbers->Buy numbers. Search for and buy a number with SMS capability. Copy the full number (including country code, e.g.,12015550123). - Trial Accounts (IMPORTANT): Trial accounts often cannot send SMS messages to any number initially. You must:
- Register Test Numbers: Navigate to
Numbers->Test numbers. Add the phone number(s) you intend to send test SMS messages to. You will need to verify ownership via an SMS or voice call code sent by Vonage. - Use Your Vonage Number (if provided): Sometimes trial accounts come with a pre-assigned number. Check under
Numbers->Your numbers. - Use an Alphanumeric Sender ID (Limited): In some countries, you can use a custom string (like your brand name, e.g., "MyApp") as the sender ID instead of a number. This usually requires registration and may have limitations (e.g., recipients cannot reply). Check Vonage documentation for supported countries and regulations. For simplicity, using a purchased or test-verified number is recommended initially.
- Register Test Numbers: Navigate to
- Dashboard Path:
Vonage Dashboard->Numbers-> (Buy numbers|Your numbers|Test numbers). - Action: Obtain your Vonage virtual number or approved Sender ID.
- Paid Accounts: You can purchase virtual numbers. Navigate to
-
Update
.envFile: Add your Vonage sender number/ID to your.envfile:dotenv# .env VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID_HERE # e.g., 12015550123 or MyAppName PORT=3000
Environment Variables Summary:
VONAGE_API_KEY: Your unique key for authenticating API requests. Found on the Vonage dashboard.VONAGE_API_SECRET: Your secret key for authentication. Found on the Vonage dashboard. Treat this like a password.VONAGE_SENDER_NUMBER: The phone number (in E.164 format like15551234567) or registered Alphanumeric Sender ID that will appear as the sender of the SMS. Must be associated with your Vonage account.PORT: The port your Express application will listen on (e.g.,3000).
5. Error Handling and Logging
Our current implementation includes basic error handling and logging:
- Initialization Checks:
vonageClient.jsandsmsService.jscheck for essential environment variables on startup and exit if they are missing. - API Input Validation: The
/send-smsendpoint validates the presence and basic format oftoandtextfields, returning400 Bad Requestif invalid. - Vonage API Error Handling:
smsService.jschecks thestatuscode from the Vonage API response. If it's not'0', it throws an error including the Vonage error code and message. - Catch Block in API: The
try...catchblock in the/send-smsendpoint catches errors thrown bysmsService.js(including Vonage API errors) or other unexpected issues. It logs the error and returns a structured JSON error response with an appropriate HTTP status code (e.g., 500, 403, 401). - Logging: We use
console.logfor informational messages (server start, request received, SMS sent attempt, success) andconsole.errorfor errors (missing env vars, validation failures, SMS send failures, API errors).
Further Enhancements (Beyond Basic):
- Structured Logging: Implement a dedicated logging library (like
winstonorpino) for structured logging (e.g., JSON format), different log levels (debug, info, warn, error), and routing logs to files or external services. - Centralized Error Handling Middleware: Create Express middleware to handle errors consistently across all routes, reducing code duplication in individual route handlers.
- Retry Mechanisms: For transient network errors or specific Vonage rate limit responses, implement a retry strategy (e.g., exponential backoff) using libraries like
async-retry. This is more relevant for critical notifications.
6. Database Schema and Data Layer
Not Applicable for this Basic Guide.
This specific example focuses solely on sending an SMS via an API call and does not require a database to store state, user data, or message history.
For more complex applications (e.g., tracking message delivery status via webhooks, storing user preferences), you would typically integrate a database (like PostgreSQL, MongoDB) and use an ORM (like Prisma, Sequelize) or a database client library.
7. Security Features
While basic, our implementation includes some security considerations:
- Secrets Management: API keys and secrets are stored in environment variables (
.env) and excluded from version control (.gitignore). This is crucial. - Input Validation: The
/send-smsendpoint performs basic validation on thetoandtextinputs to prevent obviously malformed requests and potentially mitigate simple injection attempts (though more robust sanitization might be needed depending on howtextis used later). The phone number format check adds another layer.
Further Enhancements (Recommended for Production):
- Rate Limiting: Implement rate limiting on the
/send-smsendpoint using middleware likeexpress-rate-limitto prevent abuse and excessive costs. - Authentication/Authorization: Secure the API endpoint itself. If this API is not meant to be public, add authentication (e.g., API keys, JWT tokens) to ensure only authorized clients can trigger SMS sending.
- Input Sanitization: If the
textfield could ever contain user-generated content that is processed further, use libraries likeDOMPurify(if rendering as HTML) or appropriate validation/escaping based on the context to prevent Cross-Site Scripting (XSS) or other injection attacks. - Helmet Middleware: Use the
helmetmiddleware for Express to set various HTTP headers that improve security (e.g.,X-Content-Type-Options,Referrer-Policy,Strict-Transport-Security). - Dependency Scanning: Regularly scan project dependencies for known vulnerabilities using
npm auditor tools like Snyk.
8. Handling Special Cases
- Phone Number Formatting: The Vonage API generally expects phone numbers in E.164 format (e.g.,
+15551234567). While our basic validation checks for a plausible format, you might need more sophisticated parsing/validation using libraries likelibphonenumber-jsif dealing with varied user inputs. Our code currently recommends E.164 in the error message. - Character Limits & Encoding: Standard SMS messages have character limits (160 for GSM-7, 70 for UCS-2/Unicode). Longer messages are split (concatenated SMS). Vonage handles concatenation automatically, but be mindful of billing (you pay per segment). Unicode characters (like emojis) will reduce the limit per segment significantly. The SDK handles encoding.
- Sender ID Restrictions: Alphanumeric Sender IDs are not supported in all countries (e.g., the US often requires standard numbers) and may have registration requirements. Using a Vonage virtual number is generally more reliable globally.
- Delivery Status: This guide only covers sending. To confirm delivery, you need to configure Delivery Receipts (DLRs) by setting up a webhook endpoint in your Vonage application settings and processing the callbacks Vonage sends to it. This is beyond the scope of this basic guide.
9. Performance Optimizations
For this simple application, performance is largely dependent on the Vonage API's response time. However:
- SDK Client Initialization: We initialize the Vonage SDK client once when the application starts (
vonageClient.js) and reuse that instance. Creating a new client for every request would add unnecessary overhead. - Asynchronous Operations: The use of
async/awaitensures that the Node.js event loop is not blocked while waiting for the Vonage API response, allowing the server to handle other requests concurrently.
Further Enhancements (For Higher Scale):
- Caching: Not directly applicable for sending unique SMS, but if you frequently look up user data before sending, caching that data could improve performance.
- Load Balancing: For very high throughput, deploy multiple instances of the application behind a load balancer.
- Connection Pooling: While the SDK manages underlying HTTP connections, ensure your server has sufficient resources (CPU, memory, network) to handle the desired load.
10. Monitoring, Observability, and Analytics
Basic monitoring is achieved through console logging.
Further Enhancements:
- Health Check Endpoint: The
/route serves as a very basic health check. More robust checks could verify connectivity to Vonage (e.g., by making a low-cost API call like checking account balance). - Application Performance Monitoring (APM): Integrate APM tools (like Datadog, New Relic, Dynatrace) to get detailed insights into request latency, error rates, resource usage, and distributed tracing.
- Metrics: Track key metrics like the number of SMS sent successfully, number of failures, API latency, and error types. Expose these via a
/metricsendpoint for scraping by systems like Prometheus, or send them directly to a monitoring service. - Error Tracking Services: Use services like Sentry or Bugsnag to automatically capture, aggregate, and alert on application errors in real-time.
11. Troubleshooting and Caveats
Non-Whitelisted DestinationError (403 Forbidden):- Meaning: You are using a Vonage trial account and tried to send an SMS to a phone number that has not been added and verified in your "Test numbers" list.
- Solution: Go to your Vonage Dashboard ->
Numbers->Test numbers. Add the recipient's phone number and verify it using the code Vonage sends.
Invalid CredentialsError (401 Unauthorized):- Meaning: The
VONAGE_API_KEYorVONAGE_API_SECRETin your.envfile is incorrect or does not match the account associated with theVONAGE_SENDER_NUMBER. - Solution: Double-check your API Key and Secret in the Vonage Dashboard (
API Settings) and ensure they are correctly copied into your.envfile. Make sure there are no extra spaces or characters.
- Meaning: The
Invalid Sender Address/Illegal Sender AddressError:- Meaning: The
VONAGE_SENDER_NUMBERin your.envfile is not a valid number associated with your account, is not SMS-capable, or is an Alphanumeric Sender ID that is not permitted or registered for the destination country. - Solution: Verify the sender number in your Vonage Dashboard (
Numbers->Your numbers). Ensure it has SMS capabilities enabled. If using an Alphanumeric Sender ID, check regulations and registration status.
- Meaning: The
- Missing Environment Variables:
- Meaning: The application terminated on startup with an error message indicating a required
VONAGE_...variable was not found. - Solution: Ensure your
.envfile exists in the project root, is correctly formatted, and contains all required variables (VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_SENDER_NUMBER). Ensuredotenv/configis imported early invonageClient.jsandsmsService.js.
- Meaning: The application terminated on startup with an error message indicating a required
- Incorrect
Content-Typefor POST Request:- Meaning: Your API client (curl, Postman) sent the request without the
Content-Type: application/jsonheader, or sent data in the wrong format.express.json()middleware won't parse the body correctly. - Solution: Ensure your API client is sending a
POSTrequest with the headerContent-Type: application/jsonand the body is valid JSON (e.g.,{ "to": "+15551234567", "text": "Hello" }).
- Meaning: Your API client (curl, Postman) sent the request without the
- SMS Not Received:
- Check the API response/logs for success (
status: '0'). - Verify the recipient number (
to) is correct. - Check if the recipient device has signal/is not blocked.
- Check Vonage Dashboard (
Logs->Messaging) for detailed logs of the message attempt. - Consider potential carrier filtering on the recipient's end.
- Check the API response/logs for success (
12. Deployment and CI/CD
Deployment:
- Environment Variables: Deployment platforms (like Heroku, Vercel, AWS Elastic Beanstalk, Docker containers) have specific ways to set environment variables securely. Do not deploy your
.envfile. ConfigureVONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_SENDER_NUMBER, andPORTin the platform's settings. - Build Step: Not strictly required for this simple Node.js app unless using TypeScript or a bundler.
- Running the App: Ensure the platform runs your application using the start command defined in
package.json.package.jsonscript:"start": "node index.js"- Command to run:
npm start
Example (Conceptual Heroku Deployment):
# Login to Heroku CLI
heroku login
# Create Heroku app
heroku create your-app-name
# Set environment variables
heroku config:set VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_HERE
heroku config:set VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_HERE
heroku config:set VONAGE_SENDER_NUMBER=YOUR_VONAGE_NUMBER_OR_ID_HERE
# PORT is usually set automatically by Heroku
# Deploy using Git
git add .
git commit -m "Prepare for Heroku deployment"
git push heroku main # Or your default branchCI/CD (Conceptual):
A typical CI/CD pipeline (using GitHub Actions, GitLab CI, Jenkins) would:
- Trigger: On push to
mainbranch or creation of a Pull Request. - Checkout Code: Get the source code.
- Setup Node.js: Specify the Node.js version.
- Install Dependencies: Run
npm ci(preferred overinstallin CI for consistency). - Linting/Formatting: Run tools like ESLint or Prettier.
- Testing: Run unit and integration tests (see Section 13). Requires setting mock environment variables for tests.
- Build (if needed): Run build steps.
- Deploy: If tests pass and on the main branch, deploy to the target environment (e.g., using the platform's CLI like
heroku deploy).
13. Verification and Testing
Manual Verification:
-
Run the Application:
bashnpm startYou should see output like:
Vonage client initialized successfully. Server listening on http://localhost:3000 SMS Send Endpoint: POST http://localhost:3000/send-sms -
Send a Test Request (using
curl): Open a new terminal window. Replace+15551234567with a verified test number (if using a trial account) or a valid recipient number, and use your actualVONAGE_SENDER_NUMBER.bashcurl -X POST http://localhost:3000/send-sms \ -H "Content-Type: application/json" \ -d '{ "to": "+15551234567", "text": "Hello from Node.js and Vonage! Test at '"$(date)"'" }' -
Check Application Logs: Look at the terminal where
npm startis running. You should see logs indicating the request was received and the attempt to send the SMS.- Success:
Attempting to send SMS...,Message sent successfully... - Failure:
Attempting to send SMS...,Message failed with error...,Failed to send SMS via API endpoint...
- Success:
-
Check API Response: The
curlcommand should output a JSON response.- Success:
{ "success": true, "message": "SMS sent successfully.", "messageId": "..." } - Failure:
{ "success": false, "message": "Failed to send SMS: Vonage API Error..." }or{ "success": false, "message": "Missing required fields..." }
- Success:
-
Check Recipient Phone: The phone number specified in the
tofield should receive the SMS message shortly. -
Check Vonage Dashboard: Log in to the Vonage Dashboard and navigate to
Logs->Messaging. You should see a record of the attempted SMS, including its status (e.g.,delivered,failed,rejected).
Automated Testing (Conceptual):
- Unit Tests: Test individual functions in isolation.
- Test
smsService.js: Use a testing framework likejestormocha. Mock the@vonage/server-sdkto simulate successful and failed API responses fromvonage.sms.sendwithout actually calling the API. Verify thatsendSmshandles responses correctly and throws errors appropriately. - Test input validation logic separately.
- Test
- Integration Tests: Test the interaction between components.
- Use a library like
supertestto make HTTP requests to your running Express application (/send-smsendpoint). - Mock the
smsService.jsmodule to control its behavior during the test (e.g., make it simulate success or failure) and assert that the API endpoint returns the correct HTTP status code and JSON response based on the mocked service behavior. This avoids hitting the actual Vonage API during tests.
- Use a library like
Frequently Asked Questions (FAQ)
What is the correct phone number format for Vonage SMS API?
Vonage SMS API requires phone numbers in E.164 format without the leading '+' sign. Use format like 15551234567 (country code + number) instead of +15551234567. This differs from standard E.164 notation and is critical for successful API calls. The Vonage API sends exactly what you input, so formatting must be correct before sending.
How do I get Vonage API credentials?
Log in to your Vonage API Dashboard at dashboard.nexmo.com. Your API Key and API Secret are displayed prominently on the main dashboard page under "API Settings." Copy these values and store them securely in your .env file. Never commit these credentials to version control.
Can I send SMS with a Vonage trial account?
Yes, but with restrictions. Trial accounts require you to register and verify recipient phone numbers before sending. Navigate to your Vonage Dashboard → Numbers → Test numbers, add each recipient's phone number, and verify it using the code Vonage sends. Trial accounts typically cannot send to unverified numbers.
What Node.js version should I use for Vonage SMS integration?
Use Node.js v20 or later. Node.js v18 reached end-of-life on April 30, 2025, and no longer receives security updates. We recommend Node.js v22 LTS "Jod" for the longest support timeline (active support through April 2027). Express v5 requires Node.js v18 minimum but v20+ is recommended.
Why am I getting "Non-Whitelisted Destination" error?
This error occurs when using a trial account to send SMS to an unverified phone number. Solution: Add the recipient's phone number to your Test numbers list in the Vonage Dashboard (Numbers → Test numbers) and complete the verification process. Once verified, you can send SMS to that number.
How do I handle Vonage SMS API rate limits?
Vonage sets a default rate limit of 30 API requests per second per API key (up to 2,592,000 SMS per day). For US 10DLC traffic, additional throughput limits apply based on your brand vetting score and campaign type. Implement retry logic with exponential backoff for rate limit errors, and contact Vonage support for higher throughput if needed.
What's the difference between vonage.sms.send and vonage.messages.send?
vonage.sms.send() works with API Key/Secret authentication and is suitable for basic SMS sending (used in this tutorial). vonage.messages.send() requires Application ID and Private Key authentication and supports multiple channels (SMS, MMS, WhatsApp, Viber). For simple SMS-only use cases with Key/Secret auth, use vonage.sms.send().
How do I verify SMS delivery with Vonage?
Implement delivery receipts (DLRs) by configuring a webhook endpoint in your Vonage application settings. Vonage sends POST requests to your webhook URL with delivery status updates. You'll need to create an endpoint like /webhooks/vonage/delivery-receipt to receive and process these callbacks. Status codes include: delivered, failed, rejected, expired.
Can I use alphanumeric sender IDs with Vonage?
Yes, but with country-specific restrictions. Alphanumeric sender IDs (3-11 characters like "MyBrand") are not supported in all countries (e.g., US requires numeric long codes or short codes). Many countries require sender ID registration through Vonage's Global Sender ID Portal, with processing times of 7-14 business days. Check country-specific requirements before implementation.
How do I deploy a Vonage SMS application to production?
Configure environment variables (VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_SENDER_NUMBER) securely in your hosting platform (Heroku, AWS, Vercel, etc.). Never deploy your .env file. Implement production-ready features: rate limiting, authentication/authorization, structured logging, error tracking (Sentry), health check endpoints, and monitoring. Use npm ci in CI/CD pipelines for consistent dependency installation.
Frequently Asked Questions
How to send SMS with Node.js and Express?
Use the Vonage SMS API and the Express.js framework. Install the necessary dependencies (`npm install express @vonage/server-sdk dotenv`), set up an Express server, and create a route that handles sending messages via the Vonage API.
What is the Vonage SMS API?
The Vonage SMS API is a service that enables you to send and receive text messages programmatically. Use the `@vonage/server-sdk` package to interact with the API from your Node.js application. This API allows sending SMS globally.
Why use dotenv with Vonage API keys?
Dotenv helps manage environment variables, storing sensitive data like API keys outside your source code. Create a `.env` file to store your `VONAGE_API_KEY` and `VONAGE_API_SECRET`, preventing accidental exposure in version control.
How to set up a Vonage SMS API project?
Install the Vonage Server SDK (`@vonage/server-sdk`), Express.js, and dotenv. Set up environment variables for your Vonage API key and secret, as well as your Vonage phone number. Create an Express server with an endpoint to handle SMS sending.
What is Vonage sender number?
The Vonage sender number or ID is what recipients see as the message origin. It can be a virtual number you rent from Vonage, or an alphanumeric Sender ID (with limitations). This number or ID must be linked to your Vonage account.
When should I validate phone numbers for Vonage?
Always validate phone numbers upon receiving them in your API requests. The recommended format is E.164 (e.g., +15551234567). This ensures messages are sent to the correct recipients and minimizes errors from the Vonage API.
How to handle Vonage API errors?
Implement robust error handling by using try-catch blocks around Vonage API calls and checking the status code in the API response. The Vonage API returns a status code of '0' for success, other codes indicate errors. Handle errors gracefully with appropriate logging and responses to the client.
How to check SMS delivery status with Vonage?
Configure Delivery Receipts (DLRs) by setting up a webhook endpoint in your Vonage application settings. Vonage sends callbacks to this endpoint, providing real-time delivery status updates.
What is an alphanumeric sender ID in Vonage?
An alphanumeric sender ID is a custom text string (e.g., your brand name) that can be used as the sender instead of a phone number. This feature has limitations: not available in all countries and may prevent recipients from replying. Trial accounts may allow this option when numbers are restricted.
Can I use emojis in Vonage SMS messages?
Yes, but be aware that emojis and other Unicode characters consume more space in SMS messages. Standard messages have a limit of 160 characters (GSM-7 encoding) or 70 characters (UCS-2 encoding for Unicode). Longer messages are split, impacting cost (charged per segment).
How to test Vonage SMS integration locally?
Use a tool like `curl` or Postman to make POST requests to your local Express server's SMS endpoint. Provide test data, including the recipient number and message text, and verify the responses and logs. If using a trial account, ensure the recipient numbers are registered as test numbers in the Vonage Dashboard.
What are common troubleshooting tips for Vonage SMS?
Check for the following: `Non-Whitelisted Destination` errors when using trial accounts, `Invalid Credentials` errors due to incorrect API keys, `Invalid Sender Address` issues from problems with your Vonage number or ID, or general network problems.
How to implement rate limiting for Vonage SMS?
Use middleware like `express-rate-limit` to restrict the number of SMS messages sent from your API endpoint within a specific time window. This helps prevent abuse, reduces costs, and stays within Vonage API limits.