code examples

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

Send MMS with MessageBird Node.js API: Complete Express Tutorial

Step-by-step guide to sending MMS messages with MessageBird API using Node.js and Express. Includes multimedia messaging code examples, error handling, security best practices, and US/Canada MMS delivery.

Send MMS with MessageBird: Node.js & Express Tutorial

Learn how to send MMS (Multimedia Messaging Service) messages using the MessageBird API with Node.js and Express. This comprehensive tutorial covers everything from initial setup to production deployment, enabling you to send images, videos, and other media files directly to mobile devices in the US and Canada.

Important: Geographic Restrictions – MessageBird MMS works only within the United States and Canada. For other countries, use an alternative service.

Technical Specifications:

  • Maximum file size: 1 MB (1,024 KB) per attachment
  • Maximum attachments: 10 per message
  • Body character limit: 2,000 characters
  • Subject character limit: 256 characters
  • Media URL response timeout: 5 seconds (URLs must respond within this timeframe)
  • Supported formats: JPEG, PNG, GIF, BMP (images); MP3, OGG (audio); MP4, WebM, QuickTime (video); PDF, vCard, CSV, RTF (documents)

You'll build a REST API endpoint that accepts recipient details, a text message body, and a URL for the media file, then uses the MessageBird SDK to send the MMS message. This guide covers setup, implementation, security, error handling, deployment considerations, and verification.

Technologies Used:

  • Node.js: A JavaScript runtime environment for server-side development.
  • Express: A minimal and flexible Node.js web application framework.
  • MessageBird Node.js SDK: Simplifies interaction with the MessageBird REST API. Current version: 4.0.1 (last updated approximately 3 years ago as of 2024-2025). While the SDK hasn't received recent updates, it remains functional with MessageBird's REST API.
  • dotenv: A module to load environment variables from a .env file.

Note on SDK Status: The MessageBird Node.js SDK (npm package messagebird) is at version 4.0.1 and has not been updated since approximately 2021-2022. While this may indicate limited active development, the SDK continues to work with MessageBird's API. For production applications, consider monitoring the GitHub repository (messagebird/messagebird-nodejs) for any updates or security patches.

System Architecture:

A typical flow involves a client application sending a request to your Node.js/Express API. Your API then calls the MessageBird API with the MMS details (including the media URL). MessageBird fetches the media and sends the MMS to the recipient's device. Optionally, MessageBird can send status updates back to your API via webhooks. Your API responds to the initial client request indicating success or failure.

Prerequisites:

  • Node.js and npm (or yarn) installed.
  • A MessageBird account with a valid API key and an available virtual mobile number (VMN) or Alphanumeric Sender ID configured for sending messages. VMN (Virtual Mobile Number) is generally recommended for MMS.
  • A publicly accessible URL for the media file you want to send (e.g., stored in cloud storage like AWS S3, Google Cloud Storage, or a public web server). MessageBird needs to retrieve the file from this URL.
  • Basic understanding of Node.js, Express, and REST APIs.

By the end of this guide, you will have a functional Express application capable of accepting API requests to send MMS messages reliably using MessageBird.

1. Setting Up Your Node.js Project for MessageBird MMS Integration

