code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / vonage

How to Send MMS Messages with Vonage Messages API in Node.js | Complete Guide

Learn how to send MMS multimedia messages using Vonage Messages API in Node.js and Express. Complete tutorial with SDK setup, authentication, webhook handling, and production deployment for US 10DLC and short code numbers.

Send MMS with Vonage Messages API: Node.js Express Guide with Multimedia Messaging

Important Notice: This guide implements MMS sending using Node.js with Express framework, not Next.js with NextAuth. If you need Next.js implementation, refer to Next.js-specific Vonage guides.

This guide walks you through building a production-ready Node.js application to send MMS messages using the Vonage Messages API and Express framework. You'll learn how to configure the Vonage Node.js SDK for multimedia messaging, handle API credentials securely, build API endpoints to trigger MMS sending, and implement essential features like error handling and logging. By the end, you'll have a functional MMS service capable of sending images programmatically to US-based phone numbers.

MMS Availability (as of January 2025): MMS sending through Vonage Messages API is available with General Availability status. However, only US short codes, 10DLC numbers, and SMS-enabled toll-free numbers are currently supported. For US short codes, MMS messages can be sent to AT&T, T-Mobile (formerly Sprint), and Verizon networks in the US. Source: Vonage MMS Documentation

What You'll Build: MMS Sending Project Overview

What You're Building: A Node.js Express API with a single endpoint (/send-mms). When this endpoint receives a POST request containing a recipient phone number, a publicly accessible image URL, and an optional caption, it uses the Vonage Messages API to send an MMS message.

Problem Solved: This enables applications to programmatically send rich media content (images) via MMS – useful for notifications, alerts, marketing campaigns, or integrating media sharing into workflows.

Technologies Used:

  • Node.js: A JavaScript runtime for building server-side applications. Chosen for its asynchronous nature and large ecosystem (npm).
  • Express: A minimal and flexible Node.js web application framework. Chosen for its simplicity in creating API endpoints.
  • Vonage Messages API: A unified API for sending messages across various channels, including MMS. Chosen for its robust features and official Node.js SDK.
  • @vonage/messages SDK: The official Vonage Node.js SDK (v3.x) specifically for interacting with the Messages API. As of January 2025, MMS support has General Availability status. Source: npm @vonage/messages
  • dotenv: A module to load environment variables from a .env file into process.env. Chosen for easy management of configuration and secrets during development.
  • Note: ngrok is used as a development utility to expose the local server for receiving webhook status updates.

System Architecture:

An API client (like curl, Postman, or another application) sends a POST request to the /send-mms endpoint of the Node.js/Express API. The Express API uses the @vonage/messages SDK to call the send method with an MMS image message object. The SDK sends an MMS request to the Vonage Messages API. Vonage delivers the MMS to the recipient's phone. Vonage also sends status updates (webhooks) about the message delivery. During development, these webhooks are sent to an ngrok tunnel, which forwards them to the /webhooks/status endpoint on the local Express API server.

Prerequisites for Sending MMS with Vonage

Before you begin, ensure you have:

  • Node.js (v14 or higher recommended) and npm (or yarn) installed locally. Download Node.js
  • A Vonage API account. Sign up for free.
  • Your Vonage API Key and API Secret (found on the Vonage API Dashboard).
  • A Vonage Application ID and associated private.key file (created in Section 4).
  • A Vonage US phone number capable of sending MMS. Purchase one from the Numbers section of your Vonage dashboard. Critical: Only US short codes, 10DLC numbers, or SMS-enabled toll-free numbers support MMS. Standard long codes do not support MMS. Source: Vonage MMS Documentation
  • ngrok installed and authenticated (a free account is sufficient). Download ngrok. Used for receiving webhook status updates during development.
  • A target US phone number to receive the MMS. If using a trial Vonage account, add this number to the whitelisted numbers in your dashboard.

How to Set Up Your Node.js MMS Project

