code examples
code examples
Send MMS with Node.js, Express, and Vonage
A guide to building a Node.js and Express application for sending MMS messages using the Vonage Messages API, covering setup, implementation, and testing.
This guide provides a step-by-step walkthrough for building a Node.js and Express application capable of sending MMS (Multimedia Messaging Service) messages using the Vonage Messages API. We'll cover everything from project setup and Vonage configuration to implementing the core sending logic, handling errors, and testing the endpoint.
By the end of this tutorial, you will have a functional Express API endpoint that accepts a recipient phone number, an image URL, and an optional caption, then sends an MMS message via Vonage.
Technologies Used:
- Node.js: A JavaScript runtime environment for server-side development.
- Express: A minimal and flexible Node.js web application framework.
- Vonage Messages API: A multi-channel API enabling communication via SMS, MMS, WhatsApp, and more. We'll use its MMS capabilities.
@vonage/messagesSDK: The official Vonage Node.js SDK specifically for interacting with the Messages API.dotenv: A module to load environment variables from a.envfile.ngrok(for setup/testing): A tool to expose local servers to the internet, necessary for configuring Vonage webhooks during setup.
System Architecture:
A typical flow involves an end-user triggering your Express API, which then uses the Vonage SDK to call the Vonage Messages API. Vonage handles the interaction with carrier networks to deliver the MMS to the recipient's phone. Configuration details like API keys are read from environment variables.
(A diagram illustrating this flow would typically be included here.)
Prerequisites:
- Node.js and npm (or yarn): Installed on your development machine. (Download Node.js)
- Vonage API Account: Sign up for free if you don't have one. (Vonage Signup) You'll get some free credit to start.
- Vonage US Number: Purchase an MMS-capable US number from your Vonage dashboard (Numbers > Buy Numbers). MMS sending is primarily supported from US 10DLC, Toll-Free, or Short Code numbers to US recipients.
ngrok: Installed and authenticated (a free account is sufficient). (Download ngrok) We need this temporarily during Vonage application setup.- Basic understanding of JavaScript, Node.js, and REST APIs.
1. Vonage Account and Application Setup
Before writing code, we need to configure Vonage correctly.
- Log in to your Vonage API Dashboard.
- Get API Key and Secret: Note down your
API keyandAPI secretfound at the top of the dashboard. - Purchase a Number: Navigate to
Numbers>Buy numbers. Search for a US number withSMSandMMScapabilities enabled. Purchase one. Note down this number (e.g.,12015550123). This will be yourVONAGE_NUMBER. - Set Messages API as Default: Go to
Account Settings. Scroll down toAPI settings>Default SMS Setting. EnsureMessages APIis selected as the default for sending SMS messages. This ensures consistency if you later mix SMS and MMS via the Messages API. ClickSave changes. - Create a Vonage Application:
- Navigate to
Applications>+ Create a new application. - Give it a name (e.g.,
My Node MMS App). - Generate Public/Private Key: Click
Generate public and private key. A public key will appear in the form, and aprivate.keyfile will be downloaded. Save thisprivate.keyfile securely — we'll place it in our project directory later. - Enable Capabilities: Toggle on
Messages. This will reveal fields forStatus URLandInbound URL. - Configure Webhooks (Temporary Setup): Vonage requires these URLs even if you're only sending MMS, as they can receive delivery status updates. We'll use
ngrokto create temporary public URLs pointing to our local machine.- Open a terminal and run:
ngrok http 3000(assuming our Express app will run on port 3000). - Copy the
ForwardingURL provided by ngrok (it looks likehttps://<random_string>.ngrok.io). - In the Vonage application settings:
- Set
Status URLto<your_ngrok_url>/webhooks/status - Set
Inbound URLto<your_ngrok_url>/webhooks/inbound - Select
POSTfor the HTTP Method for both.
- Set
- Note: These endpoints don't need to actually exist in our simple sending application, but Vonage requires valid URLs during setup.
- Open a terminal and run:
- Click
Create application.
- Navigate to
- Link Your Number: On the next screen, find the US number you purchased earlier and click the
Linkbutton next to it to associate it with this application. - Note Application ID: After linking the number, you'll be back on the application's overview page. Note down the
Application ID.
You now have:
- Vonage API Key
- Vonage API Secret
- Vonage Application ID
- Your purchased Vonage Number (Sender Number)
- The
private.keyfile
2. Setting Up the Node.js Project
Let's create our Node.js project structure.
-
Create Project Directory:
bashmkdir vonage-mms-sender cd vonage-mms-sender -
Initialize Node.js Project:
bashnpm init -yThis creates a
package.jsonfile. -
Enable ES Modules: Open the generated
package.jsonfile and add the following top-level key-value pair to enable ES Module syntax (import/export) used in the code:json{ ""name"": ""vonage-mms-sender"", ""version"": ""1.0.0"", ""description"": """", ""main"": ""index.js"", ""type"": ""module"", ""scripts"": { ""test"": ""echo \""Error: no test specified\"" && exit 1"" }, ""keywords"": [], ""author"": """", ""license"": ""ISC"" }Save the
package.jsonfile. -
Install Dependencies:
bashnpm install express @vonage/messages dotenvexpress: The web framework.@vonage/messages: The Vonage SDK for the Messages API.dotenv: To manage environment variables.
-
Create Project Files:
bashtouch index.js .env .gitignore -
Configure
.gitignore: Add the following lines to your.gitignorefile to prevent committing sensitive information and unnecessary files:text# Dependencies node_modules # Environment variables .env # Vonage Private Key private.key # Logs npm-debug.log* yarn-debug.log* yarn-error.log* -
Place Private Key: Move the
private.keyfile you downloaded from Vonage into the root of yourvonage-mms-senderproject directory. -
Set Up Environment Variables: Open the
.envfile and add your Vonage credentials and configuration. Replace the placeholder values with your actual credentials.dotenv# .env # Vonage Credentials VONAGE_API_KEY=YOUR_VONAGE_API_KEY VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET VONAGE_APP_ID=YOUR_VONAGE_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to project root # Vonage Number (Must be MMS capable and linked to the App ID) # Use E.164 format (e.g., 12015550123) VONAGE_NUMBER=YOUR_VONAGE_SENDER_NUMBER # Server Configuration PORT=3000VONAGE_API_KEY: Found on your Vonage Dashboard.VONAGE_API_SECRET: Found on your Vonage Dashboard.VONAGE_APP_ID: Found on your Vonage Application's page after creation.VONAGE_PRIVATE_KEY_PATH: The path to theprivate.keyfile you placed in the project root../private.keyis correct if it's in the same directory asindex.js.VONAGE_NUMBER: The MMS-capable US number you purchased and linked, in E.164 format.PORT: The port your Express server will listen on (matching thengroksetup).
3. Implementing Core Functionality: The Express API
Now, let's write the code for our Express server and the MMS sending logic.
Edit index.js:
// index.js
import express from 'express';
import 'dotenv/config'; // Load environment variables from .env file
import { Messages, MMSImage } from '@vonage/messages'; // Import necessary classes
// --- Configuration ---
const PORT = process.env.PORT || 3000;
const VONAGE_API_KEY = process.env.VONAGE_API_KEY;
const VONAGE_API_SECRET = process.env.VONAGE_API_SECRET;
const VONAGE_APP_ID = process.env.VONAGE_APP_ID;
const VONAGE_PRIVATE_KEY_PATH = process.env.VONAGE_PRIVATE_KEY_PATH;
const VONAGE_NUMBER = process.env.VONAGE_NUMBER;
// Basic validation for essential environment variables
if (!VONAGE_API_KEY || !VONAGE_API_SECRET || !VONAGE_APP_ID || !VONAGE_PRIVATE_KEY_PATH || !VONAGE_NUMBER) {
console.error(""Error: Missing required Vonage environment variables. Please check your .env file."");
process.exit(1); // Exit if configuration is incomplete
}
// --- Initialize Vonage Client ---
// Use a try-catch block for robustness in case the private key path is wrong
let vonageMessages;
try {
vonageMessages = new Messages({
apiKey: VONAGE_API_KEY,
apiSecret: VONAGE_API_SECRET,
applicationId: VONAGE_APP_ID,
privateKey: VONAGE_PRIVATE_KEY_PATH,
});
console.info(""Vonage client initialized successfully."");
} catch (error) {
console.error(""Error initializing Vonage client:"", error.message);
console.error(""Ensure VONAGE_PRIVATE_KEY_PATH in .env points to your private.key file."");
process.exit(1);
}
// --- Initialize Express App ---
const app = express();
// Middleware to parse JSON request bodies
app.use(express.json());
// Middleware to parse URL-encoded request bodies
app.use(express.urlencoded({ extended: true }));
// --- API Routes ---
// Simple health check endpoint
app.get('/', (req, res) => {
res.status(200).json({ message: 'Vonage MMS Sender API is running!' });
});
/**
* POST /send-mms
* Sends an MMS message via Vonage.
* Request Body:
* {
* ""to"": ""RECIPIENT_PHONE_NUMBER"", // E.164 format (e.g., 14155550100)
* ""imageUrl"": ""PUBLICLY_ACCESSIBLE_IMAGE_URL"", // e.g., https://placekitten.com/200/300
* ""caption"": ""Optional image caption"" // Optional
* }
*/
app.post('/send-mms', async (req, res) => {
console.log(""Received request to /send-mms:"", req.body);
const { to, imageUrl, caption } = req.body;
// --- Input Validation ---
if (!to || !imageUrl) {
console.error(""Validation Error: 'to' and 'imageUrl' are required."");
return res.status(400).json({ success: false, message: ""Missing required fields: 'to' and 'imageUrl'."" });
}
// Basic validation for phone number format (simple check)
// WARNING: Basic E.164 format check. Use a library like libphonenumber-js for production.
if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
console.error(`Validation Error: Invalid 'to' phone number format: ${to}`);
return res.status(400).json({ success: false, message: ""Invalid 'to' phone number format. Use E.164 format (e.g., 14155550100)."" });
}
// Basic validation for URL format (simple check)
try {
new URL(imageUrl);
} catch (_) {
console.error(`Validation Error: Invalid 'imageUrl': ${imageUrl}`);
return res.status(400).json({ success: false, message: ""Invalid 'imageUrl'. Must be a valid URL."" });
}
// --- Construct MMS Payload ---
const mmsPayload = new MMSImage({
to: to,
from: VONAGE_NUMBER, // Your Vonage sender number from .env
image: {
url: imageUrl,
caption: caption || '', // Use provided caption or empty string
},
// channel: 'mms' // Channel is inferred by MMSImage, but can be explicit
});
// --- Send MMS via Vonage ---
try {
console.log(`Attempting to send MMS to ${to} from ${VONAGE_NUMBER}`);
const response = await vonageMessages.send(mmsPayload);
console.log(""Success: Vonage API Response:"", response);
res.status(200).json({
success: true,
message: ""MMS sent successfully!"",
message_uuid: response.message_uuid,
});
} catch (error) {
console.error(""Error sending MMS via Vonage:"", error);
// Provide more specific feedback if possible
let errorMessage = ""Failed to send MMS."";
if (error.response && error.response.data) {
console.error(""Vonage Error Details:"", error.response.data);
errorMessage = error.response.data.title || error.response.data.detail || errorMessage;
// Check for common trial account error
if (error.response.data.detail && error.response.data.detail.includes(""Non-Whitelisted Destination"")) {
errorMessage += "" Ensure the recipient number is added to your Vonage account's allowed list for trial accounts."";
}
} else if (error.message) {
errorMessage = error.message;
}
res.status(500).json({
success: false,
message: ""Error sending MMS."",
error: errorMessage,
details: error.response ? error.response.data : ""No response data"" // Include details if available
});
}
});
// --- Start Server ---
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
console.log(`Vonage Number (Sender): ${VONAGE_NUMBER}`);
console.log(`API Key Loaded: ${!!VONAGE_API_KEY}`);
console.log(`App ID Loaded: ${!!VONAGE_APP_ID}`);
console.log(`Private Key Path: ${VONAGE_PRIVATE_KEY_PATH}`);
console.log(`\nSend POST requests to http://localhost:${PORT}/send-mms`);
console.log(`Example Body: { ""to"": ""1..."", ""imageUrl"": ""https://..."", ""caption"": ""Hi!"" }`);
});Code Explanation:
- Imports: We import
express,dotenv/config(to load.envimmediately), and the necessary classes (Messages,MMSImage) from@vonage/messages. - Configuration: We load environment variables using
process.envand perform basic checks to ensure critical Vonage credentials are set. - Vonage Client Initialization: We create an instance of the
Messagesclient, passing our API key, secret, application ID, and the path to the private key. Atry-catchblock handles potential errors during initialization (e.g., wrong private key path). - Express Setup: We initialize the Express app and add middleware (
express.json,express.urlencoded) to parse incoming request bodies. - Health Check Route (
/): A simple GET route to verify the server is running. /send-mmsRoute (POST):- This is the core endpoint. It's an
asyncfunction to handle the asynchronousvonageMessages.sendcall. - Input Extraction: It retrieves
to,imageUrl, andcaptionfrom thereq.body. - Input Validation: It performs basic checks:
- Ensures
toandimageUrlare present. - Uses a simple regex to validate the
tonumber format. Note: This regex is very basic and primarily for demonstration. For production applications, use a dedicated, robust phone number validation library (likelibphonenumber-js) to handle various international formats and validity rules correctly. - Uses
new URL()in atry-catchto validate theimageUrl.
- Ensures
- Payload Construction: It creates an
MMSImageobject, providing thetonumber, thefromnumber (your Vonage number from.env), and animageobject containing theurlandcaption. - Sending: It calls
vonageMessages.send(mmsPayload)within atry...catchblock. - Success Response: If the API call is successful, it logs the response and sends a 200 status with the
message_uuid. - Error Handling: If the API call fails, it logs the error extensively. It attempts to extract specific error messages from the Vonage response (
error.response.data) and sends a 500 status with details about the failure. It specifically checks for the ""Non-Whitelisted Destination"" error common with trial accounts.
- This is the core endpoint. It's an
- Server Start:
app.listenstarts the server on the specifiedPORTand logs informative messages to the console.
4. Running and Testing the Application
-
Start the Server: Open your terminal in the
vonage-mms-senderdirectory and run:bashnode index.js(Because we added
""type"": ""module""topackage.json, we can run the file directly withnode). You should see output indicating the server is running on port 3000 and confirming your configuration loaded. -
Test with
curlor Postman: Send a POST request to your running server.Using
curl: Replace<RECIPIENT_PHONE_NUMBER>with a valid US phone number (in E.164 format, e.g.,14155550100). If you have a trial Vonage account, this number must be added to your allowed list in the Vonage Dashboard (Sandbox & Test Numbers section). Replace<PUBLIC_IMAGE_URL>with a direct link to a JPG or PNG image (e.g.,https://placekitten.com/300/400).bashcurl -X POST http://localhost:3000/send-mms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""<RECIPIENT_PHONE_NUMBER>"", ""imageUrl"": ""<PUBLIC_IMAGE_URL>"", ""caption"": ""Hello from Node.js and Vonage!"" }'Using Postman:
- Create a new request.
- Set the method to
POST. - Set the URL to
http://localhost:3000/send-mms. - Go to the
Bodytab, selectraw, and chooseJSONfrom the dropdown. - Paste the JSON payload:
json
{ ""to"": ""<RECIPIENT_PHONE_NUMBER>"", ""imageUrl"": ""<PUBLIC_IMAGE_URL>"", ""caption"": ""Hello from Postman, Node.js and Vonage!"" } - Click
Send.
-
Check the Response:
- Success: You should receive a
200 OKstatus code and a JSON response like:Check the recipient's phone; the MMS should arrive shortly. Also, check your terminal running the Node.js app for logs.json{ ""success"": true, ""message"": ""MMS sent successfully!"", ""message_uuid"": ""some-unique-message-identifier"" } - Failure: You might receive:
400 Bad Request: If required fields are missing or formats are invalid (check terminal logs for specifics).500 Internal Server Error: If Vonage failed to send the message (check terminal logs for Vonage error details). Common issues include invalid credentials, non-whitelisted number on trial accounts, or problems with the image URL.
- Success: You should receive a
5. Troubleshooting and Caveats
- Trial Account Limitation: If you're using a free trial Vonage account, you can only send messages to phone numbers you've verified and added to your test numbers list in the dashboard (
Sandbox & Test Numbers). Attempts to send elsewhere will result in a ""Non-Whitelisted Destination"" error. - A2P (Application-to-Person) Only: The Vonage Messages API, like most carrier-based messaging, generally does not support sending MMS from one virtual number (like your Vonage number) to another virtual number. It's designed for sending from an application/number to an end-user's actual mobile device on a carrier network. This limitation is typical for standard MMS delivery.
- US MMS Routing: MMS sending via Vonage is primarily supported from registered US 10DLC, Toll-Free, or Short Code numbers to recipients within the US. International MMS has limited support and may require specific configurations. Always consult the official Vonage documentation for the most up-to-date information on international MMS capabilities and requirements.
- Publicly Accessible Image URL: The
imageUrlmust point to a publicly accessible JPG, JPEG, or PNG file. Vonage needs to fetch this image to send it. Private URLs or URLs requiring authentication will not work. Use services like Imgur or public cloud storage buckets (ensure permissions are public read). - File Size Limits: Carriers often impose limits on MMS message size (typically 300KB - 1MB). Very large images might fail to send.
- Invalid Credentials/Setup: Double-check your
VONAGE_API_KEY,VONAGE_API_SECRET,VONAGE_APP_ID, andVONAGE_PRIVATE_KEY_PATHin your.envfile. Ensure theprivate.keyfile exists at the specified path. Make sure theVONAGE_NUMBERis correctly linked to theVONAGE_APP_IDin the dashboard. - Number Formatting: Always use the E.164 format for phone numbers (e.g.,
+14155550100or14155550100). - Webhook Configuration: While our app doesn't use the webhook endpoints defined during setup, Vonage requires them for the application configuration. Ensure the ngrok tunnel used during setup was running and the URLs were entered correctly. For production, you'd replace these with actual endpoints on your deployed server to handle status updates.
- SDK Versioning: Ensure you are using compatible versions of Node and the
@vonage/messagesSDK. Check the SDK documentation for requirements if you encounter unexpected issues.
6. Security Considerations
This example provides a basic, open endpoint. For production use, consider:
- Authentication/Authorization: Protect the
/send-mmsendpoint. Implement API key checking, JWT validation, or other mechanisms to ensure only authorized clients can trigger MMS sending. - Input Validation & Sanitization: Use a robust validation library (like
joiorexpress-validator) to thoroughly check incomingto,imageUrl, andcaptiondata against expected formats and lengths. Sanitize inputs to prevent potential injection attacks (though less critical for these specific fields, it's good practice). The basic regex andnew URL()checks included in this example are insufficient for a production environment. - Rate Limiting: Implement rate limiting (e.g., using
express-rate-limit) to prevent abuse and control costs. Limit how many MMS messages a single client or IP address can send within a certain time window. - Secret Management: In production deployments, avoid storing secrets directly in
.envfiles within the repository. Use dedicated secret management solutions provided by your cloud provider (e.g., AWS Secrets Manager, Google Secret Manager, Azure Key Vault) or tools like HashiCorp Vault. - Error Handling: Log errors appropriately but avoid leaking sensitive details (like full error stacks or internal paths) in responses sent back to the client.
7. Deployment and CI/CD (Conceptual)
- Environment Configuration: Use environment variables specific to your deployment environment (staging, production). Do not commit
.envfiles with production secrets. - Platform Choice: Deploy to platforms like Heroku, AWS (EC2, Lambda, Fargate), Google Cloud (App Engine, Cloud Run), Azure (App Service), or DigitalOcean.
- Build Process: Your CI/CD pipeline would typically:
- Check out code.
- Install dependencies (
npm install). - Run tests (
npm test- you'd add tests!). - Build (if necessary).
- Package the application (e.g., into a Docker container).
- Deploy the package/container to your chosen platform, injecting environment variables securely.
- Webhooks in Production: Update the Vonage Application's
Status URLandInbound URLto point to the actual public URLs of your deployed application (e.g.,https://your-app-domain.com/webhooks/status). Implement handlers for these endpoints if you need delivery receipts or want to process inbound messages.
8. Verification and Further Steps
- Manual Verification: Send test MMS messages to different (whitelisted, if trial) numbers and confirm receipt. Test with various valid image URLs. Test edge cases like missing captions or different image types (JPG vs PNG). Test invalid inputs (bad phone numbers, non-existent URLs) to ensure error handling works.
- Logging: Enhance logging using a dedicated library (like
winstonorpino) for structured logging, different log levels, and easier analysis in production. - Delivery Status: Implement the
/webhooks/statusendpoint to receive delivery receipts (DLRs) from Vonage, allowing you to track if messages were successfully delivered to the carrier and handset. - Fallback: For critical messages, consider using the Vonage Dispatch API. If MMS delivery fails (indicated by a DLR), Dispatch could automatically try sending the message content via SMS (perhaps with a link to the image).
- Testing: Add unit tests (e.g., using Jest or Mocha) to test individual functions and input validation logic. Add integration tests to verify the endpoint's interaction with the Vonage SDK (potentially using mocks or a dedicated test account).
This guide provides a solid foundation for sending MMS messages using Node.js, Express, and Vonage. Remember to adapt the security, error handling, and deployment strategies for your specific production requirements.
Frequently Asked Questions
How to send MMS with Node.js and Express?
Use the Vonage Messages API and the @vonage/messages SDK. Set up an Express server, configure your Vonage account, and create an API endpoint that accepts recipient details and image information. The Vonage SDK handles communication with the API to deliver the MMS.
What is the Vonage Messages API used for?
The Vonage Messages API is a multi-channel API that enables sending messages via various channels like SMS, MMS, WhatsApp, and more. In this tutorial, it's used to send multimedia messages (MMS) containing images and captions.
Why does Vonage require a private key?
The private key is crucial for secure authentication with the Vonage API. It's used to sign API requests, ensuring that only authorized applications can access your Vonage account and send messages.
When should I use ngrok for Vonage setup?
ngrok is useful during development and initial setup when your local server isn't publicly accessible. It creates a temporary public URL that Vonage can use for webhooks, which are necessary for the setup process, even for just sending MMS.
Can I send MMS to international numbers with Vonage?
International MMS support is limited. While Vonage primarily supports MMS from US numbers to US recipients, some international options might exist. Consult the Vonage documentation for the most current information on international MMS capabilities and any specific requirements or limitations.
How to set up a Vonage application for MMS?
Log into your Vonage dashboard, create a new application, enable the Messages capability, generate and securely store your private key, and link an MMS-capable number to your Vonage application. Set up the necessary webhooks and note your Application ID.
What is the purpose of the private.key file?
The `private.key` file contains your Vonage application's private key, which is essential for authenticating your application with the Vonage API. It must be kept secure and should not be shared or committed to version control.
How to install the necessary Node.js packages?
Use `npm install express @vonage/messages dotenv` to install the required packages. `express` is the web framework, `@vonage/messages` is the Vonage SDK, and `dotenv` helps manage environment variables.
Why do I need to configure webhooks for sending MMS?
While the basic MMS sending example doesn't actively use webhooks, Vonage requires them to be set up during application creation. They are typically used for receiving delivery receipts and inbound messages, which might be needed for more advanced functionalities.
What is the maximum MMS file size Vonage supports?
Carriers typically impose MMS file size limits, often between 300KB and 1MB. While not explicitly mentioned in the article with respect to Vonage's limits, large files may cause failures, and it's recommended to keep files small or compress them before sending.
How to handle errors when sending MMS with Vonage?
The provided code includes robust error handling using try-catch blocks around the API call. It logs errors to the console, attempts to extract specific error messages from the Vonage API response, and returns user-friendly errors to the client.
What is the 'Non-Whitelisted Destination' error?
This error occurs with trial Vonage accounts when you try to send an MMS to a number that hasn't been added to your allowed list in the Vonage dashboard's Sandbox & Test Numbers section. Add the recipient's number to your allowed list to resolve the issue.
How to structure the request body for sending MMS?
The request body must be JSON formatted and should include the recipient's phone number (`to`), the publicly accessible image URL (`imageUrl`), and an optional caption (`caption`). Format phone numbers using E.164.
What are some security best practices for a production MMS application?
Implement authentication and authorization for your sending endpoint. Use strong input validation, sanitize user inputs, implement rate limiting to prevent abuse, and use secure methods to manage secrets, avoiding storing them directly in `.env` files.