Let's start by creating our project directory, initializing Node.js, and installing the necessary dependencies for MessageBird MMS integration.

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

    bash
    mkdir node-messagebird-mms
    cd node-messagebird-mms
  2. Initialize Node.js Project: This command creates a package.json file to manage project dependencies and metadata.

    bash
    npm init -y

    The -y flag accepts the default settings.

  3. Install Dependencies: We need express for the web server, messagebird for the SDK, and dotenv to manage environment variables securely.

    bash
    npm install express messagebird dotenv
  4. Create Project Structure: Create the main application file.

    bash
    touch index.js

    Create a file to store environment variables.

    bash
    touch .env

    Create a .gitignore file to prevent sensitive information and unnecessary files from being committed to version control.

    bash
    touch .gitignore
  5. Configure .gitignore: Add the following lines to your .gitignore file to exclude the node_modules directory and the .env file (which will contain your API key).

    # .gitignore node_modules .env

    Why? Keeping node_modules out of Git prevents bloating the repository. Excluding .env prevents accidentally committing sensitive API keys.

  6. Configure Environment Variables (.env): Open the .env file and add your MessageBird API key and the originator (sender) phone number or Alphanumeric ID.

    dotenv
    # .env
    MESSAGEBIRD_API_KEY=YOUR_LIVE_API_KEY
    MESSAGEBIRD_ORIGINATOR=YOUR_SENDER_NUMBER_OR_ID
    • MESSAGEBIRD_API_KEY: Your live API key obtained from the MessageBird Dashboard (Developers -> API access). Ensure it's the live key, not the test key, for sending actual messages.
    • MESSAGEBIRD_ORIGINATOR: The phone number (in E.164 format, e.g., +12025550142) or approved Alphanumeric Sender ID you want the message to appear from. Check MessageBird documentation for country restrictions, especially regarding Alphanumeric Sender IDs. Using a purchased MessageBird virtual number is recommended for reliable two-way communication and MMS support.
  7. Basic Express Server Setup (index.js): Open index.js and set up a minimal Express application.

    javascript
    // index.js
    require('dotenv').config(); // Load environment variables from .env file
    const express = require('express');
    const messagebird = require('messagebird').initClient(process.env.MESSAGEBIRD_API_KEY);
    
    const app = express();
    const port = process.env.PORT || 3000; // Use environment port or default to 3000
    
    app.use(express.json()); // Middleware to parse JSON request bodies
    
    // Basic route for testing server status
    app.get('/health', (req, res) => {
      res.status(200).send('Server is healthy');
    });
    
    // Placeholder for our MMS sending route (to be added)
    // app.post('/send-mms', (req, res) => { /* ... implementation ... */ });
    
    // Start the server
    app.listen(port, () => {
      console.log(`Server listening on port ${port}`);
    });
    
    // Basic error handling for MessageBird client initialization
    if (!process.env.MESSAGEBIRD_API_KEY) {
      console.error("FATAL ERROR: MESSAGEBIRD_API_KEY is not set in the environment variables.");
      process.exit(1); // Exit if the API key is missing
    }
    
    console.log('MessageBird SDK initialized.');

    Why dotenv.config() first? It needs to load variables before they are accessed by messagebird.initClient. Why express.json()? This middleware is crucial for parsing the JSON payload we'll send to our API endpoint. Why initClient? The official SDK requires using .initClient() to instantiate the client with your API key, as confirmed by documentation and common issues.

You now have a basic Node.js Express project structure ready for implementing the MMS sending functionality.

2. Implementing the MessageBird MMS Sending Function

The core logic involves using the messagebird.messages.create method from the SDK, providing not just the standard SMS parameters but also the specific MMS details.

  1. Define the sendMms Function: It's good practice to encapsulate the sending logic in a reusable function. Let's add this within index.js, before the app.listen call.

    javascript
    // index.js
    // ... (require statements and app initialization) ...
    
    /**
     * Sends an MMS message using the MessageBird API.
     * @param {string} recipient - The recipient's phone number in E.164 format.
     * @param {string} body - The text body of the message.
     * @param {string} mediaUrl - The publicly accessible URL of the media file.
     * @returns {Promise<object>} - A promise that resolves with the MessageBird API response.
     */
    function sendMms(recipient, body, mediaUrl) {
      return new Promise((resolve, reject) => {
        const params = {
          originator: process.env.MESSAGEBIRD_ORIGINATOR,
          recipients: [recipient],
          body: body,
          // MMS specific parameters
          mms: {
            mediaUrls: [mediaUrl]
          }
        };
    
        // Validate essential parameters before sending
        if (!params.originator || !recipient || !body || !mediaUrl) {
           return reject(new Error('Missing required parameters for sending MMS.'));
        }
        if (!params.mms.mediaUrls[0].startsWith('http')) {
           return reject(new Error('Invalid mediaUrl format. It must be a publicly accessible URL.'));
        }
    
        // Validate character limits (MessageBird API specifications)
        if (body.length > 2000) {
           return reject(new Error(`Message body exceeds maximum length of 2,000 characters. Current length: ${body.length}`));
        }
    
        // Note: File size validation should be done client-side or via additional checks
        // MessageBird requires files to be under 1MB per attachment
    
        console.log(`Attempting to send MMS to ${recipient} with media: ${mediaUrl}`);
    
        messagebird.messages.create(params, (err, response) => {
          if (err) {
            // Log the detailed error from MessageBird
            console.error("MessageBird API Error:", JSON.stringify(err, null, 2));
            // Provide a clearer error message for upstream handling
            const errorMessage = err.errors ? err.errors.map(e => e.description).join(', ') : 'Unknown MessageBird API error';
            return reject(new Error(`Failed to send MMS: ${errorMessage}`));
          }
          // Log success and the response from MessageBird
          console.log("MessageBird API Success:", JSON.stringify(response, null, 2));
          resolve(response);
        });
      });
    }
    
    // ... (app.get('/health'), app.listen, etc.) ...

    Why the mms object? This is the key difference from sending SMS. The MessageBird API requires MMS-specific parameters, like mediaUrls, to be nested within an mms object in the request payload. mediaUrls must be an array of strings, each being a publicly accessible URL. Why a Promise? The messagebird.messages.create method uses a callback pattern. Wrapping it in a Promise allows us to use async/await syntax in our API route handler for cleaner asynchronous code. Why basic validation? Checking for required parameters and a valid URL format before calling the API prevents unnecessary API calls and provides immediate feedback for invalid requests.