Initialize your Node.js project and install the necessary dependencies.

  1. Create Project Directory: Open your terminal and create a new directory for the project, then navigate into it.

    bash
    mkdir vonage-mms-sender
    cd vonage-mms-sender
  2. Initialize Node.js Project: Create a package.json file.

    bash
    npm init -y
  3. Install Dependencies: Install Express for the server, the Vonage Messages SDK, and dotenv for environment variables.

    bash
    npm install express @vonage/messages dotenv
  4. Set Up Project Structure: Create directories for source code and configuration.

    bash
    mkdir src config
  5. Create Environment File (.env): Create a file named .env in the project root. This file stores sensitive credentials and configuration. Never commit this file to version control.

    text
    # .env
    # Vonage API Credentials (From Dashboard -> API Settings)
    VONAGE_API_KEY=YOUR_API_KEY
    VONAGE_API_SECRET=YOUR_API_SECRET
    
    # Vonage Application Credentials (Generated in Dashboard -> Applications)
    VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID
    VONAGE_PRIVATE_KEY_PATH=./private.key # Relative path to your private key file
    
    # Vonage Number (From Dashboard -> Numbers -> Your Numbers)
    VONAGE_FROM_NUMBER=YOUR_VONAGE_US_MMS_NUMBER # Must be MMS-capable US number, e.g., 14155550100
    
    # Server Configuration
    PORT=3000
    
    # Ngrok URL (For development status webhooks)
    # Example: https://YOUR_UNIQUE_SUBDOMAIN.ngrok-free.app
    # Leave blank initially, fill in after running ngrok
    NGROK_BASE_URL=
    • VONAGE_API_KEY, VONAGE_API_SECRET: Found directly on your Vonage dashboard. Required for basic SDK authentication.
    • VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH: Obtained when creating a Vonage Application (covered in Section 4). Used for authenticating requests specifically for the Messages API.
    • VONAGE_FROM_NUMBER: Your purchased Vonage US number capable of sending MMS. Enter it without + or spaces.
    • PORT: The port your Express server listens on.
    • NGROK_BASE_URL: The public URL provided by ngrok, used for receiving status webhooks during development.
  6. Create Git Ignore File (.gitignore): Create a file named .gitignore in the project root to prevent committing sensitive files and generated folders.

    text
    # .gitignore
    node_modules
    .env
    private.key
    npm-debug.log
  7. Configuration Loading: Create a file config/index.js to load and export environment variables.

    javascript
    // config/index.js
    require('dotenv').config(); // Load variables from .env into process.env
    
    const config = {
        vonage: {
            apiKey: process.env.VONAGE_API_KEY,
            apiSecret: process.env.VONAGE_API_SECRET,
            applicationId: process.env.VONAGE_APPLICATION_ID,
            privateKeyPath: process.env.VONAGE_PRIVATE_KEY_PATH,
            fromNumber: process.env.VONAGE_FROM_NUMBER,
        },
        server: {
            port: process.env.PORT || 3000,
        },
        ngrokBaseUrl: process.env.NGROK_BASE_URL, // Used later for webhook setup if needed
    };
    
    // Basic validation
    if (!config.vonage.apiKey || !config.vonage.apiSecret || !config.vonage.applicationId || !config.vonage.privateKeyPath || !config.vonage.fromNumber) {
        console.error('Missing required Vonage credentials in .env file');
        process.exit(1); // Exit if critical config is missing
    }
    
    module.exports = config;

How to Implement the Vonage MMS Service

