Send SMS with Node.js, Express, and Vonage
This guide provides a complete walkthrough for building a Node.js application using the Express framework to send SMS messages via the Vonage API. We will create a simple REST API endpoint that accepts a phone number and a message, then uses the Vonage Node.js SDK to dispatch the SMS.
This solution addresses the common need for applications to programmatically send SMS notifications, alerts, or verification codes. By the end of this guide, you will have a functional Express API capable of sending SMS messages, along with considerations for security, error handling, and testing.
Project Overview and Goals
- Goal: Build a simple REST API endpoint (
POST /send
) using Node.js and Express that sends an SMS message using the Vonage API. - Problem Solved: Provides a basic backend service for applications needing programmatic SMS sending capabilities.
- Technologies:
- Node.js: JavaScript runtime for building the server-side application.
- Express: Minimalist web framework for Node.js, used to create the API endpoint.
- Vonage Node.js SDK: Official library for interacting with Vonage APIs.
- dotenv: Module to load environment variables from a
.env
file intoprocess.env
.
- Outcome: A runnable Node.js Express application with a single endpoint that sends SMS messages via Vonage.
- Prerequisites:
- Node.js and npm (or yarn) installed.
- A Vonage API account (a free trial account is sufficient to start).
- A text editor or IDE (like VS Code).
- Optional: An API testing tool like Postman or
curl
.
System Architecture
The basic flow is straightforward:
- A client (e.g., Postman, frontend application, another backend service) sends a POST request to our Express API's
/send
endpoint with the recipient's phone number and the message text. - The Express application receives the request, validates the input (basic validation in this guide).
- The application uses the Vonage Node.js SDK, configured with your API credentials, to make an API call to Vonage.
- Vonage receives the request and sends the SMS message to the specified recipient number.
- Vonage returns a response to our Express application indicating success or failure.
- The Express application relays this success or failure status back to the original client.
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for your project. Navigate into it:
mkdir vonage-sms-sender cd vonage-sms-sender
-
Initialize Node.js Project: Initialize the project using npm, accepting the defaults. The
-y
flag skips the interactive prompts.npm init -y
-
Install Dependencies: Install Express, the Vonage Server SDK, and
dotenv
.npm install express @vonage/server-sdk dotenv
-
Enable ES Modules: To use modern
import
syntax, open thepackage.json
file created in step 2 and add the""type"": ""module""
line:{ ""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.14.0"", ""dotenv"": ""^16.4.5"", ""express"": ""^4.19.2"" } }
Note: The dependency versions shown (
^3.14.0
,^16.4.5
,^4.19.2
) are examples. Runnpm install express @vonage/server-sdk dotenv
to get the latest compatible versions. -
Create
.gitignore
: Create a file named.gitignore
in the project root to prevent committing sensitive information and unnecessary files (likenode_modules
).# .gitignore # Dependencies node_modules/ # Environment Variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Optional Build Folders dist/ build/ # Misc .DS_Store
-
Create Environment File (
.env
): Create a file named.env
in the project root. This file will store your Vonage API credentials and other configuration. Do not commit this file to version control.# .env # Vonage API Credentials VONAGE_API_KEY=YOUR_VONAGE_API_KEY VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET # Vonage Sender Number or ID (Use a purchased number or an Alphanumeric Sender ID) # Alphanumeric Sender IDs (e.g., ""MyApp"") may have restrictions. # A purchased Vonage number (e.g., 14155550100) is generally more reliable. VONAGE_SENDER_ID=YOUR_VONAGE_NUMBER_OR_SENDER_ID # Server Port PORT=3000
You will replace the placeholder values later in Section 4.
-
Create Project Files: Create the main application file and a library file for Vonage logic.
touch index.js lib.js
Your project structure should now look like this:
vonage-sms-sender/
├── .env
├── .gitignore
├── index.js
├── lib.js
├── node_modules/
├── package-lock.json
└── package.json
2. Implementing Core Functionality (SMS Sending Logic)
We'll encapsulate the Vonage interaction logic within the lib.js
file.
-
Edit
lib.js
: Openlib.js
and add the following code:// lib.js import { Vonage } from '@vonage/server-sdk'; import 'dotenv/config'; // Load environment variables from .env file // --- Configuration --- // Instantiate Vonage client using credentials from environment variables const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, }); // Get the sender ID or number from environment variables const sender = process.env.VONAGE_SENDER_ID; // --- Core SMS Sending Function --- /** * Sends an SMS message using the Vonage API. * @param {string} recipient - The recipient's phone number (E.164 format recommended, e.g., +14155550101). * @param {string} message - The text message content. * @returns {Promise<object>} A promise that resolves with the Vonage API response data on success. * @throws {Error} Throws an error if the API call fails or returns an error status. */ export const sendSms = async (recipient, message) => { console.log(`Attempting to send SMS from ${sender} to ${recipient}`); if (!sender) { throw new Error('VONAGE_SENDER_ID is not set in environment variables.'); } try { // Use the vonage.sms.send method const responseData = await vonage.sms.send({ to: recipient, from: sender, text: message }); // Check the status of the first message in the response // Vonage API returns an array of messages, typically one for a single SMS send. if (responseData.messages[0].status === '0') { console.log('Message sent successfully:', responseData.messages[0].messageId); return responseData; // Resolve with the full response data } else { // Log the specific error text from Vonage const errorText = responseData.messages[0]['error-text']; console.error(`Message failed with error: ${errorText}`); // Throw an error with the specific reason from Vonage throw new Error(`Failed to send SMS: ${errorText} (Status: ${responseData.messages[0].status})`); } } catch (err) { // Catch errors during the API call itself (network issues, SDK errors) // or errors thrown from the status check above. console.error('Error sending SMS via Vonage:', err); // Re-throw the error to be caught by the calling function (in index.js) // If it's not already an Error object, wrap it if (err instanceof Error) { throw err; } else { throw new Error(`Vonage API Error: ${err}`); } } };
- Explanation:
- We import
Vonage
from the SDK anddotenv/config
to load environment variables immediately. - We instantiate the
Vonage
client using theapiKey
andapiSecret
fromprocess.env
. - The
sendSms
function is anasync
function that takes therecipient
number andmessage
text. - It calls
vonage.sms.send()
, which returns a promise. - We check
responseData.messages[0].status
. A status of'0'
indicates success. Any other status indicates an error. - If successful, we log the message ID and return the response data.
- If unsuccessful, we log and throw a detailed error including the
error-text
provided by Vonage. - A
try...catch
block handles potential network errors or issues within the SDK call itself.
- We import
- Explanation:
3. Building the API Layer
Now, let's create the Express server and the /send
endpoint in index.js
.
-
Edit
index.js
: Openindex.js
and add the following code:// index.js import express from 'express'; import 'dotenv/config'; // Load environment variables import { sendSms } from './lib.js'; // Import our SMS sending function // --- Configuration --- const { json, urlencoded } = express; // Destructure middleware const app = express(); const port = process.env.PORT || 3000; // Use port from .env or default to 3000 // --- Middleware --- // Enable parsing of JSON request bodies app.use(json()); // Enable parsing of URL-encoded request bodies (optional, but good practice) app.use(urlencoded({ extended: true })); // --- API Routes --- /** * @route POST /send * @description Endpoint to send an SMS message. * @access Public (For production, add authentication) * @requestBody { ""phone"": ""string"", ""message"": ""string"" } * @responseSuccess { ""success"": true, ""data"": { ...Vonage Response... } } * @responseError { ""success"": false, ""message"": ""Error description"" } */ app.post('/send', async (req, res) => { // Basic Input Validation const { phone, message } = req.body; if (!phone || !message || typeof phone !== 'string' || typeof message !== 'string') { console.error('Invalid request body:', req.body); return res.status(400).json({ success: false, message: 'Invalid input. Please provide ""phone"" (string) and ""message"" (string) in the request body.', }); } // Basic phone format check (very lenient, allows optional '+') // Consider a dedicated library (e.g., google-libphonenumber) for robust E.164 validation in production. if (!/^\+?[1-9]\d{1,14}$/.test(phone)) { console.warn(`Potentially invalid phone format received: ${phone}. Sending anyway, Vonage will perform final validation.`); } try { // Call the sendSms function from lib.js const result = await sendSms(phone, message); console.log('SMS Send API call successful.'); // Send success response back to the client res.status(200).json({ success: true, data: result, // Include Vonage response details }); } catch (error) { // Catch errors from sendSms (API errors, validation errors in lib.js) console.error('Failed to process /send request:', error.message); // Send error response back to the client // Determine status code based on error if possible, default to 500 const statusCode = error.message.includes('Non-Whitelisted Destination') ? 403 : error.message.includes('Invalid input') ? 400 : 500; // Basic example, refine as needed res.status(statusCode).json({ success: false, message: error.message || 'An unexpected error occurred while sending the SMS.', }); } }); // --- Basic Health Check Route --- /** * @route GET /health * @description Simple health check endpoint. * @access Public * @response { ""status"": ""ok"", ""timestamp"": ""ISO Date String"" } */ app.get('/health', (req, res) => { res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() }); }); // --- Start Server --- app.listen(port, () => { console.log(`Server listening for SMS requests at http://localhost:${port}`); });
- Explanation:
- Imports
express
,dotenv/config
, and oursendSms
function. - Initializes the Express app and defines the port.
- Uses
express.json()
andexpress.urlencoded()
middleware to parse request bodies. - Defines the
POST /send
route:- It's an
async
function to handle the promise fromsendSms
. - It performs basic validation to ensure
phone
andmessage
are present and are strings. A more robust validation library (like Joi or express-validator) is recommended for production. - Includes a very basic, lenient check for phone number format with a warning. Vonage performs its own validation.
- Calls
sendSms
within atry...catch
block. - On success, returns a 200 status with
{ success: true, data: result }
. - On failure, catches the error thrown by
sendSms
, logs it, and returns an appropriate status code (e.g., 400 for bad input, 403 for whitelisting issues, 500 for other server/API errors) with{ success: false, message: error.message }
.
- It's an
- Includes a simple
GET /health
endpoint for monitoring. - Starts the server using
app.listen
.
- Imports
- Explanation:
4. Integrating with Vonage (Credentials and Setup)
Before running the application, you need to configure it with your Vonage account details.
-
Sign Up/Log In to Vonage: Go to the Vonage API Dashboard and sign up for a free trial account or log in if you already have one.
-
Find API Key and Secret: On your Vonage Dashboard homepage, your API Key and API Secret are usually displayed near the top.
- Navigate to API Settings in the left-hand menu if you can't find them immediately.
- Copy the
API key
andAPI secret
.
-
Get a Vonage Number or Set Sender ID:
- Option A (Recommended): Purchase a Number:
- Navigate to Numbers > Buy numbers in the dashboard.
- Search for and purchase a number with SMS capabilities in your desired country. This number will be used as the
from
number when sending SMS. - Copy the purchased number (in E.164 format, e.g.,
14155550100
).
- Option B: Use Alphanumeric Sender ID:
- You can use a custom string (e.g.,
""MyApp""
,""Alerts""
) as the sender ID. However, support for alphanumeric sender IDs varies by country and carrier, and may require pre-registration. Check Vonage documentation for specific country regulations. If using this, copy the desired ID string.
- You can use a custom string (e.g.,
- Option C (Trial Account Default): The default sender ID for trial accounts might be
""Vonage""
. This usually works but provides less branding.
- Option A (Recommended): Purchase a Number:
-
Whitelist Test Numbers (CRITICAL FOR TRIAL ACCOUNTS):
- If you are using a free trial account, Vonage requires you to whitelist the phone numbers you intend to send SMS to. You cannot send messages to unverified numbers until you upgrade your account.
- Navigate to Numbers > Test numbers in the Vonage Dashboard.
- Click Add test number.
- Enter the recipient phone number you want to test with (your own mobile number is a good choice).
- Vonage will send a verification code via SMS or voice call to that number. Enter the code to confirm ownership.
- Repeat for any other numbers you need to test with during the trial period. Failure to do this will result in the
""Non-Whitelisted Destination""
error.
-
Update
.env
File: Open your.env
file and replace the placeholder values with your actual credentials:# .env VONAGE_API_KEY=YOUR_ACTUAL_API_KEY_FROM_DASHBOARD VONAGE_API_SECRET=YOUR_ACTUAL_API_SECRET_FROM_DASHBOARD VONAGE_SENDER_ID=YOUR_PURCHASED_VONAGE_NUMBER_OR_SENDER_ID PORT=3000
VONAGE_API_KEY
: Your Vonage API Key.VONAGE_API_SECRET
: Your Vonage API Secret.VONAGE_SENDER_ID
: Your purchased Vonage number (e.g.,14155550100
) or your chosen alphanumeric sender ID (e.g.,MyCompany
).PORT
: The port your Express server will run on (3000 is common for development).
5. Implementing Error Handling and Logging
We've already incorporated basic error handling and logging:
lib.js
:- Checks the Vonage API response status (
responseData.messages[0].status
). - Throws specific errors using
error-text
from Vonage for failed sends. - Uses
console.log
for successful sends andconsole.error
for failures or API issues. - Includes a check for the
VONAGE_SENDER_ID
environment variable.
- Checks the Vonage API response status (
index.js
:- Uses a
try...catch
block around thesendSms
call in the/send
endpoint. - Logs errors using
console.error
. - Returns structured JSON error responses to the client (
{ success: false, message: ... }
). - Includes basic input validation (checking for
phone
andmessage
). - Attempts to set appropriate HTTP status codes (400, 403, 500).
- Uses a
Further Improvements (Beyond Basic):
- Structured Logging: For production, use a dedicated logging library like
pino
orwinston
to output logs in JSON format. This makes them easier to parse by log aggregation tools (e.g., Datadog, Splunk, ELK stack).You would then integratenpm install pino pino-http
pino-http
as Express middleware. - Centralized Error Handling Middleware: Create custom Express error handling middleware to standardize error responses and logging across all routes.
- Retry Mechanisms: Sending SMS can occasionally fail due to transient network issues or temporary carrier problems. Implement a retry strategy with exponential backoff for specific error codes (e.g., timeouts, potentially certain Vonage error codes indicating temporary issues). Libraries like
async-retry
can help. This is often best implemented client-side when calling this API, but could be added server-side for resilience.
6. Creating a Database Schema and Data Layer
Not Applicable for this specific guide.
This application is designed to be stateless. It receives a request, calls the Vonage API, and returns a response. It does not need to store any information about the messages sent (like status, recipient, content) in a database.
If you needed to track message history, delivery status (via Vonage webhooks), or manage contacts, you would introduce a database (e.g., PostgreSQL, MongoDB) and a data access layer (using an ORM like Prisma or Sequelize, or native drivers).
7. Adding Security Features
Security is crucial, even for simple APIs.
-
Input Validation:
- We added basic checks in
index.js
for the presence and type ofphone
andmessage
. - Recommendation: Use a dedicated validation library like
joi
orexpress-validator
for more robust schema validation, format checking (especially for phone numbers using libraries likegoogle-libphonenumber
), and length limits.
npm install express-validator
Example with
express-validator
:// index.js (modified example) import express from 'express'; import 'dotenv/config'; import { sendSms } from './lib.js'; import { body, validationResult } from 'express-validator'; // Import validator const { json, urlencoded } = express; const app = express(); const port = process.env.PORT || 3000; app.use(json()); app.use(urlencoded({ extended: true })); // Define validation rules as an array const sendSmsValidationRules = [ body('phone') .isString().withMessage('Phone must be a string.') .notEmpty().withMessage('Phone number is required.') // Add more specific validation if needed, e.g., using a custom validator or isMobilePhone // .isMobilePhone('any', { strictMode: false }).withMessage('Invalid phone number format.') .trim(), // Sanitize input body('message') .isString().withMessage('Message must be a string.') .notEmpty().withMessage('Message is required.') .isLength({ min: 1, max: 1600 }).withMessage('Message cannot exceed 1600 characters.') .trim() // Sanitize input ]; // Define the route handler separately const handleSendSms = async (req, res) => { // Check for validation errors const errors = validationResult(req); if (!errors.isEmpty()) { console.error('Validation errors:', errors.array()); return res.status(400).json({ success: false, errors: errors.array() }); } // Proceed if validation passes const { phone, message } = req.body; // Basic phone format check (optional, as validator can handle it) if (!/^\+?[1-9]\d{1,14}$/.test(phone)) { console.warn(`Potentially invalid phone format received: ${phone}. Sending anyway, Vonage will perform final validation.`); } try { const result = await sendSms(phone, message); console.log('SMS Send API call successful.'); res.status(200).json({ success: true, data: result }); } catch (error) { console.error('Failed to process /send request:', error.message); const statusCode = error.message.includes('Non-Whitelisted Destination') ? 403 : error.message.includes('Invalid input') ? 400 : 500; res.status(statusCode).json({ success: false, message: error.message || 'An unexpected error occurred while sending the SMS.', }); } }; // Apply validation rules middleware before the route handler app.post('/send', sendSmsValidationRules, handleSendSms); // Health check route app.get('/health', (req, res) => { res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Start server app.listen(port, () => { console.log(`Server listening for SMS requests at http://localhost:${port}`); });
- We added basic checks in
-
Rate Limiting:
- Protect your API from abuse and brute-force attacks by limiting the number of requests a client can make in a given time window.
- Recommendation: Use
express-rate-limit
.
npm install express-rate-limit
Example Implementation:
// index.js (near the top, after express() instantiation) import rateLimit from 'express-rate-limit'; // ... (other imports) ... const app = express(); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes) standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers message: { success: false, message: 'Too many requests from this IP, please try again after 15 minutes' } }); // Apply the rate limiting middleware to all requests or specific routes app.use(limiter); // Apply to all routes // Or apply only to the send route: app.use('/send', limiter); // ... (rest of the app setup: middleware, routes, listen) ...
-
API Key Security:
- Never commit your
.env
file or hardcode API keys/secrets directly in your code. Use environment variables as shown. Ensure.env
is listed in your.gitignore
. - In production environments, use your deployment platform's mechanism for securely managing environment variables (e.g., AWS Secrets Manager, Azure Key Vault, Heroku Config Vars, Docker secrets).
- Never commit your
-
Authentication/Authorization:
- The current
/send
endpoint is public. In a real-world scenario, you would protect it. - Recommendation: Implement API key authentication, JWT (JSON Web Tokens), or OAuth2 depending on your use case. The client calling
/send
would need to provide a valid token or key in the request headers (e.g.,Authorization: Bearer <token>
orX-API-Key: <key>
). You would add middleware to verify this before allowing access to the/send
logic.
- The current
-
Helmet:
- Use the
helmet
middleware to set various HTTP headers that help protect your app from common web vulnerabilities (like XSS, clickjacking).
npm install helmet
// index.js (near the top) import helmet from 'helmet'; // ... (other imports) ... const app = express(); app.use(helmet()); // ... (rest of the app setup) ...
- Use the
8. Handling Special Cases
- Phone Number Formatting: Vonage generally prefers the E.164 format (e.g.,
+14155550101
). While it might handle other formats, standardizing on E.164 using a library likegoogle-libphonenumber
during input validation is best practice to avoid ambiguity and potential delivery issues. - Message Encoding & Length: Standard SMS messages have character limits (160 for GSM-7 encoding, 70 for UCS-2/Unicode). Longer messages are split into multiple segments (concatenated SMS), which might incur higher costs. Be mindful of message length, especially if constructing messages dynamically. The Vonage API handles concatenation automatically, but the
max: 1600
validation example provides a basic safeguard. - Sender ID Restrictions: As mentioned, alphanumeric sender IDs are not universally supported and might be overwritten by carriers or require registration. Using a purchased Vonage virtual number is generally more reliable for consistent delivery and branding.
- International Sending: Ensure your Vonage account is enabled for sending to the specific countries you target. Pricing and regulations vary significantly by country.
9. Implementing Performance Optimizations
For this simple API, performance bottlenecks are unlikely within the Node.js application itself. The main latency will come from the external network call to the Vonage API.
- Connection Pooling: The
@vonage/server-sdk
likely handles underlying HTTP connection management efficiently. - Asynchronous Operations: Using
async/await
ensures Node.js's event loop is not blocked during the API call to Vonage, allowing the server to handle other requests concurrently. - Caching: Caching is not applicable here, as each request should trigger a unique SMS send.
- Load Testing: Use tools like
k6
,artillery
, orApacheBench
(ab
) to simulate traffic and identify potential bottlenecks under load. Monitor CPU, memory usage, and response times. Pay attention to Vonage API rate limits you might hit under heavy load. - Profiling: Use Node.js built-in profiler or tools like Clinic.js to analyze CPU usage and identify performance hotspots if needed, though unlikely for this simple application.
10. Adding Monitoring, Observability, and Analytics
- Health Checks: The
/health
endpoint provides a basic way for load balancers or monitoring systems (like UptimeRobot, Pingdom) to check if the service is running. - Performance Metrics: Instrument your application to collect key metrics:
- Request rate (requests per second/minute).
- Request latency (how long the
/send
endpoint takes to respond). - Error rate (percentage of 5xx or 4xx errors).
- Node.js process metrics (CPU usage, memory usage, event loop lag).
- Tools: Prometheus with
prom-client
, Datadog APM, New Relic.
- Logging: As mentioned in Section 5, structured logging (JSON) is essential. Ship logs to a centralized platform (Datadog Logs, AWS CloudWatch Logs, ELK Stack) for analysis and alerting.
- Error Tracking: Integrate an error tracking service (Sentry, Bugsnag, Datadog Error Tracking) to capture, aggregate, and alert on exceptions that occur in your application, providing stack traces and context.
- Vonage Dashboard: Monitor your SMS usage, delivery rates, and spending directly within the Vonage API Dashboard. Configure balance alerts.
Example Alert Configuration (Conceptual):
- High Error Rate: Alert if the percentage of 5xx responses from
/send
exceeds 5% over a 5-minute period. - High Latency: Alert if the p95 response time for
/send
exceeds 1 second over a 10-minute period. - Low Vonage Balance: Configure alerts in the Vonage dashboard if your account balance drops below a certain threshold.
- Application Down: Alert if the
/health
endpoint fails for 3 consecutive checks from your monitoring service.
11. Troubleshooting and Caveats
Error: Non-Whitelisted Destination
(Status Code 15):- Meaning: You are using a Vonage trial account and tried to send an SMS to a phone number not added to your verified ""Test numbers"" list.
- Solution: Go to your Vonage Dashboard -> Numbers -> Test numbers. Add the recipient number and verify it using the code sent to that phone.
Error: Authentication failed
/Error: Invalid Credentials
(Status Code 4/9):- Meaning: The
VONAGE_API_KEY
orVONAGE_API_SECRET
in your.env
file is incorrect or doesn't match the ones in your Vonage Dashboard. - Solution: Double-check the API Key and Secret in your
.env
file against the values shown in the Vonage Dashboard -> API settings. Ensure there are no typos or extra spaces. Regenerate secrets if necessary.
- Meaning: The
Error: Invalid Sender Address (from)
(Status Code 1):- Meaning: The
VONAGE_SENDER_ID
in your.env
file is not a valid Vonage number associated with your account or is an invalid/unsupported alphanumeric sender ID format for the destination country. - Solution: Verify the
VONAGE_SENDER_ID
. Ensure it's a number you've purchased in your Vonage account or a correctly formatted/registered alphanumeric ID. Try using a purchased number instead of an alphanumeric ID if issues persist.
- Meaning: The
Error: Insufficient funds
(Status Code 2):- Meaning: Your Vonage account balance is too low to cover the cost of the SMS.
- Solution: Add credit to your Vonage account via the Dashboard.
- Network Errors (
ECONNREFUSED
,ETIMEDOUT
):- Meaning: Your server cannot establish a connection to the Vonage API endpoints.
- Solution: Check your server's internet connectivity. Ensure firewalls are not blocking outbound connections to
rest.nexmo.com
orapi.nexmo.com
on port 443 (HTTPS).
- Environment Variables Not Loaded:
- Meaning:
process.env.VONAGE_API_KEY
(or others) isundefined
. - Solution: Ensure the
dotenv
package is installed andimport 'dotenv/config';
is called at the very top oflib.js
andindex.js
(or your main entry point). Verify the.env
file exists in the project root where you runnode index.js
and is correctly formatted.
- Meaning:
TypeError: Cannot read property 'messages' of undefined
:- Meaning: The call to
vonage.sms.send
likely failed at a network level or returned an unexpected response structure before themessages
array could be accessed. - Solution: Check the preceding
console.error
logs for network issues or authentication problems caught by the outercatch
block inlib.js
.
- Meaning: The call to
- SMS Not Received (but API shows success
status: '0'
):- Check Recipient Number: Double-check the
phone
number sent in the request is correct (ideally E.164 format). - Carrier Filtering: Mobile carriers sometimes filter messages they perceive as spam. Ensure your message content is appropriate and consider using a purchased number instead of generic sender IDs.
- Vonage Delivery Receipts (DLRs): For detailed delivery status, implement Vonage webhooks to receive DLRs. This is beyond the scope of this basic guide but provides insight into whether the message reached the handset. Check Vonage documentation on setting up SMS DLR webhooks.
- Check Vonage Dashboard Logs: Examine the message logs in your Vonage Dashboard for more details on the specific message status and potential delivery issues reported by carriers.
- Check Recipient Number: Double-check the