3. Creating a REST API Endpoint to Send MMS Messages

Now, let's create the Express API endpoint that will use our sendMms function to handle incoming MMS requests.

  1. Create the POST /send-mms Route: Add the following route handler to your index.js file, typically after the /health route and before app.listen.

    javascript
    // index.js
    // ... (previous code, including sendMms function) ...
    
    // API Endpoint to send MMS
    app.post('/send-mms', async (req, res) => {
      const { recipient, body, mediaUrl } = req.body;
    
      // --- Basic Input Validation ---
      if (!recipient) {
        return res.status(400).json({ error: 'Recipient phone number is required.' });
      }
      if (!body) {
        return res.status(400).json({ error: 'Message body is required.' });
      }
      if (!mediaUrl) {
        return res.status(400).json({ error: 'Media URL is required.' });
      }
      // Basic E.164 format check (starts with '+', followed by digits)
      if (!/^\+[1-9]\d{1,14}$/.test(recipient)) {
         return res.status(400).json({ error: 'Invalid recipient phone number format. Use E.164 format (e.g., +12025550142).' });
      }
       if (!mediaUrl.startsWith('http://') && !mediaUrl.startsWith('https://')) {
         return res.status(400).json({ error: 'Invalid mediaUrl. Must be a public HTTP or HTTPS URL.' });
      }
      // --- End Basic Input Validation ---
    
    
      try {
        // Call the core sending function
        const messageBirdResponse = await sendMms(recipient, body, mediaUrl);
    
        // Respond with success and the MessageBird response details
        res.status(200).json({
          message: 'MMS sent successfully.',
          details: {
             id: messageBirdResponse.id,
             status: messageBirdResponse.recipients.items[0].status, // Initial status
             recipient: messageBirdResponse.recipients.items[0].recipient
          }
        });
      } catch (error) {
        // Log the error on the server
        console.error('Error in /send-mms route:', error.message);
    
        // Respond with a generic server error
        // Avoid sending detailed internal errors to the client
        res.status(500).json({ error: `Failed to send MMS. ${error.message}` });
      }
    });
    
    // ... (app.listen and other code) ...

    Why async/await? It simplifies handling the Promise returned by our sendMms function, making the asynchronous code look more synchronous and readable. Why status codes? Using appropriate HTTP status codes (200 for success, 400 for bad input, 500 for server errors) is standard practice for REST APIs. Why basic validation here too? While sendMms has some checks, validating input at the API boundary is crucial for security and user feedback before attempting business logic. More robust validation (e.g., using express-validator) is recommended for production. Why return limited details? We return the MessageBird message ID and initial status, which are useful for tracking, but avoid exposing the full internal API response.

  2. Testing the Endpoint: You can test this endpoint using tools like curl or Postman. Make sure your server is running (node index.js).

    Using curl: Replace placeholders with your actual recipient number and a publicly accessible media URL.

    bash
    curl -X POST http://localhost:3000/send-mms \
    -H "Content-Type: application/json" \
    -d '{
      "recipient": "+1xxxxxxxxxx",
      "body": "Check out this cool image!",
      "mediaUrl": "https://www.example.com/path/to/your/public/image.jpg"
    }'

    Expected Success Response (JSON):

    json
    {
      "message": "MMS sent successfully.",
      "details": {
        "id": "mb-message-id-string",
        "status": "sent",
        "recipient": 31612345678
      }
    }

    Expected Error Response (JSON - e.g., Bad Input):

    json
    {
      "error": "Recipient phone number is required."
    }

    (Status Code: 400)

    Expected Error Response (JSON - e.g., API Failure):

    json
    {
       "error": "Failed to send MMS. Failed to send MMS: The originator is incorrect."
    }

    (Status Code: 500)

4. Getting Your MessageBird API Credentials