Create a dedicated service file to handle interactions with the Vonage Messages API. This promotes separation of concerns.

  1. Create Service File: Create src/services/vonageService.js.

  2. Implement MMS Sending Logic: Add the following code to initialize the Vonage SDK and define the function to send MMS.

    javascript
    // src/services/vonageService.js
    const { Messages, MMSImage } = require('@vonage/messages');
    const config = require('../../config'); // Load our configuration
    
    // Initialize the Vonage Messages client
    // The Messages API uses Application ID + Private Key for JWT authentication,
    // alongside the standard API Key + Secret.
    const vonageMessages = new Messages({
        apiKey: config.vonage.apiKey,
        apiSecret: config.vonage.apiSecret,
        applicationId: config.vonage.applicationId,
        privateKey: config.vonage.privateKeyPath, // SDK reads the file path
    });
    
    /**
     * Sends an MMS message using the Vonage Messages API.
     * @param {string} to - The recipient phone number (E.164 format, e.g., 14155550101).
     * @param {string} imageUrl - The publicly accessible URL of the image to send.
     * @param {string} [caption=''] - Optional caption for the image.
     * @returns {Promise<object>} - A promise that resolves with the Vonage API response.
     * @throws {Error} - Throws an error if the message sending fails.
     */
    const sendMms = async (to, imageUrl, caption = '') => {
        console.log(`Attempting to send MMS to ${to} from ${config.vonage.fromNumber}`);
    
        // Validate image URL (basic check)
        if (!imageUrl || !imageUrl.startsWith('http')) {
            throw new Error('Invalid or missing image URL. Must be a public HTTP/HTTPS URL.');
        }
    
        // Validate image format
        const supportedFormats = ['.jpg', '.jpeg', '.png', '.gif'];
        const urlLower = imageUrl.toLowerCase();
        const isSupported = supportedFormats.some(format => urlLower.includes(format));
    
        if (!isSupported) {
            console.warn(`Image URL may not be a supported format. Vonage MMS supports: ${supportedFormats.join(', ')}`);
        }
    
        // Construct the MMS message payload using the MMSImage class
        const messagePayload = new MMSImage({
            to: to,
            from: config.vonage.fromNumber, // Your Vonage MMS-capable number
            image: {
                url: imageUrl,
                caption: caption, // Optional caption
            },
            channel: 'mms', // Specify the channel as MMS
            message_type: 'image', // Specify message type
        });
    
        try {
            // Send the message using the SDK
            const response = await vonageMessages.send(messagePayload);
            console.log(`MMS submitted successfully to Vonage. Message UUID: ${response.message_uuid}`);
            return response; // Contains message_uuid
        } catch (error) {
            console.error(`Error sending MMS via Vonage: ${error.message}`);
            // Log more details for debugging if available
            if (error.response && error.response.data) {
                console.error('Vonage API Error Details:', JSON.stringify(error.response.data, null, 2));
            }
            // Re-throw a structured error or handle appropriately
            throw new Error(`Failed to send MMS: ${error.message || 'Unknown Vonage API error'}`);
        }
    };
    
    module.exports = {
        sendMms,
    };

    Why this approach?

    • @vonage/messages SDK: This specific SDK package is designed for the Vonage Messages API, which handles MMS with General Availability status as of January 2025. Older SDKs or different methods might target the legacy SMS API.
    • MMSImage Class: The SDK provides convenient classes like MMSImage to structure the payload correctly for different message types, reducing errors.
    • Application ID + Private Key: The Messages API requires JWT authentication generated from your Application ID and private key for sending messages via specific channels like MMS, WhatsApp, etc. The SDK handles JWT generation internally when you provide the App ID and private key path.
    • Image Format Validation: Added validation to warn about unsupported formats. Vonage MMS accepts .jpg, .jpeg, .png, and .gif formats. Source: Vonage MMS Documentation
    • Async/Await: Using async/await makes asynchronous code cleaner and easier to read compared to raw promises or callbacks.
    • Error Handling: The try...catch block ensures that errors from the Vonage API are caught, logged, and potentially transformed before being propagated, allowing the calling code (your API endpoint) to handle them gracefully.

How to Build the Express API Endpoint for MMS

Create the Express server and the /send-mms endpoint.

  1. Create Server File: Create src/server.js.

  2. Implement Express Server and Endpoint:

    javascript
    // src/server.js
    const express = require('express');
    const config = require('../config'); // Load configuration
    const { sendMms } = require('./services/vonageService'); // Import our service function
    
    const app = express();
    
    // --- Middleware ---
    // Parse JSON request bodies
    app.use(express.json());
    // Parse URL-encoded request bodies
    app.use(express.urlencoded({ extended: true }));
    
    // Simple request logger middleware (optional but helpful)
    app.use((req, res, next) => {
        console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
        next();
    });
    
    // --- Routes ---
    // Health check endpoint
    app.get('/health', (req, res) => {
        res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() });
    });
    
    // Endpoint to send MMS
    app.post('/send-mms', async (req, res, next) => {
        // Basic Input Validation (Consider using a library like express-validator for production)
        const { to, imageUrl, caption } = req.body;
    
        if (!to || !imageUrl) {
            return res.status(400).json({
                success: false,
                message: 'Missing required fields: `to` and `imageUrl` are required.',
            });
        }
    
        // Simple validation for phone number format (basic example)
        if (!/^\d{10,15}$/.test(to.replace(/^\+/, ''))) {
             return res.status(400).json({
                success: false,
                message: 'Invalid `to` phone number format. Use E.164 or digits only (10–15 digits).',
             });
        }
    
        try {
            // Call the Vonage service function
            const result = await sendMms(to, imageUrl, caption);
    
            // Send success response
            res.status(202).json({ // 202 Accepted: Request received, processing initiated
                success: true,
                message: 'MMS submission accepted by Vonage.',
                data: result, // Contains message_uuid
            });
        } catch (error) {
            // Pass error to the error handling middleware
            next(error);
        }
    });
    
    // --- Webhook Endpoint (for status updates) ---
    // Vonage sends status updates here (POST or GET depending on config)
    app.all('/webhooks/status', (req, res) => {
        console.log('Webhook received (Status):');
        console.log('Headers:', JSON.stringify(req.headers, null, 2));
        console.log('Body:', JSON.stringify(req.body, null, 2)); // Body format depends on Vonage settings
        console.log('Query Params:', JSON.stringify(req.query, null, 2));
    
        // IMPORTANT: Always respond with 200 OK to Vonage webhooks
        // Otherwise, Vonage will retry, potentially causing duplicate processing.
        res.status(200).send('OK');
    });
    
    // --- Global Error Handler ---
    // This middleware catches errors passed via next(error)
    app.use((err, req, res, next) => {
        console.error('Global Error Handler Caught:', err.stack || err);
    
        // Determine status code – default to 500
        const statusCode = err.statusCode || 500;
    
        res.status(statusCode).json({
            success: false,
            message: err.message || 'An internal server error occurred.',
            // Optionally include stack trace in development
            ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
        });
    });
    
    // --- Start Server ---
    const PORT = config.server.port;
    app.listen(PORT, () => {
        console.log(`Server listening on http://localhost:${PORT}`);
        console.log(`Health check available at http://localhost:${PORT}/health`);
        if (config.ngrokBaseUrl) {
            console.log(`Status Webhook expected at: ${config.ngrokBaseUrl}/webhooks/status`);
        } else {
            console.warn("NGROK_BASE_URL not set in .env – Status webhooks won't be received locally without ngrok.");
        }
    });
  3. Add Start Script to package.json: Modify the scripts section in your package.json:

    json
    // package.json
    {
      // ... other properties
      "scripts": {
        "start": "node src/server.js",
        "dev": "nodemon src/server.js" // Optional: if you install nodemon (`npm install --save-dev nodemon`)
      },
      // ... other properties
    }

