code examples
code examples
Send SMS with Node.js, Express, and Vonage
A guide on building a Node.js/Express application to send SMS using the Vonage Messages API, covering setup, implementation, configuration, and best practices.
This guide provides a complete walkthrough for building a Node.js application using the Express framework to send SMS messages via the Vonage Messages API. We will cover everything from project setup and configuration to implementation, error handling, security considerations, and deployment.
By the end of this tutorial, you will have a functional Express API endpoint capable of accepting a phone number and message, and using Vonage to deliver that message as an SMS. This serves as a foundational building block for applications requiring SMS notifications, alerts, or communication features.
Project Overview and Goals
Goal: To create a simple, robust Node.js REST API endpoint that sends SMS messages using the Vonage Messages API.
Problem Solved: Enables developers to programmatically send SMS messages from their applications, facilitating communication features like notifications, two-factor authentication codes (though Vonage Verify API is often better suited), or marketing messages.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications. Chosen for its asynchronous nature, large ecosystem (npm), and suitability for I/O-bound tasks like API interactions.
- Express: A minimal and flexible Node.js web application framework. Chosen for its simplicity in setting up routes and handling HTTP requests, making it ideal for building APIs quickly.
- Vonage Messages API: A powerful API from Vonage that allows sending messages across various channels (SMS, MMS, WhatsApp, etc.). We use it specifically for SMS in this guide. Chosen for its reliability and developer-friendly SDK.
- Vonage Node.js Server SDK: A library provided by Vonage to simplify interaction with their APIs from Node.js applications.
- dotenv: A module to load environment variables from a
.envfile intoprocess.env, keeping sensitive credentials out of source code.
System Architecture:
+-------------+ +-----------------------+ +-----------------+ +--------------+
| Client | ----> | Node.js/Express API | ----> | Vonage Messages | ----> | Target Phone |
| (e.g. curl,| | (Your Server) | | API | | (SMS) |
| Postman) | | - /send endpoint | +-----------------+ +--------------+
+-------------+ | - Vonage SDK init |
| - Sends SMS request |
+-----------------------+
| ^
| reads | uses
V |
+-----------------------+
| .env / private.key |
| (Credentials/Config) |
+-----------------------+Prerequisites:
- Node.js and npm (or yarn): Installed on your system. You can download them from nodejs.org.
- Vonage API Account: Sign up for free at Vonage API Dashboard. You'll get some free credit to start.
- Vonage Application: You need to create a Vonage Application within your dashboard.
- Private Key: Generated when creating the Vonage Application.
- Vonage Virtual Number: A phone number purchased or claimed within your Vonage account, linked to your Application. Alternatively, for trial accounts, you'll need to whitelist recipient numbers (see Section 4).
- Basic understanding of JavaScript and Node.js.
- A text editor or IDE (e.g., VS Code).
- A tool to make HTTP requests (e.g.,
curl, Postman, Insomnia).
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, then navigate into it.
bashmkdir vonage-sms-sender cd vonage-sms-sender -
Initialize Node.js Project: This creates a
package.jsonfile to manage your project's dependencies and scripts.bashnpm init -y -
Install Dependencies: We need Express for the web server, the Vonage SDK to interact with the API, and dotenv to handle environment variables.
bashnpm install express @vonage/server-sdk dotenvexpress: Web framework.@vonage/server-sdk: Official Vonage SDK for Node.js.dotenv: Loads environment variables from.env.
-
Create Project Files: Create the main application file and a file for environment variables.
bashtouch index.js .env .gitignoreindex.js: Our main application code..env: Stores sensitive credentials and configuration (API keys, numbers, etc.). Never commit this file to version control..gitignore: Specifies files that Git should ignore.
-
Configure
.gitignore: Addnode_modulesand.envto your.gitignorefile to prevent committing dependencies and secrets.text# .gitignore node_modules/ .env private.key # Also ignore your private key file npm-debug.log* yarn-debug.log* yarn-error.log* -
Project Structure: Your basic project structure should now look like this:
textvonage-sms-sender/ ├── .env ├── .gitignore ├── index.js ├── node_modules/ └── package.json -
Add Start Script (Optional but Recommended): Open
package.jsonand add astartscript for easily running your application.json// package.json (partial) { ""name"": ""vonage-sms-sender"", ""version"": ""1.0.0"", ""description"": ""Node.js Express app to send SMS via Vonage"", ""main"": ""index.js"", ""scripts"": { ""start"": ""node index.js"", ""test"": ""echo \""Error: no test specified\"" && exit 1"" }, ""keywords"": [""vonage"", ""sms"", ""node"", ""express""], ""author"": """", ""license"": ""ISC"", ""dependencies"": { ""@vonage/server-sdk"": ""^3.x.x"", ""dotenv"": ""^16.x.x"", ""express"": ""^4.x.x"" } }You can now run the app using
npm start.
2. Implementing Core Functionality (Vonage Client)
Before building the API endpoint, let's set up the core logic for interacting with the Vonage SDK.
-
Initialize Vonage Client: In
index.js, we'll require the necessary modules and initialize the Vonage client using credentials that we'll load from environment variables shortly.javascript// index.js require('dotenv').config(); // Load environment variables from .env file const { Vonage } = require('@vonage/server-sdk'); const express = require('express'); const app = express(); const port = process.env.PORT || 3000; // Use port from env or default to 3000 // --- Vonage Client Initialization --- // Ensure required environment variables are loaded if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_PRIVATE_KEY_PATH || !process.env.VONAGE_NUMBER) { console.error('_ Error: Missing required Vonage environment variables (VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH, VONAGE_NUMBER).'); console.error('Please check your .env file.'); process.exit(1); // Exit if configuration is missing } let vonage; try { vonage = new Vonage({ applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: process.env.VONAGE_PRIVATE_KEY_PATH // Path to your private key file }); console.log('__ Vonage client initialized successfully.'); } catch (error) { console.error('_ Error initializing Vonage client:', error.message); // Common issue: Ensure the private key file exists at the specified path and is readable. if (error.code === 'ENOENT') { // File not found error console.error(`_ Private key file not found at path: ${process.env.VONAGE_PRIVATE_KEY_PATH}`); } process.exit(1); } // --- Middleware --- app.use(express.json()); // To parse JSON request bodies app.use(express.urlencoded({ extended: true })); // To parse URL-encoded request bodies // --- Routes (To be added in next step) --- app.get('/', (req, res) => { res.send('Vonage SMS Sender API is running!'); }); // --- Server Start --- app.listen(port, () => { console.log(`__ Server listening at http://localhost:${port}`); });require('dotenv').config();: Loads variables from.envintoprocess.env. Must be called early.new Vonage(...): Creates an instance of the Vonage client.applicationId: Your Vonage Application ID (from.env).privateKey: The path to theprivate.keyfile you downloaded when creating the Vonage Application (from.env).
- Error Handling: We add checks to ensure environment variables are present and catch potential errors during client initialization, including checking if the private key file exists.
-
Explanation of Choices:
- We initialize the Vonage client outside the request handler. This is crucial for performance, as it avoids recreating the client on every incoming request.
- Using environment variables via
dotenvis standard practice for managing sensitive credentials securely. - We use the Application ID and Private Key method for authentication, as recommended for server-to-server interactions with the Messages API (which we'll use for sending).
3. Building the API Layer (/send Endpoint)
Now, let's create the Express route that will receive requests and trigger the SMS sending.
-
Create the
/sendRoute: Add the following POST route handler inindex.jsafter the middleware and beforeapp.listen.javascript// index.js (continued) // --- Routes --- app.get('/', (req, res) => { res.send('Vonage SMS Sender API is running!'); }); // POST route to send an SMS app.post('/send', async (req, res) => { console.log('Received /send request:', req.body); // --- Basic Input Validation --- const { to, text } = req.body; if (!to || !text) { console.error('Validation Error: Missing ""to"" or ""text"" in request body.'); return res.status(400).json({ success: false, error: 'Missing required fields: ""to"" (recipient phone number) and ""text"" (message content).' }); } // Consider adding more robust validation (e.g., phone number format check using a library) // E.164 format is recommended for phone numbers (e.g., +14155552671) const fromNumber = process.env.VONAGE_NUMBER; try { console.log(`Attempting to send SMS from ${fromNumber} to ${to}`); // --- Call Vonage Messages API --- const resp = await vonage.messages.send({ message_type: ""text"", to: to, // Recipient phone number from request body from: fromNumber, // Your Vonage virtual number from .env channel: ""sms"", text: text // Message content from request body }); console.log('__ SMS sent successfully! Message UUID:', resp.message_uuid); res.status(200).json({ success: true, message_uuid: resp.message_uuid }); } catch (err) { console.error('_ Error sending SMS via Vonage:', err); // Provide more context if available let errorMessage = 'Failed to send SMS.'; let statusCode = 500; // Default to Internal Server Error if (err.response && err.response.data) { console.error('Vonage API Error Details:', JSON.stringify(err.response.data, null, 2)); errorMessage = err.response.data.title || err.response.data.detail || errorMessage; statusCode = err.response.status || 500; // Use status code from Vonage if available // Check for common non-whitelisted error (specific to trial accounts) if (err.response.data.title === 'Invalid parameters' && err.response.data.invalid_parameters?.some(p => p.name === 'to')) { errorMessage += ' Potential issue: Ensure the recipient number is whitelisted in your Vonage trial account, if applicable.'; statusCode = 400; // Treat as bad request if the number is invalid/non-whitelisted } else if (statusCode === 401) { // Authentication error errorMessage = 'Authentication failed with Vonage. Check Application ID and Private Key.'; } } else if (err.message) { errorMessage = err.message; } res.status(statusCode).json({ success: false, error: errorMessage, details: err.response?.data || err.message }); } }); // --- Server Start --- // (app.listen code remains the same) -
Code Explanation:
app.post('/send', ...): Defines a handler for POST requests to the/sendpath.async (req, res): Marks the handler as asynchronous, allowing us to useawaitfor the Vonage API call.- Input Validation: Checks if
to(recipient number) andtext(message body) are present in the JSON request body (req.body). Returns a 400 Bad Request error if not. Note: Production apps should have more robust validation (e.g., checking phone number format). vonage.messages.send({...}): This is the core Vonage SDK method call.message_type: ""text"": Specifies a plain text message.to: The recipient's phone number.from: Your Vonage virtual number (loaded from.env).channel: ""sms"": Explicitly tells the Messages API to use the SMS channel.text: The content of the SMS message.
- Success Response: If the API call is successful (
awaitdoesn't throw an error), it returns a 200 OK status with themessage_uuidprovided by Vonage. - Error Handling: The
try...catchblock handles errors during the API call. It logs the error and returns an appropriate HTTP status code (often 500, but potentially 400 or 401 based on Vonage's response) with an error message. We attempt to parse specific error details from the Vonage response if available and provide hints for common issues like whitelisting.
-
API Endpoint Documentation:
- Endpoint:
POST /send - Description: Sends an SMS message to the specified recipient.
- Request Body (JSON):
json
{ ""to"": ""+14155550100"", ""text"": ""Hello from your Node.js app!"" } - Success Response (200 OK):
json
{ ""success"": true, ""message_uuid"": ""aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"" } - Error Response (400 Bad Request - Missing Fields):
json
{ ""success"": false, ""error"": ""Missing required fields: \""to\"" (recipient phone number) and \""text\"" (message content)."" } - Error Response (e.g., 400 Bad Request - Non-Whitelisted Number on Trial Account):
json
{ ""success"": false, ""error"": ""Invalid parameters. Potential issue: Ensure the recipient number is whitelisted in your Vonage trial account, if applicable."", ""details"": { /* Detailed error object from Vonage SDK */ } } - Error Response (500 Internal Server Error - General Vonage API Failure):
json
{ ""success"": false, ""error"": ""Failed to send SMS."", ""details"": { /* Optional: Detailed error object from Vonage SDK */ } }
- Endpoint:
-
Testing with
curl: Replace placeholders with your actual recipient number and message. Important: If using a Vonage trial account, ensure the recipient number has been whitelisted in your Vonage dashboard (see Section 4, Step 5). Ensure your server is running (npm start).bashcurl -X POST http://localhost:3000/send \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+1YOUR_RECIPIENT_NUMBER"", ""text"": ""Testing Vonage SMS from curl!"" }'
4. Integrating with Vonage (Configuration)
Proper configuration is key. Let's set up the Vonage account and environment variables.
-
Sign Up/Log In: Go to the Vonage API Dashboard.
-
Get API Key and Secret (For Reference): Your main Account API Key and Secret are visible at the top of the dashboard. While we're using Application ID/Private Key for the Messages API, knowing where these are is useful.
-
Create a Vonage Application:
- Navigate to ""Applications"" in the left-hand menu.
- Click ""Create a new application"".
- Give it a name (e.g., ""Node SMS Sender App"").
- Click ""Generate public and private key"". Crucially, save the
private.keyfile that downloads. Store this file securely within your project directory (e.g., at the root level). Remember we addedprivate.keyto.gitignore. - Enable the ""Messages"" capability.
- For ""Inbound URL"" and ""Status URL"", you can enter placeholders like
https://example.com/webhooks/inboundandhttps://example.com/webhooks/statusfor now, as we are only sending SMS in this guide. If you planned to receive messages or delivery receipts, you'd need valid, publicly accessible URLs here (often usingngrokduring development). - Click ""Generate new application"".
- You will now see your Application ID. Copy this value.
-
Link a Vonage Number:
- Go to ""Numbers"" > ""Your numbers"".
- If you don't have a number, go to ""Buy numbers"" and find one with SMS capability in your desired country.
- Once you have a number, click the ""Manage"" (or gear icon) next to it.
- In the ""Application"" dropdown under ""Forwarding"", select the Application you just created (""Node SMS Sender App"").
- Click ""Save"". Copy your Vonage virtual number (in E.164 format, e.g.,
+12015550123).
-
Whitelist Test Numbers (Trial Accounts Only):
- This is a critical step for trial accounts. If you are using a free trial, you can only send SMS to numbers you have explicitly verified.
- Go to the Vonage Dashboard home page.
- Scroll down to the ""Test Numbers"" or ""Sandbox"" section.
- Add the phone number(s) you intend to send test messages to. You will need to verify ownership via a code sent to that number. Failure to do this will result in errors when trying to send messages.
-
Configure API Settings (IMPORTANT):
- Go to ""Account Settings"" in the dashboard.
- Scroll down to ""API Settings"".
- Under ""Default SMS Setting"", ensure ""Messages API"" is selected. This is critical for the
@vonage/server-sdk'smessages.sendmethod to work correctly for SMS. - Click ""Save changes"".
-
Populate
.envFile: Open the.envfile in your project and add the credentials:dotenv# .env - Vonage Credentials and Configuration # NEVER commit this file to version control! # Vonage Application Credentials (Required for Messages API) VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID_HERE # Paste the Application ID you copied VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to your project root where you saved the private key # Vonage Virtual Number (Required) VONAGE_NUMBER=+1YOUR_VONAGE_NUMBER_HERE # Your purchased/linked Vonage number in E.164 format # Optional: Default port for the server # PORT=3000- Replace placeholders with your actual Application ID and Vonage number.
- Ensure
VONAGE_PRIVATE_KEY_PATHpoints correctly to where you saved theprivate.keyfile (e.g.,./private.keyif it's in the project root).
5. Error Handling, Logging, and Retries
Our current implementation has basic error handling and logging. Let's refine it.
- Error Handling Strategy:
- Use
try...catchblocks around external API calls (likevonage.messages.send). - Validate user input early and return specific 4xx errors (like 400 Bad Request).
- Return appropriate HTTP status codes (4xx, 5xx) based on the error type (client error vs. server/dependency error).
- Parse error responses from Vonage to provide more specific feedback when possible (as implemented in the
/sendroute).
- Use
- Logging:
console.logandconsole.errorare used for simplicity in this guide.- Production Recommendation: Use a structured logging library like Pino or Winston. This enables:
- Different log levels (debug, info, warn, error).
- Structured output (JSON) for easier parsing by log management systems (e.g., Datadog, Splunk, ELK stack).
- Configurable output destinations (console, files, external services).
- Example Log Enhancement (Conceptual):
javascript
// const pino = require('pino')(); // Example using Pino // Inside the catch block of /send // logger.error({ err: err, requestBody: req.body, vonageDetails: err.response?.data }, 'Failed to send SMS via Vonage');
- Retry Mechanisms:
- Sending a single SMS typically doesn't require complex client-side retries, as Vonage handles delivery attempts internally.
- However, if the initial API call to Vonage fails due to transient network issues or temporary Vonage unavailability (e.g., 502, 503, 504 errors), implementing a simple retry strategy with exponential backoff could be beneficial in high-reliability scenarios. Libraries like
async-retrycan help. - Example (Conceptual):
javascript
// const retry = require('async-retry'); // // Inside /send route... // try { // const resp = await retry(async bail => { // try { // return await vonage.messages.send({ /* ... */ }); // } catch (err) { // // If the error is definitely not retryable (e.g., 4xx client error), bail out // if (err.response && err.response.status >= 400 && err.response.status < 500) { // bail(err); // Stop retrying and throw the original error // return; // } // throw err; // Re-throw other errors to trigger retry // } // }, { // retries: 3, // Number of retries // factor: 2, // Exponential backoff factor // minTimeout: 1000 // Initial timeout in ms // }); // // Success handling... // } catch (err) { // // Final error handling after retries fail... // // logger.error({ finalErr: err }, 'SMS sending failed after multiple retries'); // // Respond to client... // } - For this basic guide, we will omit client-side retries for simplicity.
6. Database Schema and Data Layer
This specific application only sends SMS messages and doesn't store any state or data persistently. Therefore, no database schema or data layer is required.
If you were building a more complex application (e.g., tracking sent messages, user preferences, conversation history), you would typically introduce:
- A database (e.g., PostgreSQL, MongoDB).
- An ORM (e.g., Prisma, Sequelize, Mongoose) or query builder (e.g., Knex.js).
- Schema definitions and migrations to manage database structure.
- Data access functions or classes to interact with the database.
7. Security Features
Security is paramount, especially when dealing with APIs and potentially sensitive communication.
- Input Validation and Sanitization:
- Validation: We implemented basic presence checks for
toandtext. Production apps should add format validation (e.g., ensuringtolooks like a phone number, potentially using a library likelibphonenumber-js). Check message length against SMS limits (typically 160 GSM-7 characters or 70 UCS-2 characters per segment). - Sanitization: Less critical for data being sent to Vonage, but crucial if displaying user-provided content elsewhere. Libraries like
DOMPurify(for HTML) or simply ensuring correct data types can help. For SMS text, ensure you handle or strip characters that might cause issues.
- Validation: We implemented basic presence checks for
- Credential Security:
- Environment Variables: Use
.envand ensure it's in.gitignore. - Private Key: Store securely and keep it out of version control (
.gitignore). - File Permissions (Best Practice): On Linux/macOS/Unix-like systems, set strict file permissions on your private key file to ensure only the application user can read it:
chmod 400 private.key. This prevents accidental exposure. - Production: Use dedicated secret management solutions provided by your cloud provider (e.g., AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) instead of relying solely on environment variables or deployed files.
- Environment Variables: Use
- Rate Limiting: Protect your API endpoint from abuse and brute-force attempts. Use middleware like
express-rate-limit.bashnpm install express-rate-limitjavascript// index.js (near the top, after require statements) const rateLimit = require('express-rate-limit'); // ... (Vonage client init) ... // --- Middleware --- app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Apply rate limiting to the /send endpoint const sendLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: { success: false, error: 'Too many requests, please try again after 15 minutes.' }, standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers }); app.use('/send', sendLimiter); // Apply only to /send route // --- Routes --- // ... (your routes) ... - HTTPS: Always use HTTPS in production to encrypt data in transit. This is typically handled by a reverse proxy (like Nginx or Caddy) or your deployment platform (like Heroku, Vercel, AWS Elastic Beanstalk).
- Authentication/Authorization: Our
/sendendpoint is currently open. In a real application, you would protect it:- API Keys: Require clients to send a secret API key in headers (e.g.,
Authorization: Bearer YOUR_SECRET_KEYorX-API-Key: YOUR_SECRET_KEY). Validate this key on the server. - JWT/OAuth: Implement standard authentication flows if users are involved.
- API Keys: Require clients to send a secret API key in headers (e.g.,
8. Handling Special Cases
- Phone Number Formatting: Vonage generally prefers the E.164 format (e.g.,
+14155552671). While it might handle other formats, standardizing on E.164 prevents ambiguity. Consider using libraries likelibphonenumber-jsto parse and validate numbers. - Character Encoding & Limits:
- Standard SMS messages using the GSM-7 character set are limited to 160 characters.
- Using characters outside GSM-7 (like emojis or non-Latin characters) forces UCS-2 encoding, limiting messages to 70 characters.
- Longer messages are automatically split into multiple segments by carriers, which might incur additional costs. Vonage handles the underlying segmentation. Be mindful of length if cost or user experience is critical.
- Internationalization (i18n): If sending messages globally, ensure your
fromnumber supports sending to the destination country. Vonage number capabilities vary. Message content might need translation. - Delivery Receipts (DLRs): This guide doesn't implement DLRs. To track message status (e.g.,
delivered,failed), you would need to:- Provide a publicly accessible
status_urlwhen creating the Vonage Application or configure it per number. - Create a webhook endpoint (e.g.,
POST /webhooks/status) in your Express app to receive status updates from Vonage. - Parse the DLR data sent by Vonage.
- Provide a publicly accessible
- Invalid
fromNumber: Ensure theVONAGE_NUMBERin your.envis correctly formatted, linked to your Application, and capable of sending SMS.
9. Performance Optimizations
For this simple application, performance bottlenecks are unlikely unless sending extremely high volumes.
- Vonage Client Initialization: As already implemented, initialize the client once outside the request handler.
- Asynchronous Operations: Node.js and Express are inherently asynchronous. Using
async/awaitensures the server isn't blocked during the API call to Vonage. - Payload Size: Keep request/response payloads minimal.
- Caching: Not applicable here, but could be relevant if fetching configuration or user data frequently.
- Load Testing (Production): Use tools like
k6,artillery, orApacheBenchto simulate traffic and identify bottlenecks under load before deploying to production. Monitor CPU, memory, and network I/O. - Profiling: Use Node.js built-in profiler (
node --prof index.js) or tools like Clinic.js to analyze performance and pinpoint slow code sections if needed.
10. Monitoring, Observability, and Analytics
Basic monitoring is essential for production health.
- Health Checks: Add a simple health check endpoint.
Monitoring services can ping this endpoint to verify the application is running.javascript
// index.js (within Routes section) app.get('/health', (req, res) => { // Optionally add checks for dependencies (e.g., can Vonage client be initialized?) res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() }); }); - Logging: As mentioned in Section 5, use structured logging and centralize logs using a log aggregation service (Datadog, Logz.io, ELK, etc.). This enables searching, filtering, and alerting on log patterns.
- Metrics: Track key performance indicators (KPIs):
- Request rate (requests per second/minute).
- Request latency (how long
/sendtakes). - Error rate (percentage of 5xx/4xx errors).
- Vonage API call latency/errors (can sometimes be inferred from overall latency/errors).
- Use libraries like
prom-clientfor Prometheus-compatible metrics or integrate with APM (Application Performance Monitoring) tools (Datadog APM, New Relic, Dynatrace).
- Error Tracking: Use services like Sentry or Bugsnag to capture, aggregate, and alert on application exceptions in real-time.
- Dashboards: Visualize metrics and logs in dashboards (Grafana, Kibana, Datadog Dashboards) to get an overview of application health and performance. Example dashboard widgets: SMS Sent Rate, SMS Error Rate, P95/P99 Latency for
/send. - Alerting: Configure alerts based on metrics (e.g., error rate > 5%, latency > 500ms) or log patterns (e.g., frequent ""Non-Whitelisted Destination"" errors) to notify developers of issues.
11. Troubleshooting and Caveats
Non-Whitelisted DestinationError /Invalid parametersontofield:- Cause: Primarily occurs when sending from a Vonage trial account to a phone number not added to the ""Test Numbers"" list in the Vonage dashboard. Can also occur with paid accounts if the
tonumber format is highly invalid. - Solution (Trial Account): Log in to the Vonage dashboard, find the ""Test Numbers"" section (usually on the overview page), and add/verify the recipient number. This is the most common cause for beginners.
- Solution (General): Ensure the
tonumber is in a valid format, preferably E.164 (e.g.,+14155550100).
- Cause: Primarily occurs when sending from a Vonage trial account to a phone number not added to the ""Test Numbers"" list in the Vonage dashboard. Can also occur with paid accounts if the
Authentication failed/Invalid Credentials/ 401 Unauthorized:- Cause: Incorrect
VONAGE_APPLICATION_IDor invalid/missingprivate.keyfile specified byVONAGE_PRIVATE_KEY_PATH. Incorrect API Key/Secret if using that auth method (not applicable here).
- Cause: Incorrect
Frequently Asked Questions
How to send SMS with Node.js and Express?
Use the Vonage Messages API and the Vonage Node.js Server SDK. This involves setting up an Express route to handle incoming requests, initializing the Vonage client with your credentials, and using the `messages.send()` method to send SMS messages.
What is the Vonage Messages API?
A versatile API for sending messages through various channels such as SMS, MMS, and WhatsApp. In this Node.js/Express guide, we specifically use it to send text messages (SMS).
Why use Express for SMS sending?
Express.js simplifies the creation of routes and handling HTTP requests in Node.js, making it a fast and efficient choice for building a RESTful SMS API endpoint.
When should I use the Vonage Verify API?
While the Vonage Messages API *can* send two-factor authentication codes, the Vonage Verify API is specifically designed and generally recommended for that purpose, offering more robust security and features.
Can I use a trial Vonage account?
Yes, but trial accounts require whitelisting the recipient phone numbers in the Vonage dashboard before sending test messages. This is a common step that trips up new users.
How to set up Vonage application credentials?
Create a Vonage Application in your dashboard and save the downloaded `private.key` file securely. Then, get your Application ID and add both values (along with your Vonage virtual number) to a `.env` file. Never commit your `.env` or `private.key` to version control.
What is the purpose of dotenv in this setup?
Dotenv loads environment variables from a `.env` file into `process.env`, providing a secure way to manage sensitive credentials like API keys and numbers without exposing them in your codebase.
Why initialize Vonage client outside request handler?
Initializing the Vonage client outside the request handler is crucial for performance. Doing so avoids recreating the client object on every incoming request, improving efficiency.
How to fix 'Non-Whitelisted Destination' Vonage error?
This error usually occurs with Vonage trial accounts. Whitelist the recipient phone number in your Vonage dashboard under 'Test Numbers' (or 'Sandbox'). Also, ensure the 'to' number is in E.164 format.
What are important security considerations when sending SMS?
Protect your API credentials (`.env`, `private.key`), validate and sanitize user inputs to prevent injection attacks, use HTTPS in production, implement rate limiting, and consider authentication/authorization to secure your endpoint.
How to handle long SMS messages exceeding character limits?
Vonage's API automatically segments messages longer than the standard SMS limit (160 characters for GSM-7, 70 for UCS-2). Be mindful of potential extra costs associated with multi-segment messages.
What phone number format should I use for recipients?
E.164 format is recommended for recipient phone numbers when sending SMS messages via Vonage (e.g., `+14155552671`). This international standard format helps prevent ambiguity.
How to handle Vonage API errors?
Use `try...catch` blocks around `vonage.messages.send()` calls and provide informative error responses to the client. Parse the error response from the Vonage SDK to give more specific error messages when available.
How to implement SMS delivery receipts (DLRs) in Node.js?
Set up a webhook endpoint (e.g., `/webhooks/status`) in your Express app and provide a publicly accessible URL to Vonage's API. Your endpoint will receive DLR updates, which you can then parse.
Where can I find my Vonage API Key and Secret?
Your API Key and Secret are shown in your Vonage API Dashboard. However, this guide uses Application ID and Private Key for authentication with the Messages API.