This section details obtaining and managing the necessary MessageBird credentials for MMS integration.

  1. Obtain API Key:

    • Log in to your MessageBird Dashboard.
    • Navigate to the "Developers" section in the left-hand menu.
    • Click on the "API access" tab.
    • If you don't have a key, click "Add access key".
    • Ensure you are using a Live API Key, not a test key. Copy the key.
  2. Obtain Originator Number/ID:

    • You need a way to identify the sender. This can be:
      • Virtual Mobile Number (VMN): Purchase a number from MessageBird ("Numbers" section -> "Buy a number"). Select one with SMS/MMS capabilities in your desired country. This is generally recommended for MMS.
      • Alphanumeric Sender ID: Apply for one under "Numbers" -> "Sender IDs". Approval is required, and support varies by country (often not supported for MMS or in regions like the US/Canada).
    • Copy the number (in E.164 format, e.g., +12025550142) or the approved Sender ID.
  3. Securely Store Credentials:

    • As configured in Step 1, place your Live API Key and Originator into the .env file:
      dotenv
      # .env
      MESSAGEBIRD_API_KEY=your_copied_live_api_key_here
      MESSAGEBIRD_ORIGINATOR=+1yourpurchasednumber
    • Never commit .env files to version control. Use the .gitignore entry created earlier.
    • For production environments, use your hosting provider's secret management system (e.g., Heroku Config Vars, AWS Secrets Manager, GCP Secret Manager) instead of deploying a .env file.
  4. Understanding Environment Variables:

    • MESSAGEBIRD_API_KEY: (String) Your unique key to authenticate with the MessageBird API. It proves your identity. Obtained from the MessageBird Dashboard's Developer section.
    • MESSAGEBIRD_ORIGINATOR: (String) The sender identifier (phone number in E.164 format or Alphanumeric ID) that will appear on the recipient's device. Obtained from the MessageBird Dashboard's Numbers section (either purchased VMN or approved Sender ID). Must be capable of sending messages in the target country/network.

5. Error Handling and Logging for MMS Applications

Robust applications need to handle failures gracefully and maintain comprehensive logs for troubleshooting.

  1. Error Handling Strategy:

    • API Boundary: Validate inputs at the start of the /send-mms route (as implemented) to catch malformed requests early (4xx errors).
    • Service Layer: Use try...catch blocks around API calls (messagebird.messages.create) to handle network issues or API-specific errors (5xx errors).
    • SDK Callback: The messagebird.messages.create callback provides an err object containing detailed error information from the API (e.g., invalid recipient, insufficient balance, invalid originator). We log this detailed error on the server but return a more generic message to the client.
  2. Logging:

    • Our current implementation uses console.log and console.error. For production, use a dedicated logging library like winston or pino.
    • Key Events to Log:
      • Successful MMS submission (with MessageBird ID).
      • API call failures (with the err object from MessageBird).
      • Input validation errors.
      • Server startup/shutdown.
    • Log Format: Include timestamps, log levels (INFO, ERROR, WARN), and contextual information (e.g., recipient, attempted action). JSON format is often preferred for easier parsing by log analysis tools.
    javascript
    // Example using console (enhance with Winston/Pino for production)
    // Inside the .catch block of /send-mms route
    console.error(`[${new Date().toISOString()}] ERROR: Failed to send MMS for recipient ${recipient}. Reason: ${error.message}`);
    // Inside the .then block (or after await)
    console.info(`[${new Date().toISOString()}] INFO: Successfully submitted MMS to ${recipient}. MessageBird ID: ${messageBirdResponse.id}`);
  3. Retry Mechanisms:

    • Network glitches or temporary MessageBird issues might cause transient failures. Implementing retries can improve reliability.
    • Strategy: Use exponential backoff (wait increasingly longer times between retries) to avoid overwhelming the API. Limit the number of retries.
    • Implementation: Libraries like async-retry or p-retry can simplify this. For this guide, we'll keep it simple and mention the concept.
    • Caveat: Only retry on potentially transient errors (e.g., network timeouts, 5xx errors from MessageBird). Do not retry on permanent errors like invalid API keys (401), invalid recipients (invalid_recipient), or insufficient funds. Check the err.statusCode or err.errors[0].code from the MessageBird response.
    javascript
    // Conceptual Example (Not implemented here, use a library)
    // Inside /send-mms route, replace direct call with retry logic
    /*
    const retry = require('async-retry'); // Example library
    
    try {
      const messageBirdResponse = await retry(
        async bail => {
          try {
            return await sendMms(recipient, body, mediaUrl);
          } catch (error) {
            // Check if error is permanent (e.g., bad request, auth error, insufficient funds)
            const mbError = error.message.includes("MessageBird API Error:") ? JSON.parse(error.message.split(': ')[1]) : null;
            // Check for specific status codes or MessageBird error codes that indicate non-retryable errors
            if (mbError && (mbError.statusCode === 401 || mbError.statusCode === 422 || (mbError.errors && mbError.errors[0].code === 25) )) { // 25 = Insufficient balance
              bail(new Error('Permanent error, not retrying.')); // Stop retrying
            }
            throw error; // Throw error to trigger retry
          }
        },
        {
          retries: 3,       // Attempt 3 times
          factor: 2,        // Exponential backoff factor
          minTimeout: 1000, // Minimum wait 1 second
          onRetry: (error, attempt) => console.warn(`Retrying MMS send (attempt ${attempt}): ${error.message}`)
        }
      );
      // ... success handling ...
    } catch (error) {
       // ... final error handling ...
    }
    */
  4. Testing Error Scenarios:

    • Send requests with missing recipient, body, or mediaUrl (expect 400).
    • Send with an invalid E.164 number (expect 400).
    • Temporarily set an incorrect MESSAGEBIRD_API_KEY in .env (expect 500 with auth error).
    • Use an invalid MESSAGEBIRD_ORIGINATOR (expect 500 with originator error).
    • Use a non-public or malformed mediaUrl (expect 400 or 500 depending on when error is caught).
    • Simulate network issues if possible.