How to Configure Vonage Dashboard for MMS Sending

Before running the code, configure your Vonage account and application correctly.

  1. Get API Key and Secret:

    • Navigate to your Vonage API Dashboard.
    • Your API key and API secret are displayed at the top.
    • Copy these values into the VONAGE_API_KEY and VONAGE_API_SECRET fields in your .env file.
  2. Set Default SMS API to Messages API:

    • Go to Account Settings.
    • Scroll down to "API settings".
    • Under "Default SMS Setting", ensure Messages API is selected. This is crucial for MMS and the @vonage/messages SDK to work correctly.
    • Click "Save changes".
  3. Create a Vonage Application:

    • Navigate to Applications in the dashboard.
    • Click "Create a new application".
    • Enter an Application name (e.g., "Node MMS Sender").
    • Click "Generate public and private key". This automatically downloads a private.key file to your computer and populates the public key field.
    • IMPORTANT: Move the downloaded private.key file into the root directory of your Node.js project (the same level as your .env file). Ensure the VONAGE_PRIVATE_KEY_PATH in your .env file points to it (e.g., ./private.key).
    • Enable the "Messages" capability.
    • You'll see fields for Inbound URL and Status URL. These are webhook endpoints where Vonage sends data to your application.
      • Status URL: This is where Vonage sends updates about the delivery status of your outgoing MMS messages. For development, use ngrok. Leave it blank for now, or put a placeholder like http://localhost:3000/webhooks/status. Update this after starting ngrok. Set the HTTP method to POST.
      • Inbound URL: This is where Vonage sends incoming messages sent to your Vonage number. We are not handling inbound MMS in this guide, but it's required. Use the same placeholder: http://localhost:3000/webhooks/inbound. Set the HTTP method to POST.
    • Click "Create application".
    • You'll be redirected to the application's page. Copy the Application ID displayed.
    • Paste this ID into the VONAGE_APPLICATION_ID field in your .env file.
  4. Link Your Vonage Number:

    • On the application page you were just directed to (or by navigating back to Applications and clicking your app name), scroll down to the "Linked numbers" section.
    • Find your US MMS-capable Vonage number in the list.
    • Click the "Link" button next to it. If you don't have a suitable number, click "Buy numbers".
    • Ensure the VONAGE_FROM_NUMBER in your .env file matches this linked number (use E.164 format without +, e.g., 14155550100).
  5. Set up ngrok (for Status Webhooks during Development):

    • Open a new terminal window (keep your Node.js app terminal separate).

    • Run ngrok to expose the port your Express app listens on (defined in .env, default 3000).

      bash
      ngrok http 3000
    • ngrok displays output including a Forwarding URL (e.g., https://random-subdomain.ngrok-free.app). Copy this HTTPS URL.

    • Paste this base URL into the NGROK_BASE_URL field in your .env file.

    • Go back to your application settings in the Vonage Dashboard.

    • Edit the Status URL under the "Messages" capability. Set it to your ngrok Forwarding URL followed by the webhook path defined in server.js: YOUR_NGROK_URL/webhooks/status (e.g., https://random-subdomain.ngrok-free.app/webhooks/status). Ensure the method is POST.

    • Update the Inbound URL similarly: YOUR_NGROK_URL/webhooks/inbound.

    • Click "Save changes".

How to Handle Errors and Implement Retry Logic

Your current setup includes basic error handling:

  • Service Level (vonageService.js): Catches errors during the vonageMessages.send call, logs details, and throws a new error.

  • API Level (server.js):

    • Uses try...catch in the route handler to catch errors from the service.
    • Passes caught errors to a global Express error handling middleware using next(error).
    • The global error handler logs the error stack and sends a standardized JSON error response to the client.
    • Basic input validation prevents obviously bad requests.
  • Logging: Uses console.log and console.error. For production, consider structured logging libraries like Winston or Pino for better log management and analysis.

    bash
    # Example: Add Winston (Optional)
    npm install winston

    You would then configure Winston (e.g., log to files, different levels) and replace console.log/error calls.

  • Retry Mechanisms:

    • Vonage Retries: Vonage automatically retries sending webhooks (like the status update) to your specified URL if it doesn't receive a 200 OK response quickly. This is why res.status(200).send('OK'); in the /webhooks/status endpoint is crucial.
    • Application Retries: For failures in the initial sendMms call (e.g., temporary network issue to Vonage, intermittent Vonage API errors), you might implement application-level retries. Libraries like async-retry can help implement strategies like exponential backoff. However, be cautious about retrying errors that are clearly permanent (e.g., invalid credentials, invalid recipient number).
    javascript
    // Conceptual example using async-retry (Install: npm install async-retry)
    // In vonageService.js
    // const retry = require('async-retry');
    //
    // const sendMmsWithRetry = async (to, imageUrl, caption = '') => {
    //   await retry(async bail => {
    //     // If Vonage returns specific errors indicating non-transient failure, use bail()
    //     // Example: if (error.response && error.response.data.code === 'INVALID_CREDENTIALS') {
    //     //   bail(new Error('Non-retryable error: Invalid Credentials'));
    //     //   return;
    //     // }
    //     await sendMms(to, imageUrl, caption); // Call original function
    //   }, {
    //     retries: 3, // Number of retries
    //     factor: 2, // Exponential backoff factor
    //     minTimeout: 1000, // Initial timeout
    //     onRetry: (error, attempt) => console.warn(`Retry attempt ${attempt} failed: ${error.message}`),
    //   });
    // };

How to Track MMS Delivery Status with Webhooks

While this guide focuses solely on sending MMS, a production application often needs to track the status of sent messages. This typically involves a database.

  • Conceptual Schema: You might have a messages table:

    sql
    CREATE TABLE messages (
        id SERIAL PRIMARY KEY, -- Or UUID
        vonage_message_uuid VARCHAR(255) UNIQUE, -- UUID returned by Vonage
        recipient_number VARCHAR(20) NOT NULL,
        sender_number VARCHAR(20) NOT NULL,
        image_url TEXT,
        caption TEXT,
        status VARCHAR(50) DEFAULT 'submitted', -- e.g., submitted, delivered, failed, unknown
        status_timestamp TIMESTAMPTZ,
        vonage_status_code VARCHAR(10), -- Code received in status webhook
        error_description TEXT, -- Error details from webhook
        submitted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
    );
  • Data Layer:

    • Use an ORM (like Prisma, Sequelize, TypeORM) or a database client (like pg for PostgreSQL) to interact with this table.
    • On Send: After successfully calling sendMms and getting the message_uuid, insert a new record into the messages table with status = 'submitted'.
    • On Status Webhook: When the /webhooks/status endpoint receives an update from Vonage containing a message_uuid, find the corresponding record in the messages table and update its status, status_timestamp, vonage_status_code, and potentially error_description based on the webhook data.

Implementing this is beyond the scope of this specific guide but is a critical next step for robust tracking. For more details on handling webhook callbacks, see our guide on delivery status callbacks.

How to Secure Your Vonage MMS API

Security is paramount. Here are essential considerations:

  1. Input Validation and Sanitization:

    • We added basic checks in server.js. For production, use a dedicated library like express-validator for more robust validation (e.g., checking phone number formats more strictly, validating URL structure, limiting caption length).
    • Sanitize inputs to prevent Cross-Site Scripting (XSS) if captions or other data are ever displayed in a web context.
    bash
    # Example: Add express-validator (Optional)
    npm install express-validator
    javascript
    // Example usage in server.js route
    // const { body, validationResult } = require('express-validator');
    //
    // app.post('/send-mms',
    //   body('to').isMobilePhone('any', { strictMode: false }).withMessage('Invalid phone number format'),
    //   body('imageUrl').isURL({ protocols: ['http', 'https'] }).withMessage('Invalid or non-HTTPS image URL'),
    //   body('caption').optional().isLength({ max: 1000 }).escape(), // Escape for safety
    //   async (req, res, next) => {
    //     const errors = validationResult(req);
    //     if (!errors.isEmpty()) {
    //       return res.status(400).json({ success: false, errors: errors.array() });
    //     }
    //     // ... rest of the route logic ...
    // });
  2. Protect API Credentials:

    • NEVER commit .env or private.key to Git (enforced by .gitignore).
    • In production environments, use secure methods for managing secrets (e.g., environment variables injected by the platform, AWS Secrets Manager, HashiCorp Vault). Do not deploy .env files to production servers.
  3. Rate Limiting:

    • Protect your API endpoint from abuse by implementing rate limiting.
    bash
    # Example: Add express-rate-limit (Optional)
    npm install express-rate-limit
    javascript
    // Example usage in server.js (apply before routes)
    // const rateLimit = require('express-rate-limit');
    //
    // const limiter = 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', limiter); // Apply specifically to the send endpoint
  4. API Endpoint Authentication/Authorization:

    • The /send-mms endpoint itself is currently open. In a real application, protect it. Methods include:
      • API Keys: Require clients to send a secret API key in a header (e.g., X-API-Key). Validate this key in middleware.
      • JWT Tokens: If users log in to your application, issue JWTs and require them for API calls.
      • IP Whitelisting: Restrict access to specific IP addresses if applicable.
  5. Webhook Security:

    • Vonage supports signing webhooks using either a shared secret or your application's private key (JWT). Validating these signatures ensures that incoming webhook requests genuinely originated from Vonage. Refer to the Vonage documentation on securing webhooks. Implementing signature validation is highly recommended for production.

Understanding MMS Limitations and Requirements

Understanding Vonage MMS limitations is critical for successful implementation:

  • US Number Requirement (Critical): Vonage MMS sending is only supported from US short codes, 10DLC numbers, or SMS-enabled toll-free numbers. Standard US long codes do not support MMS. MMS can be sent to US numbers on AT&T, T-Mobile (formerly Sprint), and Verizon networks. Sending to international numbers or from non-US/Canada numbers will not work. Source: Vonage MMS Documentation
  • 10DLC Registration: If using a 10DLC number, ensure you have completed 10DLC registration including brand and campaign registration. Message throughput and deliverability vary by number type. For detailed guidance, see our 10DLC SMS registration guide.
  • Publicly Accessible Image URL: The imageUrl provided must resolve to a publicly accessible image file over HTTP or HTTPS. Vonage needs to fetch this image to send it. Local file paths or private URLs will not work.
  • Supported Image Formats: Vonage MMS supports .jpg, .jpeg, .png, and .gif formats. Verify the image URL resolves to one of these formats. Source: Vonage MMS Documentation
  • Image Size Limits: While Vonage documentation does not specify exact file size limits, carrier restrictions typically limit MMS messages to 300–600 KB total size. Test with images under 500 KB for best compatibility across carriers.
  • Trial Account Limitations: If using a free trial Vonage account, you can only send messages (SMS/MMS) to phone numbers that you have verified and added to your test numbers list in the dashboard. Attempting to send to other numbers will result in a "Non-Whitelisted Destination" error.
  • Character Encoding/Captions: Ensure captions use standard character sets (like UTF-8). Special characters might be handled differently by recipient carriers. Keep captions concise (recommended under 160 characters).
  • A2P Compliance: Ensure your use case complies with Application-to-Person messaging regulations and Vonage's acceptable use policies, especially regarding consent if sending marketing messages. US carriers enforce strict A2P compliance for 10DLC and short code messaging.

How to Optimize MMS Sending Performance

For this specific use case (sending individual MMS messages triggered by an API call), major performance bottlenecks are less likely within the Node.js application itself compared to external factors (Vonage API latency, carrier delivery). However:

  • Asynchronous Operations: Node.js and the Vonage SDK are inherently asynchronous. Ensure you don't block the event loop with synchronous code in your route handlers or services. Using async/await correctly helps manage this.
  • Resource Usage: Monitor CPU and memory usage under load. If the service handles high traffic, ensure efficient JSON parsing and minimize unnecessary computation per request.
  • Connection Pooling: If interacting with a database (Section 6), ensure your ORM or client uses connection pooling effectively.
  • Caching: Caching is generally not applicable for the sending action itself. However, if you frequently retrieve data before sending (e.g., user details, image URLs from a CMS), caching that retrieved data can improve the performance of the steps leading up to the sendMms call.
  • Load Testing: Use tools like k6, artillery, or ApacheBench (ab) to simulate traffic against your /send-mms endpoint and identify potential bottlenecks under load. Monitor Vonage API response times and error rates during tests.
  • Profiling: Use Node.js built-in profiler or tools like Clinic.js to analyze CPU usage and identify performance hotspots within your code if needed.

How to Monitor Your MMS Service in Production

To understand how your service behaves in production:

  1. Health Checks: The /health endpoint provides a basic check. Monitoring systems (like Prometheus, Datadog, UptimeRobot) can periodically ping this endpoint to ensure the service is running.

  2. Performance Metrics:

    • Track the latency of requests to the /send-mms endpoint.
    • Track the latency of calls to the Vonage API (sendMms function duration).
    • Monitor the rate of successful requests vs. error responses (HTTP 4xx/5xx).
    • Tools: Prometheus with prom-client, Datadog APM, New Relic.
  3. Error Tracking:

    • Integrate an error tracking service (e.g., Sentry, Bugsnag, Datadog Error Tracking). These services capture unhandled exceptions and errors passed to the global error handler, group them, and provide detailed context (stack traces, request data).
    bash
    # Example: Add Sentry (Optional)
    npm install @sentry/node @sentry/tracing
    javascript
    // Example integration in server.js (early in the file)
    // const Sentry = require('@sentry/node');
    // const Tracing = require('@sentry/tracing'); // If using tracing
    //
    // Sentry.init({
    //   dsn: "YOUR_SENTRY_DSN",
    //   integrations: [
    //     // enable HTTP calls tracing
    //     new Sentry.Integrations.Http({ tracing: true }),
    //     // enable Express.js middleware tracing
    //     new Tracing.Integrations.Express({ app }),
    //   ],
    //   // Set tracesSampleRate to 1.0 to capture 100%
    //   // of transactions for performance monitoring.
    //   // Adjust in production!
    //   tracesSampleRate: 1.0,
    // });
    //
    // // RequestHandler creates a separate execution context using domains, so that every
    // // transaction/span/breadcrumb is attached to its own Hub instance
    // app.use(Sentry.Handlers.requestHandler());
    // // TracingHandler creates a trace for every incoming request
    // app.use(Sentry.Handlers.tracingHandler());
    //
    // // ... Your routes BEFORE the error handler ...
    //
    // // The error handler must be before any other error middleware and after all controllers
    // app.use(Sentry.Handlers.errorHandler());
    //
    // // Optional fallthrough error handler
    // app.use(function onError(err, req, res, next) {
    //   // The error id is attached to `res.sentry` to be returned
    //   // and optionally displayed to the user for support.
    //   res.statusCode = 500;
    //   res.end(res.sentry + "\n");
    // });
  4. Structured Logging:

    • Replace console.log/error with a structured logging library (Winston, Pino). Log events with consistent formats (JSON is common), including timestamps, severity levels, request IDs, and relevant context (e.g., message_uuid, recipient).
    • Send logs to a centralized logging platform (e.g., Datadog Logs, ELK Stack, Splunk, Grafana Loki) for searching, analysis, and alerting.
  5. Vonage Dashboard & Analytics:

    • Regularly check the Vonage Messages API Dashboard for insights into delivery rates, error codes, and usage trends provided directly by Vonage. This complements your application-level monitoring.

By implementing these observability practices, you gain crucial visibility into your MMS sending service's health, performance, and potential issues.

Expand your Vonage messaging capabilities:

Vonage MMS Implementation Summary

This guide covered how to send MMS messages using the Vonage Messages API with Node.js and Express. You learned to:

  • Set up the Vonage Messages SDK with proper authentication (API Key, Secret, Application ID, and private key)
  • Build a production-ready Express API with MMS sending capabilities
  • Handle critical MMS limitations including US number requirements (short codes, 10DLC, toll-free only)
  • Implement security features including credential management, input validation, and webhook verification
  • Configure webhooks for delivery status tracking using ngrok during development
  • Optimize performance and implement monitoring for production deployments

Key Takeaways:

  • MMS support has General Availability status as of January 2025
  • Only US short codes, 10DLC numbers, and SMS-enabled toll-free numbers support MMS
  • Supported image formats: .jpg, .jpeg, .png, .gif (recommended under 500 KB)
  • 10DLC registration required for 10DLC numbers
  • Vonage Messages SDK (v3.x) provides the MMSImage class for structured payloads

Frequently Asked Questions

How to send MMS messages with Node.js?

Use the Vonage Messages API with the official Node.js SDK (@vonage/messages) and Express.js to create an API endpoint that handles MMS sending. The API endpoint receives the recipient's number, image URL, and an optional caption, then uses the Vonage SDK to send the MMS via the Vonage Messages API. This allows you to integrate rich media messaging into your Node.js application.

What is Vonage Messages API used for in Node.js?

The Vonage Messages API is a unified API for sending messages through various channels, including MMS, and it's used with the @vonage/messages SDK in Node.js for sending image and text based messages. It is chosen for its rich features and ease of integration with the Node.js ecosystem. This enables sending of MMS messages, notifications, alerts, and incorporating rich media content into your application workflows.

Why is Express.js used in the Node.js Vonage MMS setup?

Express.js simplifies creating the necessary API endpoints for sending MMS messages. Its minimal and flexible structure makes setting up routes, handling requests, and managing middleware easy, providing a solid foundation for the MMS sending application. Express.js is well-suited for building APIs and web applications in Node.js.

How can I handle Vonage API credentials securely in my Node.js project?

Store credentials like API keys and secrets in a .env file. Use the dotenv package to load these variables into process.env. Never commit the .env file to version control. In production, employ more secure methods like environment variables injected by the platform, AWS Secrets Manager, or HashiCorp Vault.

What are the prerequisites for setting up Vonage MMS sending in Node.js?

You'll need Node.js, npm or yarn, a Vonage API account with an API key and secret, a Vonage application with an associated private.key file, a Vonage virtual US phone number enabled for MMS, ngrok for local development webhooks, and a target US phone number for testing.

When should I use ngrok with the Vonage Messages API?

Use ngrok during development to create a public URL that tunnels webhook requests from the Vonage Messages API to your local server. This allows you to receive status updates about your messages in your development environment without deploying your application publicly.

How to create a Vonage application for MMS messaging?

In your Vonage dashboard, navigate to 'Applications' and click 'Create a new application.' Generate public and private keys (store the private key securely). Enable the 'Messages' capability, setting Inbound and Status URLs to your application endpoints. Link your Vonage number capable of sending MMS to this application.

Can I send MMS messages to international numbers using Vonage from my Node.js app?

Vonage's MMS functionality is primarily designed for sending messages from US/Canada numbers to US numbers. While some international sending might be possible, there are limitations. Check Vonage's documentation for specific international capabilities from your number and potential restrictions.

What is the purpose of the private.key file in the Vonage MMS setup?

The private.key file is used for authentication and authorization. It's part of the JWT (JSON Web Token) authentication process that the Vonage Messages API uses, alongside your Application ID. The Vonage Node.js SDK reads this file to securely sign API requests, ensuring only your application can send messages associated with your account.

How do I set the default SMS API to Messages API in the Vonage dashboard?

Go to your Vonage Account Settings, then API Settings. Under 'Default SMS Setting,' select 'Messages API.' This setting is crucial for MMS and the Vonage Node.js messages SDK to function correctly.

How to handle errors when sending MMS messages with the Vonage API?

Implement error handling with try-catch blocks around the Vonage SDK's send function. Use a global error handler in Express.js to log detailed error information, including stack traces. Consider using a dedicated logging library like Winston or Pino for improved logging in production. Use res.status(200).send('OK') in webhook endpoints to avoid Vonage retrying failed webhooks.

How do I structure my project to send MMS messages with Node.js and Vonage?

Set up separate folders for source code (src), configuration (config), and routes. Create a dedicated service module to encapsulate interactions with the Vonage Messages API, and manage configuration and credentials securely. Consider libraries like express-validator and retry mechanisms using async-retry for more robust code in production.

How can I test my Vonage MMS Node.js application locally?

Run your Express server, use ngrok to create a public tunnel to your localhost, and update your Vonage application webhook URLs to use the ngrok address. Then, use tools like curl or Postman to send test MMS messages through your API endpoint, and inspect status updates delivered by Vonage to your local server through the ngrok tunnel.

Why do I need to whitelist numbers for testing MMS with a Vonage trial account?

With a Vonage trial account, you can only send SMS/MMS to verified numbers added to your 'test numbers' list in the dashboard. This is a security measure. Add your test recipient numbers to this whitelist to avoid 'Non-Whitelisted Destination' errors during testing.

What information should I include in a database schema for tracking sent MMS messages?

Include fields for Vonage's message UUID, recipient and sender numbers, image URL, caption, message status, status timestamps, Vonage status codes, error descriptions, and timestamps for when messages were submitted and updated. This comprehensive data will help in tracking and managing sent messages.