Send MMS with Node.js, Express, and Vonage
This guide provides a step-by-step walkthrough for building a Node.js application using the Express framework to send Multimedia Messaging Service (MMS) messages via the Vonage Messages API. We'll cover everything from project setup and core implementation to security, error handling, and deployment considerations.
By the end of this tutorial, you will have a functional API endpoint capable of accepting requests and sending MMS messages, including images, to specified recipients using your Vonage account. This solves the common need for applications to programmatically send rich media content to users' mobile devices within the US.
Project Overview and Goals
- Goal: Create a simple REST API endpoint using Node.js and Express that sends an MMS message (text + image) via the Vonage Messages API.
- Problem Solved: Enables applications to programmatically send images and text via MMS to US phone numbers, useful for notifications, alerts, marketing, or user engagement.
- Technologies:
- Node.js: A JavaScript runtime environment for server-side development.
- Express: A minimal and flexible Node.js web application framework for building APIs.
- Vonage Messages API: A unified API from Vonage for sending messages across various channels, including MMS.
- @vonage/server-sdk: The official Vonage Node.js SDK, simplifying interaction with Vonage APIs.
- dotenv: A module to load environment variables from a
.env
file.
- Prerequisites:
- A Vonage API account (Sign up here).
- Your Vonage API Key and API Secret (found on your Vonage Dashboard).
- Node.js and npm (or yarn) installed locally.
- A US-based Vonage phone number capable of sending SMS & MMS (How to buy a number).
- A publicly accessible URL for the image you want to send (e.g., hosted on a CDN or public server). Supported formats: JPG, JPEG, PNG.
- (Optional but recommended) ngrok or a similar tool to expose your local server for testing webhooks, though not strictly required for just sending MMS.
- Basic familiarity with JavaScript and REST APIs.
System Architecture
The basic flow involves your client application making an HTTP POST request to your Node.js/Express API. The API validates the request, uses the Vonage SDK (authenticated with your credentials) to call the Vonage Messages API, which then sends the MMS message to the recipient's phone.
+-----------------+ +-------------------------+ +------------------+ +-----------------+
| Client App |----->| Node.js/Express API |----->| Vonage Messages |----->| Recipient Phone |
| (e.g., Web/Mobile)| POST /send-mms | (using SDK + Credentials)| API | (Receives MMS) |
+-----------------+ +-------------------------+ +------------------+ +-----------------+
| - Validate Request |
| - Construct MMS Payload |
| - Call Vonage SDK |
+-------------------------+
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.
mkdir vonage-mms-sender cd vonage-mms-sender
-
Initialize npm: Initialize a new Node.js project. The
-y
flag accepts default settings.npm init -y
-
Install Dependencies: Install Express for the web server, the Vonage Server SDK, and
dotenv
for managing environment variables.npm install express @vonage/server-sdk dotenv
-
Create Project Structure: Create the main application file and a file for environment variables.
touch server.js .env .gitignore
Your basic structure should look like this:
vonage-mms-sender/ ├── node_modules/ ├── .env ├── .gitignore ├── package.json ├── package-lock.json └── server.js
-
Configure
.gitignore
: Addnode_modules
and.env
to your.gitignore
file to avoid committing sensitive credentials and dependencies.node_modules .env
-
Set up Environment Variables (
.env
): Open the.env
file and add placeholders for your Vonage credentials and configuration. We will fill these in later during the Vonage configuration step.# .env # Vonage API Credentials (Get from main Vonage Dashboard homepage) VONAGE_API_KEY=YOUR_VONAGE_API_KEY VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET # Vonage Application Credentials (Get from Dashboard > Applications > Your Application) VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to project root # Vonage Number (Your MMS-capable US number from Vonage) VONAGE_MMS_NUMBER=YOUR_VONAGE_US_NUMBER # Server Port PORT=3000
- Purpose: Using environment variables keeps sensitive data out of your codebase, making it more secure and configurable across different environments (development, staging, production).
2. Implementing Core Functionality
Now, let's write the core logic in server.js
to set up the Express server and handle the MMS sending.
// server.js
require('dotenv').config(); // Load environment variables from .env file
const express = require('express');
const { Vonage } = require('@vonage/server-sdk');
const { Messages } = require('@vonage/messages'); // Specifically for Messages API
// --- Input Validation ---
// Basic check function (can be expanded with libraries like Joi or express-validator)
function validateInput(body) {
const { to, imageUrl, caption } = body;
if (!to || !imageUrl || !caption) {
return 'Missing required fields: to, imageUrl, caption';
}
// Add more specific validation (e.g., phone number format, URL format) here
if (!/^https?:\/\/.+\.(jpg|jpeg|png)$/i.test(imageUrl)) {
return 'Invalid or unsupported image URL format. Must be HTTP/HTTPS JPG, JPEG, or PNG.';
}
// Basic E.164 format validation isn't strictly done here anymore,
// but you could add a regex check if needed, returning an error message.
// The warning about non-E.164 is handled in the route logic now.
// Example: if (!/^\+?[1-9]\d{1,14}$/.test(to)) { return 'Invalid phone number format'; }
return null; // No errors
}
// --- Vonage Client Initialization ---
// Ensure all required environment variables are present
const requiredEnvVars = [
'VONAGE_API_KEY',
'VONAGE_API_SECRET',
'VONAGE_APPLICATION_ID',
'VONAGE_PRIVATE_KEY_PATH',
'VONAGE_MMS_NUMBER'
];
const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]);
if (missingEnvVars.length > 0) {
console.error(`ERROR: Missing required environment variables: ${missingEnvVars.join(', ')}`);
console.error(""Please check your .env file or environment configuration."");
process.exit(1); // Exit if configuration is incomplete
}
let vonageMessages;
try {
// Use the specific Messages client from the SDK
vonageMessages = new Messages({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_PRIVATE_KEY_PATH // Path to your private key file
});
} catch (error) {
console.error(""ERROR: Failed to initialize Vonage Messages client."", error.message);
console.error(""Check Vonage credentials and private key path in .env file."");
if (error.code === 'ENOENT') {
console.error(`Ensure the private key file exists at: ${process.env.VONAGE_PRIVATE_KEY_PATH}`);
}
process.exit(1);
}
// --- Express App Setup ---
const app = express();
app.use(express.json()); // Middleware to parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Middleware to parse URL-encoded bodies
const PORT = process.env.PORT || 3000;
// --- Health Check Route ---
app.get('/health', (req, res) => {
res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() });
});
// --- MMS Sending Route ---
app.post('/send-mms', async (req, res) => {
// 1. Validate Input
const validationError = validateInput(req.body);
if (validationError) {
console.warn('Validation Error:', validationError, 'Request Body:', req.body);
return res.status(400).json({ success: false, message: validationError });
}
const { to, imageUrl, caption } = req.body;
const from = process.env.VONAGE_MMS_NUMBER; // Use the number from .env
// Ensure 'to' number is in E.164 format (Vonage prefers this)
// Basic example: remove non-digits, add +1 if needed for US
let formattedTo = to.replace(/\D/g, '');
if (formattedTo.length === 10) {
formattedTo = `+1${formattedTo}`;
} else if (formattedTo.length === 11 && formattedTo.startsWith('1')) {
formattedTo = `+${formattedTo}`;
} else if (!formattedTo.startsWith('+')) {
// If it's not clearly US/Canada or already E.164, log a warning but proceed
console.warn(""Attempting to send to potentially non-E.164 number:"", formattedTo, ""(Original input:"", to, "")"");
// For robustness, you might want stricter validation or prefixing rules
// depending on your target regions. MMS outside US/Canada via Vonage
// has varying support and different number requirements.
}
// 2. Construct MMS Payload using Vonage SDK helpers
const mmsPayload = {
message_type: 'image', // Specify message type as image for MMS
to: formattedTo,
from: from,
channel: 'mms', // Specify MMS channel
image: {
url: imageUrl,
caption: caption
}
// client_ref: 'your-internal-reference-123' // Optional: For tracking
};
// 3. Send MMS via Vonage
try {
console.log(`Attempting to send MMS to ${formattedTo} from ${from}`);
const response = await vonageMessages.send(mmsPayload);
console.log('Vonage API Response:', response);
// Success: Vonage accepted the message for delivery
res.status(202).json({ // 202 Accepted is appropriate as delivery is async
success: true,
message: 'MMS submitted successfully.',
message_uuid: response.message_uuid
});
} catch (error) {
// Handle Vonage API errors
console.error('Vonage API Error:', error);
let statusCode = 500; // Internal Server Error by default
let errorMessage = 'Failed to send MMS due to an internal error.';
if (error.response) {
// Error response from Vonage API
statusCode = error.response.status || 500;
errorMessage = `Vonage API Error (${statusCode}): ${error.response.data?.title || error.message}`;
if(error.response.data?.detail) errorMessage += ` Details: ${error.response.data.detail}`;
if(error.response.data?.invalid_parameters) {
errorMessage += ` Invalid Parameters: ${JSON.stringify(error.response.data.invalid_parameters)}`;
}
} else if (error.request) {
// Request made but no response received (network issue, Vonage down?)
statusCode = 504; // Gateway Timeout
errorMessage = 'No response received from Vonage API.';
} else {
// Setup error or other unexpected issue
errorMessage = `Failed to send MMS: ${error.message}`;
}
// Specific error handling checks (based on common Vonage errors)
if (errorMessage.includes('Non-Whitelisted Destination') || (error.response?.data?.type?.includes('AUTHENTICATION_FAILED') && errorMessage.includes('demo mode'))) {
statusCode = 403; // Forbidden
errorMessage = ""Destination number not whitelisted for trial account, or authentication issue related to trial status. Add the number to your test numbers in the Vonage dashboard."";
console.error(""TRIAL ACCOUNT RESTRICTION: Ensure the recipient number is added to your allowed test numbers on the Vonage dashboard."");
} else if (statusCode === 401 || errorMessage.includes('Authentication failed')) {
statusCode = 401; // Unauthorized
errorMessage = ""Authentication failed. Check Vonage API Key, Secret, Application ID, and Private Key configuration."";
} else if (statusCode === 403) {
// Could be other authorization issues like number not linked or capability missing
errorMessage += "" Check if the 'from' number is linked to the Application ID and has MMS capabilities enabled."";
} else if (errorMessage.includes('Invalid Parameters') || statusCode === 400) {
statusCode = 400; // Bad Request
// Error message often contains details
}
res.status(statusCode).json({
success: false,
message: errorMessage,
// Optionally include error details in non-production environments
// errorDetails: process.env.NODE_ENV !== 'production' ? error : undefined
});
}
});
// --- Start Server ---
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`API Endpoint available at POST /send-mms`);
console.log(`Using Vonage Number: ${process.env.VONAGE_MMS_NUMBER}`);
console.log(`Make sure the image URL provided in requests is publicly accessible.`);
console.log(`Ensure your Vonage account/number is set up for US MMS.`);
});
// Export the app instance for testing purposes
module.exports = app;
- Why this approach?
- We use
dotenv
to manage credentials securely. - We import the specific
Messages
client from@vonage/server-sdk
as recommended for the Messages API. - Basic input validation is included to catch common errors early.
- The code attempts basic E.164 formatting for the destination number, logging a warning if the input format is ambiguous.
- The
vonageMessages.send()
method is asynchronous, so we useasync/await
. - Comprehensive error handling distinguishes between validation errors, Vonage API errors, and network issues, providing informative feedback. Specific checks for common issues like trial account restrictions are included.
- We return a
202 Accepted
status on successful submission, acknowledging the asynchronous nature of message delivery. - The Express
app
is exported to allow for integration testing with tools likesupertest
.
- We use
3. Building the API Layer
The server.js
file already defines our API endpoint: POST /send-mms
.
-
Authentication/Authorization: This simple example doesn't include explicit API authentication (like API keys or JWT for your API). In a production scenario, you would secure this endpoint using middleware (e.g.,
express-bearer-token
, Passport.js) to ensure only authorized clients can trigger MMS sends. -
Request Validation: Basic validation is implemented in the
validateInput
function. For production, consider more robust libraries likeJoi
orexpress-validator
for complex validation rules. -
API Endpoint Documentation:
- Endpoint:
POST /send-mms
- Content-Type:
application/json
- Request Body (JSON):
{ ""to"": ""+15551234567"", ""imageUrl"": ""https://placekitten.com/200/300"", ""caption"": ""Hello from Vonage MMS!"" }
- Success Response (202 Accepted):
{ ""success"": true, ""message"": ""MMS submitted successfully."", ""message_uuid"": ""aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"" }
- Error Response (e.g., 400 Bad Request, 401 Unauthorized, 403 Forbidden, 500 Internal Server Error):
{ ""success"": false, ""message"": ""Descriptive error message (e.g., Missing required fields, Authentication failed, Non-Whitelisted Destination, etc.)"" }
- Endpoint:
-
Testing with
curl
: Replace placeholders with your actual recipient number and a valid image URL.curl -X POST http://localhost:3000/send-mms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+15551234567"", ""imageUrl"": ""https://www.vonage.com/content/dam/vonage/us-en/logos/vbc/vonage_logo_retina.png"", ""caption"": ""Testing Vonage MMS API"" }'
-
Testing with Postman:
- Create a new request.
- Set the method to
POST
. - Set the URL to
http://localhost:3000/send-mms
. - Go to the ""Body"" tab, select ""raw"", and choose ""JSON"" from the dropdown.
- Paste the JSON request body (like the one in the
curl
example). - Click ""Send"".
4. Integrating with Vonage (Dashboard Setup)
This is a critical step to get the necessary credentials for your .env
file.
- Log in to Vonage: Go to your Vonage API Dashboard.
- Get API Key and Secret: Your
VONAGE_API_KEY
andVONAGE_API_SECRET
are displayed prominently at the top of the dashboard homepage. Copy these into your.env
file. - Create a Vonage Application:
- Navigate to ""Applications"" > ""Create a new application"".
- Give your application a name (e.g., ""Node MMS Sender"").
- Click ""Generate public and private key"". This will automatically download the
private.key
file. Save this file in the root directory of your Node.js project (or updateVONAGE_PRIVATE_KEY_PATH
in.env
if you save it elsewhere). The public key is automatically stored by Vonage. - Enable the ""Messages"" capability.
- You need to provide Status URL and Inbound URL webhooks. For sending MMS, these primarily receive delivery status updates or inbound replies. If you don't need to process these immediately, you can use a placeholder service:
- Go to MockBin.
- Click ""Create Bin"". Leave defaults (200 OK) and click ""Create Bin"".
- Copy the generated Mockbin URL (e.g.,
https://mockbin.org/bin/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
). - Paste this URL into both the ""Status URL"" and ""Inbound URL"" fields in the Vonage application settings. Crucially, ensure the URL ends before any
/view
path. - (Self-correction based on research): The research mentions using ngrok here. While MockBin works for just sending, if you plan to receive delivery receipts or replies later, you'll need a real endpoint exposed via ngrok or a deployed server. For now, MockBin is sufficient if you only care about sending.
- Click ""Generate new application"".
- On the next screen, copy the generated Application ID. This is your
VONAGE_APPLICATION_ID
. Paste it into your.env
file.
- Link Your Vonage Number:
- While still on the application details page, scroll down to ""Link virtual numbers"".
- Find your US MMS-capable number in the list and click the ""Link"" button next to it. If you don't have one, you'll need to purchase one via the ""Numbers"" section first. This connects incoming messages/status updates for that number to this application's webhooks. It also authorizes this application (using the Application ID and Private Key) to send messages from this number.
- Verify SMS Settings (Optional but Recommended):
- Go to ""Account"" > ""API settings"".
- Scroll down to ""SMS Settings"".
- Ensure ""Default SMS Setting"" is set to Messages API. This ensures consistency if you also send SMS using this application. Click ""Save changes"" if you modify it.
- Fill
.env
: Ensure allVONAGE_
variables in your.env
file are now filled with the correct values obtained from the dashboard and yourprivate.key
file path. Remember to add your purchased US MMS number asVONAGE_MMS_NUMBER
.
5. Error Handling, Logging, and Retry Mechanisms
- Error Handling: The
server.js
code includestry...catch
blocks around the Vonage API call and specific checks for common errors (validation, authentication, trial limits, API issues, network problems). It returns appropriate HTTP status codes and informative JSON error messages. - Logging: Basic
console.log
,console.warn
, andconsole.error
are used. For production, integrate a more robust logging library likewinston
orpino
. These enable structured logging (e.g., JSON format), different log levels (debug, info, warn, error), and outputting logs to files or external services (like Datadog, Logstash).# Example: Adding Winston npm install winston
// Example winston setup (add near top of server.js) const winston = require('winston'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.Console(), // Add file transport for production // new winston.transports.File({ filename: 'error.log', level: 'error' }), // new winston.transports.File({ filename: 'combined.log' }), ], }); // Replace console.log/warn/error with logger.info/warn/error // e.g., logger.info('Server running...'); logger.error('Vonage API Error:', error);
- Retry Mechanisms: Sending an MMS is generally idempotent on the Vonage side if you use the same parameters. However, temporary network issues or Vonage service fluctuations might cause the API call from your server to Vonage to fail.
- Client-Side: The client calling your
/send-mms
endpoint could implement retries with exponential backoff if they receive a 5xx error. - Server-Side: You could wrap the
vonageMessages.send()
call in a retry loop (using libraries likeasync-retry
) for specific error conditions (like 503 Service Unavailable or 504 Gateway Timeout from Vonage). Be cautious not to retry on 4xx errors (like invalid input or authentication failure) as these will likely fail repeatedly. - Vonage Internal Retries: Vonage itself has internal mechanisms to retry delivering the message to the carrier/handset if temporary issues occur downstream.
- Client-Side: The client calling your
6. Database Schema and Data Layer (Optional)
For this specific guide focused on sending, a database isn't strictly required. However, in a real-world application, you would likely want to track sent messages, their status, and associated metadata.
- Schema Idea (e.g., using PostgreSQL):
CREATE TABLE mms_log ( id SERIAL PRIMARY KEY, message_uuid UUID UNIQUE, -- Vonage message ID (received on successful send) recipient_number VARCHAR(20) NOT NULL, sender_number VARCHAR(20) NOT NULL, image_url TEXT NOT NULL, caption TEXT, status VARCHAR(50) DEFAULT 'submitted', -- e.g., submitted, delivered, failed, rejected submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, last_updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, vonage_status_code VARCHAR(10), -- From status webhook error_message TEXT, -- From status webhook or initial send failure client_ref VARCHAR(100) -- Optional internal tracking reference ); CREATE INDEX idx_mms_log_message_uuid ON mms_log(message_uuid); CREATE INDEX idx_mms_log_recipient ON mms_log(recipient_number); CREATE INDEX idx_mms_log_status ON mms_log(status); CREATE INDEX idx_mms_log_submitted_at ON mms_log(submitted_at);
- Implementation: Use an ORM like Prisma or Sequelize, or a query builder like Knex.js to interact with the database. You would insert a record when the message is submitted (
202 Accepted
from Vonage) and update the status based on delivery receipts received via the Status Webhook (which would require implementing an endpoint for the Status URL configured in the Vonage Application).
7. Security Features
- Input Validation & Sanitization:
- The
validateInput
function performs basic checks. Use robust libraries (Joi
,express-validator
) to strictly define expected data types, formats (E.164 for numbers, valid URL), and lengths. - Sanitize any input that might be reflected in logs or potentially other outputs, though in this case, inputs are primarily passed to Vonage. Vonage handles sanitization on their end before delivery.
- The
- Environment Variables: Keep
.env
out of Git (.gitignore
). Use platform-specific secret management in production (e.g., AWS Secrets Manager, HashiCorp Vault, Doppler). - Secure API Endpoint: Protect the
/send-mms
endpoint with authentication/authorization (API keys, JWT, OAuth) to prevent unauthorized use. - Rate Limiting: Implement rate limiting (e.g., using
express-rate-limit
) on the/send-mms
endpoint to prevent abuse and protect your Vonage account balance/rate limits.npm install express-rate-limit
// Add near other middleware in server.js const rateLimit = require('express-rate-limit'); const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again after 15 minutes' }); app.use('/send-mms', apiLimiter); // Apply limiter specifically to the send endpoint
- HTTPS: Always run your Node.js application behind a reverse proxy (like Nginx or Caddy) that handles TLS/SSL termination in production, ensuring traffic is encrypted.
- Dependency Security: Regularly audit dependencies for known vulnerabilities (
npm audit
) and update them.
8. Handling Special Cases
- MMS Limitations:
- Geography: Vonage MMS via the Messages API is primarily for A2P (Application-to-Person) use cases within the United States. Sending MMS internationally or P2P (Person-to-Person) via virtual numbers has limited or no support.
- File Types: Only
.jpg
,.jpeg
, and.png
image formats are reliably supported. - Image URL: The
imageUrl
must be publicly accessible over HTTP/HTTPS. Vonage servers need to fetch the image. Private or localhost URLs will not work. - Number Types: Sending from Toll-Free, 10DLC, or Short Codes is generally supported within the US, but capabilities might vary. Sending to virtual numbers is not supported by the Messages API for MMS.
- Character Limits: While MMS itself supports longer text, carrier limitations might exist. Keep captions reasonably concise.
- Idempotency: If you need to ensure a message isn't sent multiple times due to retries, you could generate a unique
client_ref
on your side, store it (e.g., in the database), and potentially check if a message with thatclient_ref
was recently submitted before sending again. However, relying on themessage_uuid
returned by Vonage is usually sufficient for tracking.
9. Performance Optimizations
- Asynchronous Operations: Node.js and the Vonage SDK are inherently asynchronous. The
async/await
pattern ensures your server isn't blocked while waiting for Vonage's response. - Payload Size: Image size affects fetch time by Vonage and delivery time/cost. Optimize images before hosting them.
- Connection Pooling: The Vonage SDK handles underlying HTTP connections. For very high throughput, ensure your Node.js process has sufficient resources.
- Caching: Caching is less relevant for the send operation itself, but if you were fetching data to construct the MMS, caching that data could improve performance.
- Load Balancing: For high availability and throughput, run multiple instances of your Node.js application behind a load balancer (e.g., Nginx, AWS ELB). Ensure sessions are handled appropriately if you add authentication that requires session state (or use stateless methods like JWT).
10. Monitoring, Observability, and Analytics
- Health Checks: The
/health
endpoint provides a basic check. Expand it to verify database connectivity or other critical dependencies if added. - Metrics: Track key metrics:
- Request rate to
/send-mms
. - Request latency.
- Rate of successful submissions (202 responses).
- Rate of errors (4xx, 5xx), possibly broken down by error type.
- Use libraries like
prom-client
for Prometheus metrics or integrate with APM solutions (Datadog, New Relic).
- Request rate to
- Logging: As mentioned, structured logging enables easier analysis and alerting (e.g., setting up alerts in your logging platform for high error rates).
- Error Tracking: Use services like Sentry or Bugsnag to capture, aggregate, and get notified about runtime errors in your application.
- Vonage Dashboard: Monitor your message logs, usage, and balance directly within the Vonage API Dashboard (""Messages API Logs"", ""Usage"", ""Billing""). This provides visibility into successful deliveries and failures reported by carriers, which occur after your API call returns
202 Accepted
.
11. Troubleshooting and Caveats
Authentication failed
/ 401 Unauthorized: Double-checkVONAGE_API_KEY
,VONAGE_API_SECRET
,VONAGE_APPLICATION_ID
, andVONAGE_PRIVATE_KEY_PATH
in your.env
file. Ensure the private key file exists at the specified path and is readable by the Node.js process.Non-Whitelisted Destination
/ 403 Forbidden: If using a trial account, you must add the recipient's phone number to your list of approved test numbers in the Vonage Dashboard under ""Account"" > ""Test numbers"".Cannot find module './private.key'
orENOENT
error: Ensure theVONAGE_PRIVATE_KEY_PATH
in.env
correctly points to the location of yourprivate.key
file relative to where you runnode server.js
. Make sure the file was downloaded correctly and hasn't been corrupted.The requested capability is not enabled for this Application
/ 403 Forbidden: Ensure the ""Messages"" capability is enabled for theVONAGE_APPLICATION_ID
you are using in the Vonage Dashboard.The From number XXXXX is not associated with your Application
/ 403 Forbidden: Ensure theVONAGE_MMS_NUMBER
you are sending from is linked to theVONAGE_APPLICATION_ID
in the Vonage Dashboard.Invalid Parameters
/ 400 Bad Request: Check the format of theto
number (E.164 preferred), theimageUrl
(must be public, valid HTTP/HTTPS URL, correct file type), and other payload parameters against the Vonage Messages API documentation. The error response often contains details about which parameter is invalid.- Message Not Received, but API Returned Success (202):
- Check the Messages API Logs in the Vonage Dashboard for the specific
message_uuid
. Look for status updates likedelivered
,failed
, orrejected
. - Verify the recipient number is correct and can receive MMS.
- Ensure the image URL is valid and remained publicly accessible after sending.
- Carrier filtering or network issues can sometimes cause delays or failures.
- Check the Messages API Logs in the Vonage Dashboard for the specific
- MMS Works, SMS Fails (or vice-versa): Ensure the Vonage number is provisioned for both SMS and MMS capabilities and linked correctly. Check the ""Messages"" vs ""SMS"" API setting under API Settings if using both types.
- Vonage API Status: Check the Vonage Status Page for any ongoing incidents.
12. Deployment and CI/CD
- Environment Configuration: Never commit your
.env
file. Use your hosting platform's mechanism for setting environment variables (e.g., AWS Elastic Beanstalk configuration, Heroku config vars, Docker environment variables). Ensure theVONAGE_PRIVATE_KEY_PATH
environment variable correctly points to the location where your private key file is stored in the deployment environment. Alternatively, consider loading the private key content directly from an environment variable instead of a file path for better security and flexibility in containerized environments. - Build Process: For Node.js, usually involves
npm install --production
(oryarn install --production
) to install only necessary dependencies. - Deployment Strategy: Choose a suitable platform (e.g., Heroku, AWS Elastic Beanstalk, Google Cloud Run, DigitalOcean App Platform, Vercel Serverless Functions). Consider containerization with Docker for consistency.
- CI/CD Pipeline: Set up a pipeline (e.g., using GitHub Actions, GitLab CI, Jenkins) to automate testing, building, and deployment whenever changes are pushed to your repository.
- Process Management: Use a process manager like
pm2
in production to handle running your Node.js application, manage restarts, and monitor resource usage.