6. Optional: Database Integration for MMS Message Tracking

While not strictly necessary for sending, storing message status or logs locally can be useful for tracking, analytics, or building more complex workflows (like handling status updates via webhooks).

  1. Conceptual Schema (e.g., PostgreSQL):

    sql
    CREATE TABLE mms_log (
        id SERIAL PRIMARY KEY,
        messagebird_id VARCHAR(255) UNIQUE, -- ID returned by MessageBird
        recipient VARCHAR(20) NOT NULL,
        sender VARCHAR(20) NOT NULL, -- The originator used
        body TEXT,
        media_url VARCHAR(2048),
        status VARCHAR(50),          -- e.g., 'submitted', 'sent', 'delivered', 'failed'
        error_code INT,              -- MessageBird error code if failed
        error_description TEXT,      -- MessageBird error description if failed
        submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
        last_updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
    );
    
    -- Index for querying by MessageBird ID (useful for webhooks)
    CREATE INDEX idx_mms_log_messagebird_id ON mms_log(messagebird_id);
    -- Index for querying by recipient
    CREATE INDEX idx_mms_log_recipient ON mms_log(recipient);
  2. Data Access Layer:

    • You would use an ORM like Sequelize or Prisma, or a query builder like Knex.js to interact with the database.
    • Insert on Send: After successfully calling messagebird.messages.create, insert a record into mms_log with the messagebird_id, recipient, initial status ('submitted' or 'sent'), etc.
    • Update via Webhooks (Advanced): Configure MessageBird status report webhooks. Create another API endpoint (e.g., /message-status) to receive these updates. When a webhook arrives, find the corresponding record in mms_log using the messagebird_id and update its status, error_code, etc.

This guide focuses on sending; implementing the database and webhook handling is a separate, more involved task.

7. Security Best Practices for MessageBird MMS APIs

Protecting your API and credentials is vital for maintaining secure MMS operations.

  1. Input Validation and Sanitization:

    • We implemented basic validation. For production, use a dedicated library like express-validator:
      bash
      npm install express-validator
      javascript
      // Example usage in /send-mms route
      const { body, validationResult } = require('express-validator');
      
      app.post('/send-mms',
        // Validation rules
        body('recipient').isMobilePhone('any', { strictMode: true }).withMessage('Invalid E.164 recipient format.'),
        body('body').notEmpty().trim().escape().withMessage('Body cannot be empty.'),
        body('mediaUrl').isURL({ protocols: ['http', 'https'], require_protocol: true }).withMessage('Invalid or non-public media URL.'),
      
        async (req, res) => {
          const errors = validationResult(req);
          if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
          }
          // Access validated data using req.body
          const { recipient, body: validatedBody, mediaUrl } = req.body;
          // ... rest of the logic using validatedBody ...
        }
      );
    • trim() removes whitespace, escape() prevents basic XSS by escaping HTML entities (important if displaying this data later). isMobilePhone with strictMode helps enforce E.164. isURL validates the media link.
  2. Secure API Key Handling:

    • Use environment variables (.env locally, secure config management in production).
    • Never hardcode keys in source code.
    • Restrict API key permissions in the MessageBird dashboard if possible (though MessageBird keys are often account-wide).
  3. Rate Limiting:

    • Prevent abuse and brute-force attacks by limiting the number of requests from a single IP address. Use express-rate-limit:
      bash
      npm install express-rate-limit
      javascript
      // index.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',
        standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
        legacyHeaders: false, // Disable the `X-RateLimit-*` headers
      });
      
      // Apply the rate limiting middleware to API routes
      app.use('/send-mms', apiLimiter); // Apply specifically to the send route
      
      // ... rest of the code ...
  4. Webhook Security (If Implementing Status Updates):

    • MessageBird signs webhook requests using JWT (JSON Web Tokens) in the MessageBird-Signature-JWT header.
    • Verify these signatures to ensure requests genuinely come from MessageBird. The messagebird-nodejs SDK provides a helper function.
    • You'll need your Webhook Signing Key from the MessageBird Dashboard (Developers -> API Settings -> Webhook Signing Key). Store this securely like your API key (e.g., in .env locally using a variable like MESSAGEBIRD_WEBHOOK_SIGNING_KEY, or using production secrets management).
    javascript
    // Conceptual Webhook Handler (Requires signing key in env: MESSAGEBIRD_WEBHOOK_SIGNING_KEY)
    /*
    app.post('/message-status', express.raw({ type: 'application/json' }), (req, res) => { // Use raw body parser
      const signature = req.headers['messagebird-signature-jwt'];
      const webhookSigningKey = process.env.MESSAGEBIRD_WEBHOOK_SIGNING_KEY;
      const timestamp = req.headers['messagebird-request-timestamp']; // Optional but recommended check
      const queryParams = req.query; // Include query params if any
    
      if (!webhookSigningKey) {
         console.error("Webhook signing key not configured.");
         return res.status(500).send('Internal configuration error.');
      }
    
      try {
        // The verifyWebhookSignature needs the RAW request body buffer
        const decodedPayload = messagebird.webhooks.verifyWebhookSignature(
            req.body, // Raw body buffer
            queryParams,
            signature,
            webhookSigningKey
        );
    
        // Optional: Check timestamp tolerance (e.g., reject if older than 5 minutes)
        const now = Math.floor(Date.now() / 1000);
        if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
            console.warn('Received webhook with stale timestamp.');
            return res.status(400).send('Stale timestamp');
        }
    
        // Process the verified webhook payload (decodedPayload)
        console.log('Verified webhook payload:', decodedPayload);
        // ... update database based on decodedPayload.status, decodedPayload.id ...
    
        res.status(200).send('OK'); // Acknowledge receipt
    
      } catch (error) {
        console.error('Webhook signature verification failed:', error.message);
        res.status(400).send('Invalid signature');
      }
    });
    */

    Why express.raw? Signature verification must happen on the raw, unmodified request body. express.json() parses the body, which alters it.

