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
.env
file 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.
mkdir vonage-sms-sender cd vonage-sms-sender
-
Initialize Node.js Project: This creates a
package.json
file to manage your project's dependencies and scripts.npm 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.
npm install express @vonage/server-sdk dotenv
express
: 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.
touch index.js .env .gitignore
index.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_modules
and.env
to your.gitignore
file to prevent committing dependencies and secrets.# .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:
vonage-sms-sender/ ├── .env ├── .gitignore ├── index.js ├── node_modules/ └── package.json
-
Add Start Script (Optional but Recommended): Open
package.json
and add astart
script for easily running your application.// 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.// 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.env
intoprocess.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.key
file 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
dotenv
is 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
/send
Route: Add the following POST route handler inindex.js
after the middleware and beforeapp.listen
.// 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/send
path.async (req, res)
: Marks the handler as asynchronous, allowing us to useawait
for 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 (
await
doesn't throw an error), it returns a 200 OK status with themessage_uuid
provided by Vonage. - Error Handling: The
try...catch
block 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):
{ ""to"": ""+14155550100"", ""text"": ""Hello from your Node.js app!"" }
- Success Response (200 OK):
{ ""success"": true, ""message_uuid"": ""aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"" }
- Error Response (400 Bad Request - Missing Fields):
{ ""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):
{ ""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):
{ ""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
).curl -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.key
file that downloads. Store this file securely within your project directory (e.g., at the root level). Remember we addedprivate.key
to.gitignore
. - Enable the ""Messages"" capability.
- For ""Inbound URL"" and ""Status URL"", you can enter placeholders like
https://example.com/webhooks/inbound
andhttps://example.com/webhooks/status
for 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 usingngrok
during 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.send
method to work correctly for SMS. - Click ""Save changes"".
-
Populate
.env
File: Open the.env
file in your project and add the credentials:# .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_PATH
points correctly to where you saved theprivate.key
file (e.g.,./private.key
if 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...catch
blocks 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
/send
route).
- Use
- Logging:
console.log
andconsole.error
are 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):
// 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-retry
can help. - Example (Conceptual):
// 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
to
andtext
. Production apps should add format validation (e.g._ ensuringto
looks 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
.env
and 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
.npm install express-rate-limit
// 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
/send
endpoint 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_KEY
orX-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-js
to 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
from
number 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_url
when 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
from
Number: Ensure theVONAGE_NUMBER
in your.env
is 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/await
ensures 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
_ orApacheBench
to 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.
// 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
/send
takes). - Error rate (percentage of 5xx/4xx errors).
- Vonage API call latency/errors (can sometimes be inferred from overall latency/errors).
- Use libraries like
prom-client
for 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 Destination
Error /Invalid parameters
onto
field:- 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
to
number 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
to
number 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_ID
or invalid/missingprivate.key
file specified byVONAGE_PRIVATE_KEY_PATH
. Incorrect API Key/Secret if using that auth method (not applicable here).
- Cause: Incorrect