8. MessageBird MMS Requirements and Limitations

MMS sending has specific nuances and strict technical requirements:

  1. Geographic Restrictions: MessageBird MMS is only available for sending within the United States and Canada (as of 2024-2025). Attempting to send MMS to recipients in other countries will fail. If you need international MMS, consider alternative providers.

  2. Media URL Accessibility and Timeout: The mediaUrl must point to a resource publicly accessible on the internet without authentication. Critical: MessageBird's servers must be able to fetch the file within 5 seconds. URLs that take longer to respond will cause the MMS to fail. Private S3 buckets, password-protected URLs, or localhost URLs will not work.

  3. Media File Types and Sizes (Verified Specifications):

    • Maximum file size: 1MB (1,024KB) per attachment
    • Maximum attachments: 10 per message
    • Supported image formats: JPEG, PNG, GIF, BMP
    • Supported video formats: MP4, WebM, QuickTime (.mov)
    • Supported audio formats: MP3, OGG
    • Supported document formats: PDF, vCard (.vcf), CSV, RTF
    • Sending unsupported file types or files exceeding 1MB will result in API errors.
  4. Character Limits (Verified Specifications):

    • Message body: 2,000 characters maximum (not just "practical limits")
    • Subject line: 256 characters maximum (if using the optional subject parameter)
    • Messages exceeding these limits will be rejected by the API.
  5. Recipient Limits: Maximum of 50 recipients per API request. For larger distributions, batch your requests.

  6. Originator Restrictions: Alphanumeric Sender IDs are not supported for MMS in North America (US/Canada). You must use a purchased MessageBird Virtual Mobile Number (VMN) with MMS capabilities. Attempting to send MMS from an alphanumeric sender ID will fail.

  7. Recipient Country/Carrier Support: MMS rendering varies between mobile carriers. Some carriers may convert MMS to SMS with a link to view media. Always test with your target carriers before production deployment.

  8. Character Encoding: Ensure your text body uses UTF-8 encoding. Special characters, emojis, or non-standard character sets may render incorrectly or increase character count.

9. Performance Optimization for High-Volume MMS Sending

For high-volume sending, consider these optimization strategies to maximize throughput and reliability.

  1. Asynchronous Operations: Node.js is inherently non-blocking. Our use of async/await and callbacks ensures the server isn't blocked waiting for MessageBird's API response, allowing it to handle other requests concurrently.
  2. Connection Pooling: The MessageBird SDK likely handles underlying HTTP connection management. Ensure your Node.js process has sufficient resources (memory, CPU).
  3. Media File Optimization: Use reasonably sized and compressed media files. Smaller files transfer faster and are less likely to hit size limits.
  4. Payload Size: Keep the JSON request payload to your API minimal.
  5. Load Testing: Use tools like k6, Artillery, or ApacheBench to simulate high traffic and identify bottlenecks in your API endpoint or the downstream MessageBird service. Monitor response times and error rates under load.
  6. Horizontal Scaling: Deploy multiple instances of your Node.js application behind a load balancer to distribute traffic.

10. Monitoring and Analytics for MMS Delivery

Understanding how your service behaves in production is crucial for maintaining reliable MMS delivery.

  1. Health Checks: The /health endpoint is a basic start. Production health checks might verify database connectivity or dependency availability.

  2. Performance Metrics: Monitor key metrics:

    • Request latency (average, p95, p99) for the /send-mms endpoint.
    • Request rate (requests per second/minute).
    • Error rates (HTTP 4xx, 5xx).
    • Node.js process metrics (CPU usage, memory usage, event loop lag).
    • Use tools like Prometheus/Grafana, Datadog, or New Relic.
  3. Error Tracking: Integrate services like Sentry or Bugsnag. They automatically capture unhandled exceptions and provide detailed context (stack traces, request data) for faster debugging.

  4. Logging Aggregation: Ship logs (using Winston/Pino) to a centralized logging platform (e.g., Elasticsearch/Logstash/Kibana (ELK), Datadog Logs, Splunk) for searching, analysis, and alerting.

  5. MessageBird Dashboard: Regularly check the MessageBird Message Logs for detailed delivery statuses, error codes, and cost information. This is invaluable for troubleshooting specific message failures.

  6. Alerting: Set up alerts based on key metrics (e.g., high error rate, increased latency, low message success rate reported by webhooks/logs) using your monitoring or logging platform.

11. Troubleshooting Common MessageBird MMS Integration Issues

Common issues developers encounter when implementing MessageBird MMS:

  1. Error: require(...) is not a function or initClient is not a function

    • Cause: Incorrectly initializing the MessageBird SDK.
    • Solution: Ensure you use require('messagebird').initClient('<YOUR_ACCESS_KEY>'); as shown in the documentation and examples.
  2. Error: Request not allowed (incorrect access_key) (Code: 2)

    • Cause: Invalid or incorrect MESSAGEBIRD_API_KEY used. It might be a test key instead of live, mistyped, or lack permissions.
    • Solution: Double-check your .env file. Ensure you copied the full Live API key from the MessageBird Dashboard (not the Test key). Regenerate the key if necessary.
  3. Error: The originator is incorrect (Code: 9)

    • Cause: The MESSAGEBIRD_ORIGINATOR is invalid, not registered with MessageBird, or not suitable for the destination country (e.g., using an Alphanumeric ID where a VMN is required for MMS).
    • Solution: Verify the originator value in .env. For MMS in US/Canada, ensure you are using a purchased MessageBird Virtual Mobile Number with MMS support, not an Alphanumeric Sender ID. Check the number's capabilities in the MessageBird Dashboard.
  4. Error: recipient is invalid (Code: 21)

    • Cause: The recipient phone number is incorrectly formatted or not a valid mobile number.
    • Solution: Ensure the recipient number is in E.164 format (+[country code][number]). Validate the number format in your code before calling the API.
  5. Error: Media URL is unreachable or timeout errors

    • Cause: MessageBird cannot fetch the media from the provided mediaUrl within the 5-second timeout. This can be due to:
      • The URL is not publicly accessible (localhost, private network, behind authentication).
      • The server hosting the media is slow or unresponsive.
      • The URL is incorrect or points to a non-existent resource.
    • Solution: Verify the mediaUrl is fully public. Test it in a browser or using curl. Use a CDN or optimized storage (e.g., AWS S3, Google Cloud Storage, Azure Blob Storage) with fast response times. Ensure the media file is served with correct MIME types and headers.
  6. MMS not rendering correctly or received as SMS with a link

    • Cause: Some mobile carriers or devices do not fully support MMS or have restrictions on media formats/sizes. Older devices may fallback to SMS.
    • Solution: Test with multiple carriers and devices in your target market. Ensure media files are within size limits (1MB) and use widely supported formats (JPEG, PNG for images). Some carriers automatically convert MMS to a web link viewable in a browser.
  7. Error: Insufficient balance (Code: 25)

    • Cause: Your MessageBird account does not have enough credit to send the MMS.
    • Solution: Top up your MessageBird account balance via the Dashboard.
  8. Geographic restrictions - MMS failing for non-US/Canada recipients

    • Cause: MessageBird MMS is only available for the US and Canada.
    • Solution: If targeting other countries, you must use a different provider or MessageBird's standard SMS (without media, or use SMS with a shortened link to the media hosted on your server/CDN).

Frequently Asked Questions

How to send MMS messages with Node.js?

Use the MessageBird Node.js SDK with Express. Create an API endpoint that accepts recipient details, message body, and media URL, then uses the SDK's `messagebird.messages.create` method to send the MMS via the MessageBird API. Ensure your media is publicly accessible and you have a valid MessageBird API key and originator (VMN or Alphanumeric Sender ID).

What is MessageBird's mms object parameter?

The 'mms' object is a required parameter in the MessageBird API's `messagebird.messages.create` method when sending MMS messages. It nests MMS-specific parameters like 'mediaUrls', which is an array of public URLs pointing to your media files (images, GIFs, etc.).

Why does MessageBird need a public media URL for MMS?

MessageBird's servers must directly access the media file to include it in the MMS message. Therefore, the URL you provide cannot be behind authentication or restricted access (e.g., private cloud storage, localhost). Use publicly accessible URLs like those from cloud storage services with public read permissions.

When should I use a VMN vs. Alphanumeric Sender ID with MessageBird?

While both can be used as originators (senders), a Virtual Mobile Number (VMN) is generally recommended for MMS, especially in regions like North America where Alphanumeric Sender IDs are often not supported for MMS. VMNs also provide better two-way communication capabilities.

Can I send MMS messages from a localhost URL?

No, you cannot directly use localhost URLs for the MMS media because MessageBird's servers need to access the media from a publicly available URL. Host the media on a publicly accessible server or cloud storage service with appropriate read permissions.

How to set up MessageBird API key for MMS?

Obtain your live API key from the MessageBird Dashboard (Developers -> API access). Store this key securely, typically in a '.env' file locally or using a secrets management system in production. Never hardcode API keys in your codebase. Load this key into your application using `dotenv.config()` and initialize the MessageBird SDK client with `messagebird.initClient(process.env.MESSAGEBIRD_API_KEY)`.

What is the best way to handle MessageBird API errors in Node.js?

Implement a robust error handling strategy at multiple levels: validate inputs at the API endpoint level, use try-catch blocks around `messagebird.messages.create`, and handle the callback's error object. Log errors comprehensively using a dedicated logging library, and consider retry mechanisms for transient errors (network issues, 5xx server errors), but avoid retrying permanent errors like invalid API keys or recipients.

How to implement rate limiting for MessageBird MMS API?

Use the `express-rate-limit` middleware in your Express app. This middleware lets you limit the number of requests from a single IP within a timeframe, preventing abuse. Apply it to the /send-mms route to throttle excessive MMS sending attempts.

How to verify MessageBird webhook signatures for security?

Use the `messagebird.webhooks.verifyWebhookSignature` function provided by the MessageBird Node.js SDK. This function verifies JWT signatures in webhook requests, confirming their authenticity. Obtain your Webhook Signing Key from the MessageBird dashboard and store it securely, like your API Key. Check the `MessageBird-Signature-JWT` header for the JWT signature, and `MessageBird-Request-Timestamp` header to prevent replay attacks.

Why use dotenv for MessageBird API Key?

Dotenv allows you to load environment variables from a `.env` file. This is crucial for securely managing sensitive information like your MessageBird API key without committing it to your code repository. It keeps your API keys separate from your code, improving security and making it easier to manage credentials across different environments.

How to structure a Node.js project for sending MMS with MessageBird?

Initialize a Node.js project with `npm init -y`, install necessary dependencies (`express`, `messagebird`, `dotenv`), and create an `index.js` file for your main application logic. Set up an Express server, create a route to handle MMS sending (e.g. `/send-mms`), include error handling, and validate inputs. Store your API key in a `.env` file, and ensure it’s added to `.gitignore`.

What are common MessageBird API error codes and their meanings?

Error codes like 'incorrect access_key' (Code: 2) indicate an invalid or incorrect API key. 'Invalid recipient' points to incorrect phone number formatting or unsupported carriers. Check the MessageBird documentation for detailed explanations of all error codes, including status codes and error-specific codes within the 'errors' array in the API response. Also always log the complete error object returned from MessageBird calls

How to optimize media files for MMS sending with MessageBird?

Use reasonably sized and compressed media files. Check MessageBird's documentation for supported file types (JPEG, PNG, GIF, etc.) and maximum size limits. Smaller files transfer faster and reduce the chance of hitting size limits, improving the sending process's performance.

What are best practices for logging MessageBird MMS events?

Use a dedicated logging library like Winston or Pino. Log key events like successful MMS submissions (with MessageBird ID), API failures (with the full error object), input validation errors, and server start/shutdown events. Include timestamps, log levels (INFO, ERROR, WARN), and contextual information in a structured format, preferably JSON, for easy parsing by log analysis tools.