code examples
code examples
Send MMS with Node.js, Express & MessageBird: Complete Tutorial (2025)
Learn how to send MMS multimedia messages using Node.js, Express, and MessageBird API. Step-by-step tutorial covering setup, file size limits, carrier specifications, error handling, and production deployment.
Send MMS with Node.js, Express & MessageBird: Complete Tutorial (2025)
Build a production-ready Node.js MMS API using Express and MessageBird to send multimedia messages (images, GIFs, videos) programmatically. This tutorial covers MessageBird MMS integration with Node.js 22, Express.js, and the MessageBird Node.js SDK. You'll create a robust API endpoint that handles multimedia message delivery with proper file size validation, carrier-specific limits, error handling, and security best practices for production environments.
By the end, you'll have a functional MMS API endpoint (POST /send-mms) that accepts recipient phone numbers, message text, and media URLs (images, GIFs, videos, PDFs) to dispatch multimedia messages through MessageBird's infrastructure. This forms a foundational building block for Node.js applications requiring rich media notifications, marketing campaigns, customer engagement, or multimedia communication features. This assumes basic understanding of Node.js, npm (or yarn), and REST APIs.
Project Overview and Goals
- Goal: Build a Node.js Express server with a single API endpoint (
POST /send-mms). The endpoint receives a recipient phone number, optional message body, and media URL, then uses the MessageBird Node.js SDK to send an MMS message to the recipient. - Problem Solved: Send rich media content (images, GIFs, videos) alongside text messages programmatically from your applications using MessageBird's messaging infrastructure.
- Technologies:
- Node.js: JavaScript runtime environment for server-side applications.
- Express.js: Minimal, flexible Node.js web framework for creating API endpoints.
- Vite: Modern frontend build tool. As of 2025, Vite 7.1 is current, requiring Node.js 20.19+ or 22.12+. Vite 7 improved the Environment API and updated browser targets to "baseline-widely-available" standards.
- React: JavaScript library for building user interfaces. As of 2025, React 19.2 is current (released October 2025), featuring the Activity component, enhanced Chrome DevTools integration, and improved performance profiling.
- Vue: Progressive JavaScript framework. As of 2025, Vue 3.5.22 is current, with Vue 3.6 alpha introducing experimental Vapor Mode for reduced bundle sizes and improved performance.
- MessageBird Node.js SDK: Simplifies interaction with the MessageBird REST API for sending messages.
- dotenv: Module to load environment variables from a
.envfile intoprocess.env, keeping sensitive credentials out of the codebase. - (Optional) ngrok/localtunnel: Test webhooks locally if extending this application to receive messages (not the primary focus of sending MMS).
System Architecture:
graph TD
A[User/Client Application] -- HTTP POST Request --> B(Node.js/Express API Endpoint /send-mms);
B -- Uses MessageBird SDK --> C(MessageBird API);
C -- Delivers MMS --> D(Recipient's Mobile Device);
B -- Sends API Response --> A;Prerequisites:
- Node.js LTS version. As of 2025, Node.js 22 is current (Active LTS until October 2025, Maintenance LTS until April 2027). Node.js 20.19+ remains supported. Vite 7 requires Node.js 20.19+ or 22.12+ minimum (Node.js 18 reached EOL in April 2025). Run
node --versionto check your installed version. - A MessageBird account (sign up at MessageBird.com).
- A MessageBird API Key (Live Access Key).
- A MessageBird purchased number capable of sending MMS (check capabilities in the Dashboard). Virtual Mobile Numbers (VMN) or Toll-Free Numbers often support this, but check country/carrier restrictions.
- A publicly accessible URL for the media file you want to send (e.g., hosted on a CDN, S3 bucket, or public server). MessageBird needs to fetch the media from this URL.
1. Setting up the Project
Initialize your Node.js project and install dependencies.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for your project, then navigate into it.
bashmkdir node-messagebird-mms cd node-messagebird-mms -
Initialize Node.js Project: Create a
package.jsonfile to manage dependencies and scripts.bashnpm init -y -
Install Dependencies: Install Express, the MessageBird SDK, and dotenv.
bashnpm install express messagebird dotenvexpress: Web framework.messagebird: Official SDK for interacting with the MessageBird API.dotenv: Manages environment variables securely.
-
Create Project Structure: Create the main application file and environment variable files.
bashtouch index.js .env .env.example .gitignoreindex.js: Main application code..env: Stores sensitive credentials (API Key, Originator Number). Never commit this file to version control..env.example: Template showing required environment variables (safe to commit)..gitignore: Specifies files/directories Git should ignore.
-
Configure
.gitignore: Addnode_modulesand.envto prevent committing them.plaintext# .gitignore node_modules/ .env *.log -
Configure
.env.example: Add placeholders for required environment variables.plaintext# .env.example MESSAGEBIRD_ACCESS_KEY=YOUR_MESSAGEBIRD_LIVE_API_KEY MESSAGEBIRD_ORIGINATOR=YOUR_MESSAGEBIRD_MMS_ENABLED_NUMBER_OR_ALPHANUMERIC_SENDER_IDMESSAGEBIRD_ACCESS_KEY: Your Live API key from the MessageBird dashboard.MESSAGEBIRD_ORIGINATOR: Phone number (in E.164 format, e.g., +12025550144) or approved Alphanumeric Sender ID you purchased/configured in MessageBird that's enabled for MMS.
-
Configure
.env: Create a.envfile by copying.env.exampleand filling in your actual credentials.bashcp .env.example .envEdit
.envwith your real MessageBird Live API Key and Originator Number.plaintext# .env MESSAGEBIRD_ACCESS_KEY=live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx MESSAGEBIRD_ORIGINATOR=+12025550144Security: Keep your
.envfile secure and never share it publicly.
2. Implementing Core MMS Sending Functionality
Integrate the MessageBird SDK to handle MMS sending logic.
-
Obtaining MessageBird API Key:
- Log in to your MessageBird Dashboard.
- Navigate to the Developers section in the left-hand menu.
- Click the API access tab.
- If you don't have a key, click Add access key. Create or use a Live key, not a test key, for sending actual messages.
- Copy the generated Live access key.
-
Obtaining MessageBird Originator:
- In the MessageBird Dashboard, navigate to Numbers.
- Purchase a number if you don't have one. Ensure the number's capabilities include MMS for target countries. Virtual Mobile Numbers (VMN) or Toll-Free Numbers are common choices.
- Copy the number in E.164 format (e.g.,
+12025550144). - Alternatively, if approved and supported for MMS in the destination country, use an Alphanumeric Sender ID (max 11 characters). However, phone numbers are generally more reliable for MMS.
-
Initialize SDK and Load Environment Variables: Open
index.jsand require dependencies, then initialize the MessageBird client using the API key from your environment variables.javascript// index.js 'use strict'; // Load environment variables from .env file require('dotenv').config(); const express = require('express'); const { initClient } = require('messagebird'); // --- Initialization --- const app = express(); const port = process.env.PORT || 3000; // Use environment port or default to 3000 // Validate essential environment variables if (!process.env.MESSAGEBIRD_ACCESS_KEY || !process.env.MESSAGEBIRD_ORIGINATOR) { console.error(""FATAL ERROR: MESSAGEBIRD_ACCESS_KEY or MESSAGEBIRD_ORIGINATOR is not defined in the .env file.""); console.error(""Please ensure you have a .env file with your MessageBird credentials.""); process.exit(1); // Exit if critical configuration is missing } // Initialize MessageBird client let messagebird; try { messagebird = initClient(process.env.MESSAGEBIRD_ACCESS_KEY); console.log(""MessageBird SDK initialized successfully.""); } catch (error) { console.error(""Failed to initialize MessageBird SDK:"", error); process.exit(1); // Exit if SDK initialization fails } // --- Middleware --- // Enable Express to parse JSON request bodies app.use(express.json()); // Simple request logger middleware (optional but helpful) app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); // --- Routes --- // (API route will be added in the next section) // --- Start Server --- // (Server starting logic will be shown in the complete example, // including assignment to 'server' variable for graceful shutdown) const server = app.listen(port, () => { console.log(`Server listening on port ${port}`); console.log(`Using Originator: ${process.env.MESSAGEBIRD_ORIGINATOR}`); }); // --- Graceful Shutdown (Example) --- const gracefulShutdown = (signal) => { console.log(`\nReceived ${signal}. Closing HTTP server.`); server.close(() => { console.log('HTTP server closed.'); // Add any other cleanup here (e.g., database connections) process.exit(0); }); }; process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT'));- Load
dotenvfirst to make environment variables available. - Initialize Express.
- Validate essential environment variables before initializing the SDK.
- Use
initClientfrom themessagebirdpackage, passing the API key. A try-catch block handles potential initialization errors. - Add
express.json()middleware to parse incoming JSON requests. - Include a basic request logger for visibility.
- Start the server listening on the configured port and assign it to
server. - Add basic graceful shutdown handlers for
SIGTERMandSIGINT.
- Load
3. Building the API Layer
Create the /send-mms endpoint.
-
Define the POST Route: Add the following route handler within the
// --- Routes ---section inindex.js.javascript// index.js (continued) // --- Routes --- /** * POST /send-mms * Sends an MMS message using MessageBird. * Request Body (JSON): * { * ""recipient"": ""+12025550189"", // E.164 format required * ""messageBody"": ""Check out this cool picture!"", // Optional text body * ""mediaUrl"": ""https://www.example.com/path/to/your/image.jpg"" // Publicly accessible media URL * } */ app.post('/send-mms', (req, res) => { const { recipient, messageBody, mediaUrl } = req.body; // --- Basic Input Validation --- if (!recipient) { return res.status(400).json({ error: 'Recipient phone number is required.' }); } // Basic E.164 format check (improve as needed for production) if (!/^\+[1-9]\d{1,14}$/.test(recipient)) { return res.status(400).json({ error: 'Invalid recipient phone number format. Use E.164 (e.g., +12025550144).' }); } if (!mediaUrl) { return res.status(400).json({ error: 'Media URL is required for MMS.' }); } // Basic URL validation (improve as needed for production) try { new URL(mediaUrl); } catch (_) { return res.status(400).json({ error: 'Invalid media URL format.' }); } // --- Prepare MessageBird Payload --- const params = { originator: process.env.MESSAGEBIRD_ORIGINATOR, recipients: [recipient], // Must be an array // Body is optional for MMS but often useful body: messageBody || `MMS message sent at ${new Date().toLocaleTimeString()}`, // Provide a default or make it optional // --- MMS Specific Parameters --- type: 'binary', // Often 'binary' works for common image types. Check MessageBird docs if issues arise. // 'premium' might be needed for specific carrier routes or features. mediaUrls: [mediaUrl], // Must be an array of publicly accessible URLs // You can add more optional parameters here if needed (e.g., validity, scheduledDatetime) // referenceUrl: 'YourOptionalReference_12345', // reportUrl: 'https://your-app.com/messagebird-dlr-webhook' // If you want delivery reports }; console.log(`Attempting to send MMS to ${recipient} with media: ${mediaUrl}`); // Log the payload for debugging (mask sensitive data like recipient in production logs) console.log('Payload:', JSON.stringify({ ...params, recipients: ['[REDACTED]'] }, null, 2)); // --- Send the Message via MessageBird --- messagebird.messages.create(params, (err, response) => { if (err) { // --- Handle MessageBird API Errors --- console.error(""MessageBird API Error:"", err); // Log the full error object for details // Provide more context if available in err object let statusCode = 500; let errorMessage = 'Failed to send MMS due to an internal server error.'; if (err.errors && err.errors.length > 0) { // Use the first error for the response message errorMessage = `MessageBird API Error: ${err.errors[0].description} (Code: ${err.errors[0].code})`; // Map specific MessageBird error codes to 4xx status codes if appropriate if ([2, 9, 10, 21, 22, 25].includes(err.errors[0].code)) { // Example: Auth, Bad request, Invalid originator/recipient codes statusCode = 400; } else if (err.errors[0].code === 7) { // Insufficient balance statusCode = 402; // Payment Required } } return res.status(statusCode).json({ error: errorMessage, details: err.errors }); // Return structured errors } // --- Handle Success --- console.log(""MessageBird API Success:"", response); // Log the success response object // The response object contains details about the message(s) sent // e.g., response.id, response.recipients.totalSentCount res.status(200).json({ message: `MMS potentially sent to ${response.recipients.totalSentCount} recipient(s).`, messageId: response.id, status: response.recipients.items[0].status, // Status of the first recipient (e.g., 'accepted', 'scheduled') details: { // Provide a subset of useful details id: response.id, href: response.href, direction: response.direction, type: response.type, originator: response.originator, body: response.body, // May be truncated or absent depending on API version/response reference: response.reference, validity: response.validity, gateway: response.gateway, typeDetails: response.typeDetails, datacoding: response.datacoding, mclass: response.mclass, scheduledDatetime: response.scheduledDatetime, createdDatetime: response.createdDatetime, recipients: { totalCount: response.recipients.totalCount, totalSentCount: response.recipients.totalSentCount, totalDeliveredCount: response.recipients.totalDeliveredCount, totalDeliveryFailedCount: response.recipients.totalDeliveryFailedCount, items: response.recipients.items.map(item => ({ recipient: item.recipient, // Mask in production if needed status: item.status, statusDatetime: item.statusDatetime, messagePartCount: item.messagePartCount, // Omit price details unless necessary })) } } }); }); }); // --- Error Handling Middleware (Basic Catch-All) --- // This should be defined AFTER all other app.use() and routes app.use((err, req, res, next) => { console.error(""Unhandled Error:"", err.stack || err); // Avoid sending stack trace to client in production const status = err.status || 500; const message = process.env.NODE_ENV === 'production' ? 'Something broke on the server!' : err.message; res.status(status).json({ error: message }); });- The route listens for
POSTrequests on/send-mms. - It extracts
recipient,messageBody, andmediaUrlfrom the JSON request body (req.body). - Basic Input Validation: Checks if required fields are present and performs rudimentary format validation (E.164 for recipient, basic URL check). Robust validation is crucial for production.
- Payload Preparation: Constructs the
paramsobject required bymessagebird.messages.create.originatorcomes from environment variables.recipientsmust be an array, even for one number.bodyis included (optional but good practice).typeis set to'binary'. This usually works for images/GIFs. If you encounter issues with specific carriers or media types, consult MessageBird documentation;'premium'might be required.mediaUrlsis an array containing the public URL of the media file.- Optional parameters like
reportUrl(for delivery reports) orreferenceUrlcan be added.
- Logging: The code logs the attempt and the payload (recipient number redacted in the example log).
- Sending:
messagebird.messages.create(params, callback)sends the request. - Callback Handling:
- If
errexists, it logs the error and sends an appropriate error response (500 or potentially 400/402 based on the MessageBird error code). Returns structured error details. - If successful (
responseexists), it logs the success and sends a 200 response with a structured subset of details from the MessageBird response.
- If
- Catch-All Error Handler: A final middleware catches any unhandled errors in the request lifecycle, avoiding stack traces in production.
- The route listens for
4. Integrating with Third-Party Services (MessageBird)
MessageBird-specific configuration:
- Configuration: Handle via the
.envfile (MESSAGEBIRD_ACCESS_KEY,MESSAGEBIRD_ORIGINATOR). - API Key Security:
dotenvloads the key intoprocess.env, preventing hardcoding. Exclude the.envfile from Git via.gitignore. Ensure server-level permissions restrict access to this file. - SDK Initialization:
const messagebird = initClient(process.env.MESSAGEBIRD_ACCESS_KEY);securely uses the key. - Dashboard Navigation:
- API Key: Dashboard → Developers → API access → Add/View access key (Use Live).
- Originator Number: Dashboard → Numbers → Buy a Number (Ensure MMS capability) or Manage Numbers.
- Environment Variables:
MESSAGEBIRD_ACCESS_KEY: Your Live API key (e.g.,live_xxxxxxxx). Obtain from Dashboard → Developers → API access. Authenticates API requests.MESSAGEBIRD_ORIGINATOR: Your purchased/configured MessageBird number or Alphanumeric Sender ID enabled for MMS (e.g.,+12025550144). Obtain from Dashboard → Numbers. Used as the "From" address for the MMS.
5. Error Handling, Logging, and Retry Mechanisms
- Error Handling Strategy:
- Input Validation: Catch invalid requests early (missing fields, bad formats) and return 400 errors.
- SDK Initialization: Check for errors during
initClientand exit gracefully if critical. - API Call Errors: The
messagebird.messages.createcallback receives anerrobject. Inspecterr.errorsfor specific MessageBird issues. Map MessageBird errors to appropriate HTTP status codes (e.g., auth errors -> 400/401, balance -> 402, rate limits -> 429, server errors -> 500/502). - Unhandled Errors: A global Express error handler catches unexpected exceptions.
- Logging:
- Use
console.logandconsole.errorfor simplicity in this example. For production, use a dedicated logging library (like Winston or Pino) to:- Log to files or external services.
- Set log levels (info, warn, error).
- Use structured logging (JSON format) for easier parsing.
- Mask sensitive data (like recipient numbers, API keys if accidentally logged).
- Log key events: server start, incoming requests, payload before sending (masked), API success responses, API errors (full error object).
- Use
- Retry Mechanisms:
- The current implementation does not include automatic retries for the API call itself. MessageBird handles some level of retries on their end for delivery to the handset.
- For critical applications, if the API call to MessageBird fails due to transient issues (e.g., network timeout, temporary MessageBird 5xx error), you could implement a retry strategy within your Express app:
- Use libraries like
async-retry. - Implement exponential backoff (wait longer between retries).
- Limit the number of retries.
- Only retry on transient errors (5xx, network issues, specific retryable MessageBird codes), not permanent ones (4xx like invalid recipient, invalid key).
- Use libraries like
- Example Concept (using async-retry):
javascript
// Conceptual - requires installing 'async-retry': npm install async-retry const retry = require('async-retry'); // Inside the /send-mms route handler, wrap the messagebird.messages.create call: try { const response = await retry(async (bail, attempt) => { // bail(error) is called to stop retrying if the error is permanent console.log(`Attempting MessageBird API call (attempt ${attempt})...`); return new Promise((resolve, reject) => { messagebird.messages.create(params, (err, res) => { if (err) { console.warn(`MessageBird API call failed (attempt ${attempt}):`, err); // Example: Don't retry on specific permanent MessageBird errors const isPermanentError = err.errors && err.errors[0] && [2, 9, 10, 21, 22, 25, 7].includes(err.errors[0].code); // Add other permanent codes if (isPermanentError) { // Stop retrying for permanent errors bail(new Error(`Permanent MessageBird Error (Code ${err.errors[0].code}): ${err.errors[0].description}`)); return; // Important: return after calling bail } // Otherwise, reject to trigger retry for transient errors return reject(err); } // Resolve successfully resolve(res); }); }); }, { retries: 3, // Number of retries (e.g., 3 retries means 4 total attempts) factor: 2, // Exponential backoff factor (e.g., 1s, 2s, 4s) minTimeout: 1000, // Initial delay in ms maxTimeout: 10000, // Maximum delay in ms onRetry: (error, attempt) => console.warn(`Retrying API call (attempt ${attempt}) due to: ${error.message || error}`) }); // --- Handle Success (after successful try or retry) --- console.log(""MessageBird API Success (after retries if any):"", response); // Send success response (as shown previously) res.status(200).json({ /* ... success response structure ... */ }); } catch (err) { // --- Handle Final Error (after retries exhausted or permanent error) --- console.error(""Failed to send MMS after retries or due to permanent error:"", err); // Determine statusCode and errorMessage based on the final error (err might be the original permanent error or the last transient error) let finalStatusCode = 500; let finalErrorMessage = 'Failed to send MMS after multiple attempts.'; if (err.message && err.message.startsWith('Permanent MessageBird Error')) { finalErrorMessage = err.message; // Extract code if needed to set status code, e.g., parse from message if (err.message.includes('(Code 7)')) finalStatusCode = 402; else finalStatusCode = 400; } else if (err.errors && err.errors.length > 0) { // If it was the last transient error object finalErrorMessage = `MessageBird API Error: ${err.errors[0].description} (Code: ${err.errors[0].code})`; // Potentially map transient error codes if needed } res.status(finalStatusCode).json({ error: finalErrorMessage, details: err.errors || err.message }); }
6. Database Schema and Data Layer (Not Applicable)
This specific guide focuses solely on the transient action of sending an MMS and does not require a database. If you were building a system to track sent messages, manage templates, handle inbound replies, or correlate delivery reports, you would integrate a database (e.g., PostgreSQL, MongoDB) with an ORM/ODM (like Prisma, Sequelize, Mongoose) here. This would involve defining schemas, migrations, and data access functions.
7. Security Features
- Input Validation: Already implemented basic checks. For production:
- Use a robust validation library (like
joi,zod,express-validator). - Strictly validate phone number formats (E.164).
- Validate URL formats thoroughly. Consider using
URLconstructor and checking protocols (http,https). - Potentially add checks to prevent Server-Side Request Forgery (SSRF) if
mediaUrlcould be user-controlled in a broader context (e.g., allowlist domains, disallow private IPs). MessageBird's fetch might mitigate some risks, but validating input is best practice.
- Use a robust validation library (like
- API Key Security: Handled via
.envand.gitignore. Ensure the server environment where this runs is secure. Consider secrets management solutions (like HashiCorp Vault, AWS Secrets Manager, Doppler, platform-native secrets) for production. - Rate Limiting: Protect the
/send-mmsendpoint from abuse. Use libraries likeexpress-rate-limit.javascript// Example: Basic rate limiting // Ensure you have run: npm install express-rate-limit const rateLimit = require('express-rate-limit'); const mmsLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs (adjust as needed) message: { error: 'Too many MMS 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 limiter to the specific route // Place this line *before* the route definition in index.js app.use('/send-mms', mmsLimiter); - Authentication/Authorization: The current API is open. For real applications, protect it:
- Implement API keys, JWT tokens, OAuth, or other mechanisms appropriate for your use case.
- Add middleware before the route handler (and before the rate limiter if the limit is per-user) to verify credentials.
- HTTPS: Always run your Node.js application behind a reverse proxy (like Nginx, Caddy, Traefik, or cloud provider load balancers) that handles TLS/SSL termination, ensuring traffic is encrypted via HTTPS. Configure security headers (e.g., HSTS).
- Dependency Security: Regularly audit dependencies for known vulnerabilities (
npm auditoryarn audit) and update them. Use tools like Snyk or Dependabot. - Helmet: Use the
helmetmiddleware for Express to set various HTTP headers for security.javascript// npm install helmet const helmet = require('helmet'); app.use(helmet());
8. Handling Special Cases (MMS Specific)
- Media URL Accessibility: The
mediaUrlmust be publicly accessible without authentication for MessageBird servers to fetch it. Private URLs, URLs requiring logins, or localhost URLs will fail. - Media File Size/Type: MessageBird and downstream carriers impose limits on media file sizes and supported types. Carrier-specific limits as of 2025:
- Optimal Deliverability: Keep files under 500 KB to ensure delivery across all carriers. For time-sensitive campaigns, limit to 150 KB.
- Industry Standard: All operators reliably handle up to 300 KB, with most US and Canadian carriers supporting up to 1 MB.
- AT&T: Accepts media up to 1 MB.
- Verizon: Accepts images up to 1.2 MB and videos up to 3.5 MB (general media limit: 1.7 MB).
- T-Mobile: Accepts media up to 3 MB.
- Supported Formats: Common formats like JPEG, PNG, GIF, MP4, and PDF are typically supported. Sending unsupported or oversized files will fail, often with generic errors. Test with target carriers for specific requirements.
- Character Limits: While MMS supports longer text than SMS, there are still practical limits imposed by carriers and devices. Keep the
bodyreasonably concise. - Encoding: Ensure your media file uses standard encoding. Standard web formats (JPEG, PNG, GIF) are best.
- Originator Restrictions: MMS sending might be restricted from Alphanumeric Sender IDs in some countries (like the US). A purchased, MMS-capable phone number (VMN or Toll-Free) is often required. Using an invalid or restricted originator will result in an error (e.g., Code 9).
- Country/Carrier Support: MMS is not universally supported by all carriers in all countries, or support may be inconsistent. Sending may fail if the recipient's carrier doesn't support MMS or blocks it from certain originators. MessageBird provides some country-specific information, but carrier behavior can vary.
- Cost: MMS messages are typically significantly more expensive than SMS messages. Factor this into your application's economics and monitor your MessageBird balance.
9. Performance Optimizations (Less Critical for Simple Sending)
For this simple sending endpoint, performance is less critical than reliability and correctness. However, if handling high volume:
- Asynchronous Operations: Node.js is inherently async. The
messagebird.messages.createcall is non-blocking, allowing the server to handle other requests while waiting for MessageBird's response. Ensure no blocking operations exist in the request path. - Payload Size: Keep request/response payloads minimal if possible, especially if bandwidth is a concern. The current response includes detailed info; tailor it if clients need less.
- Resource Usage: Monitor CPU/Memory usage under load. Use tools like
pm2for process management and clustering on multi-core machines. - External API Latency: The main bottleneck will be the latency of the MessageBird API call. This is largely outside your direct control, beyond ensuring a good network connection from your server to MessageBird's endpoints. Retries (Section 5) can help with transient network issues.
- Connection Pooling: Not directly applicable here, but if interacting with databases or other services, use connection pooling. The MessageBird SDK likely handles its HTTP connections efficiently.
- Caching: Not directly applicable to the sending action itself. Caching might be used for configuration or templates if the application were more complex.
10. Monitoring, Observability, and Analytics
For production readiness:
- Health Checks: Implement a simple
/healthendpoint that returns a 200 OK status. Monitoring services (like Kubernetes probes, uptime checkers) can poll this to ensure the application is running and responsive.javascript// Add near other routes app.get('/health', (req, res) => { // Optionally add checks for dependencies (e.g., MessageBird connectivity if feasible) res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() }); }); - Performance Metrics: Use APM (Application Performance Monitoring) tools (like Datadog, New Relic, Dynatrace, OpenTelemetry collectors) or Prometheus/Grafana to track:
- Request latency (p50, p90, p99 for
/send-mms). - Request rate (RPM/RPS).
- Error rates (4xx, 5xx breakdown by status code and route).
- Node.js process metrics (CPU, memory usage, event loop lag, garbage collection).
- Request latency (p50, p90, p99 for
- Error Tracking: Integrate services like Sentry, Bugsnag, or Rollbar to capture, aggregate, and alert on application errors (caught and uncaught) in real-time, providing stack traces and context.
- Logging Aggregation: Send structured logs (using Winston/Pino) to a central logging platform (like Elasticsearch/Logstash/Kibana (ELK), Datadog Logs, Splunk, Grafana Loki) for searching, analysis, and alerting based on log patterns.
- MessageBird Dashboard: Regularly check the MessageBird Message Logs (or use their Insights API) for insights into sending volume, delivery rates, costs, and specific errors reported by carriers that might not surface directly in your API response.
- Delivery Reports (DLRs): For reliable status tracking, configure a
reportUrlin yourmessages.createpayload (or globally in MessageBird settings). This URL points to an endpoint you create in your Express app to receive webhooks about the final delivery status (e.g.,delivered,failed,expired). Process these DLRs asynchronously, potentially updating a database record associated with the original message ID.
11. Troubleshooting and Caveats
- Error:
Request not allowed (incorrect access_key)(Code: 2)- Cause: Invalid or incorrect
MESSAGEBIRD_ACCESS_KEYin.env. Using a Test key instead of a Live key (or vice-versa). Key lacks permissions. - Solution: Verify the key in
.envmatches the Live key in the MessageBird Dashboard (Developers -> API access). Ensure.envis being loaded correctly (check for typos, restart server). Confirm the key is active and has message sending permissions.
- Cause: Invalid or incorrect
- Error:
originator is invalidorThe originator is not allowed to send this type of message(Code: 9 or similar)- Cause: The
MESSAGEBIRD_ORIGINATORnumber/ID in.envis incorrect, not owned by your account, misspelled, or not capable of sending MMS to the target country/carrier (e.g., using Alphanumeric where a number is required, number lacks MMS capability). - Solution: Verify the originator in
.env. Check the number's capabilities in the MessageBird Dashboard (Numbers -> Manage). Ensure it's MMS-enabled for the destination. Use the full E.164 number format (+1...) if using a phone number.
- Cause: The
- Error:
recipient is invalid(Code: 10)- Cause: The recipient number provided in the API request is not in valid E.164 format, is blacklisted, or is otherwise considered invalid by MessageBird or the downstream carrier.
- Solution: Ensure the client sends the recipient number with a leading
+and country code (e.g.,+12025550189). Add stricter validation on your server. Check MessageBird logs for more details if the format seems correct.
- Error related to
mediaUrlsor fetching media (may vary, e.g.,File not accessible,Content type not supported, Code: 20)- Cause: The
mediaUrlis not publicly accessible (behind login, firewall, private IP), incorrect URL, points to an unsupported file type, or the file exceeds size limits. Network issues between MessageBird's servers and the media host. SSL/TLS issues on the media host. - Solution: Verify the URL is public (try opening it in an incognito browser tab). Check file size and type against MessageBird/carrier limits (aim for common types like JPEG/PNG/GIF under 1-2MB as a safe starting point). Ensure the media hosting server is reliable and uses valid HTTPS.
- Cause: The
- Messages Not Received (but API shows success/accepted):
- Cause: Carrier filtering (spam, content), recipient device issues (no MMS plan, poor signal, unsupported device, blocked sender), incorrect recipient number (typo), country/carrier MMS limitations or temporary outages.
- Solution: Double-check the recipient number. Test with different recipients/carriers if possible. Check MessageBird Message Logs for detailed status codes/errors provided by the downstream carrier (these arrive later). Implement Delivery Reports (DLRs) via
reportUrlfor definitive status updates. Consult MessageBird support with specific message IDs if the issue persists and logs/DLRs don't clarify.
- MMS Cost: Remember MMS messages cost significantly more than SMS. Monitor your MessageBird balance (Error Code 7 indicates insufficient funds).
- Rate Limits: MessageBird imposes API rate limits (check their documentation). High-volume sending may require contacting them or implementing throttling/queuing on your end. The
express-rate-limitmiddleware helps protect your own API endpoint, not MessageBird's limits directly. - Asynchronous Nature: Message delivery is asynchronous. A successful API response (
200 OKwith statusacceptedorscheduled) means MessageBird accepted the request, not that the message was delivered. Use Delivery Reports (reportUrl) for final delivery status confirmation.
12. Deployment and CI/CD
- Environment Configuration: Do not commit your
.envfile. Production deployment environments (Heroku, AWS ECS/EKS, Google Cloud Run, Azure App Service, Docker Swarm, etc.) provide mechanisms to securely inject environment variables. Use these platform-specific methods (e.g., ConfigMaps/Secrets in K8s, Task Definition environment variables in ECS, App Settings in Azure). - Process Management: Use a process manager like
pm2to run your Node.js application reliably in production.pm2handles:- Running the app in the background.
- Monitoring and automatic restarts on crashes.
- Clustering (running multiple instances to utilize multi-core servers).
- Log management.
- Graceful shutdowns/reloads.
- Example
pm2usage:- Install:
npm install pm2 -g - Start clustered:
pm2 start index.js -i max --name messagebird-mms-api --watch(use--watchcautiously in prod, prefer CI/CD restarts) - Manage:
pm2 list,pm2 logs messagebird-mms-api,pm2 stop/restart/delete messagebird-mms-api,pm2 reload messagebird-mms-api(graceful reload) - Consider using a
ecosystem.config.jsfile forpm2configuration.
- Install:
- Reverse Proxy: Deploy behind a web server/reverse proxy like Nginx, Caddy, Traefik, or a cloud load balancer. Benefits include:
- SSL/TLS termination (HTTPS).
- Load balancing across
pm2cluster instances. - Serving static files (if any).
- Basic caching or request buffering.
- Easier IP allow/deny listing.
- CI/CD Pipeline (Conceptual):
- Commit: Developer pushes code to a Git repository (GitHub, GitLab, Bitbucket).
- Trigger: Push triggers a CI/CD pipeline (e.g., GitHub Actions, Jenkins, GitLab CI, CircleCI).
- Build & Test:
- Checks out code.
- Installs dependencies using lockfile (
npm cioryarn install --frozen-lockfile). - Runs linters (
eslint .). - Runs automated tests (Unit, Integration - see section 13).
- (Optional) Performs security scans (
npm audit --production). - Builds artifacts (e.g., creates a production build if needed, builds a Docker image).
- Deploy:
- Pushes Docker image to a registry (Docker Hub, ECR, GCR, ACR).
- Deploys the new version to the hosting environment (e.g., updates Kubernetes deployment, triggers an ECS service update, pushes to Heroku/Cloud Run), ensuring production environment variables are securely configured in the target environment.
- Containerization (Docker): Consider packaging the application as a Docker image for consistent environments and easier deployment. Create a
Dockerfile. - Rollback Strategy: Have a procedure to quickly redeploy the previously known-good version in case of deployment issues. CI/CD tools often support automated or manual rollbacks. Versioning artifacts (like Docker images with Git commit SHAs) is crucial.
Frequently Asked Questions (FAQ)
Q: How do I send MMS with Node.js and MessageBird?
A: Send MMS with Node.js by installing the MessageBird SDK and Express framework, creating a POST endpoint that accepts recipient numbers and media URLs, then calling messagebird.messages.create() with the mediaUrls parameter. Configure your MessageBird API key via environment variables and ensure you have an MMS-capable originator number.
Q: What file size limits apply to MMS messages?
A: MMS file size limits vary by carrier. For optimal deliverability, keep files under 500 KB. AT&T accepts up to 1 MB, Verizon accepts images up to 1.2 MB and videos up to 3.5 MB, and T-Mobile accepts up to 3 MB. All carriers reliably handle files up to 300 KB. For time-sensitive campaigns, limit files to 150 KB.
Q: What media formats are supported for MMS?
A: MMS supports common formats including JPEG, PNG, GIF for images, MP4 for videos, and PDF for documents. The MessageBird API requires publicly accessible URLs for media files – the service fetches media from your URL during message delivery. Unsupported formats or oversized files will fail with generic errors.
Q: Do I need a special phone number to send MMS with MessageBird?
A: Yes, you need a MessageBird phone number with MMS capabilities. Purchase a Virtual Mobile Number (VMN) or Toll-Free Number from the MessageBird Dashboard and verify it supports MMS for your target countries. Alphanumeric Sender IDs have limited MMS support and may not work in regions like North America.
Q: How do I handle MessageBird API errors in Node.js?
A: Handle MessageBird errors by wrapping the SDK callback in a Promise, using try/catch blocks, and checking the error.errors array structure. MessageBird returns error codes and descriptions – map authentication errors to 400/401 status codes, insufficient balance to 402, and server errors to 500. Log full error objects for debugging.
Q: Why are my MMS messages not being delivered?
A: MMS delivery failures typically occur due to: (1) media files not publicly accessible, (2) file sizes exceeding carrier limits, (3) unsupported media formats, (4) originator number lacking MMS capabilities, (5) recipient carrier blocking MMS, or (6) incorrect recipient phone number format. Check MessageBird Dashboard logs for detailed delivery status and carrier-specific errors.
Q: Can I send MMS to multiple recipients at once?
A: Yes, include multiple phone numbers in the recipients array parameter when calling messagebird.messages.create(). Example: recipients: ['+12005550199', '+447123456789']. MessageBird charges per recipient, and each receives the same message and media. For personalized messages to multiple recipients, send separate API requests.
Q: What's the difference between MMS and SMS in Node.js?
A: MMS (Multimedia Messaging Service) supports images, videos, GIFs, and PDFs alongside text, while SMS only supports plain text. MMS requires the mediaUrls parameter and an MMS-capable originator number. MMS messages are significantly more expensive than SMS and have carrier-specific file size limits. Use MMS for rich media campaigns and SMS for simple text notifications.
Q: How do I test MMS sending without incurring costs?
A: Test MMS sending using MessageBird's Test API key for development (messages simulate delivery without actually sending). For real testing, send to your own phone number with small file sizes to minimize costs. MessageBird doesn't charge for failed deliveries. Check the Dashboard SMS Log to verify message status without waiting for physical delivery.
Q: What Node.js version is required for MessageBird MMS?
A: MessageBird Node.js SDK works with Node.js 14+, but for modern features and security, use Node.js 22 LTS (Active LTS until October 2025, Maintenance until April 2027). If using Vite 7 for frontend integration, you need Node.js 20.19+ or 22.12+ minimum since Node.js 18 reached end-of-life in April 2025.
Q: How do I implement retry logic for failed MMS deliveries?
A: Implement retry logic using the async-retry package with exponential backoff. Only retry transient errors (5xx, network timeouts) – don't retry permanent errors (4xx like authentication failures, invalid recipients). Set retry limits (3–5 attempts) with increasing delays (1s, 2s, 4s). Check the error code to distinguish permanent from transient failures.
Q: Can I track MMS delivery status in real-time?
A: Yes, implement delivery reports (DLRs) by configuring a reportUrl parameter in your messages.create call. This webhook URL receives status updates (delivered, failed, expired) from MessageBird. Create an Express endpoint to receive these webhooks, validate them, and update your database records with final delivery status for each message.
13. Verification and Testing
-
Manual Verification:
-
Verify your
.envfile contains the correct Live API Key and an MMS-capable Originator number. -
Upload a sample image (JPEG, PNG, GIF, or small MP4) to a publicly accessible URL (use Imgur for testing, or your own public server/CDN/S3 bucket with public read access). Keep the file size small (< 1 MB) for initial tests.
-
Start the server locally:
node index.js -
Use
curl, Postman, or Insomnia to send a POST request to your running server (http://localhost:3000/send-mmsby default):bashcurl -X POST http://localhost:3000/send-mms \ -H "Content-Type: application/json" \ -d '{ "recipient": "+1xxxxxxxxxx", "messageBody": "Node.js MMS Test from curl!", "mediaUrl": "YOUR_PUBLICLY_ACCESSIBLE_MEDIA_URL" }'Replace
+1xxxxxxxxxxwith your actual mobile number (E.164 format) capable of receiving MMS, andYOUR_PUBLICLY_ACCESSIBLE_MEDIA_URLwith the URL from step 2. -
Check your server logs for output (initialization, request received, payload, API response/error).
-
Check the response from
curl/Postman (should be 200 OK with message details on success, or an error JSON). -
Check your mobile phone for the MMS message (may take a few seconds to minutes).
-
Check the MessageBird Dashboard Logs for the message status.
-
-
Automated Testing (Conceptual):
- Unit Tests: Use Jest or Mocha/Chai to test individual functions or modules in isolation. Mock dependencies like the
messagebirdSDK to avoid making real API calls during unit tests. Test input validation logic, payload construction, etc. - Integration Tests: Test the interaction between different parts of your application, including the API endpoint. Use
supertestto make HTTP requests to your running Express app (in a test environment). You might still mock the finalmessagebird.messages.createcall to avoid sending actual MMS and incurring costs during tests, but verify that your endpoint calls the SDK correctly with the expected parameters based on the request input. - End-to-End (E2E) Tests: (Use sparingly, can be slow/expensive) Deploy the application to a staging environment and use a test MessageBird key/number (if available) or carefully manage live tests to send a real MMS. Automate verification of receipt on a physical device (difficult). Focus on critical paths.
- Unit Tests: Use Jest or Mocha/Chai to test individual functions or modules in isolation. Mock dependencies like the
-
Testing Edge Cases:
- Invalid recipient formats.
- Missing required fields (
recipient,mediaUrl). - Invalid
mediaUrl(malformed, non-existent, private). - Large media files (if you know the limits).
- Unsupported media types.
- Empty
messageBody. - Test API error handling by mocking different error responses from the MessageBird SDK.
- Test rate limiting if implemented.
Frequently Asked Questions
How to send MMS messages with Node.js and Express?
Use the MessageBird API and Node.js SDK with Express to create an API endpoint that accepts recipient details, message body, and media URL. This endpoint interacts with the MessageBird API to dispatch MMS messages. The detailed setup and code are provided in the guide.
What is the MessageBird Node.js SDK used for?
The MessageBird Node.js SDK simplifies interaction with the MessageBird REST API for sending messages, making it easier to integrate MMS functionality into Node.js applications. It handles the complexities of API calls and responses.
Why use dotenv in a Node.js MMS project?
Dotenv loads environment variables from a .env file, which keeps sensitive credentials like your MessageBird API key out of your codebase. This improves security and makes it easier to manage different configurations.
When should I use ngrok or localtunnel with MessageBird?
Ngrok or localtunnel are useful when you need to test webhooks locally, particularly if you extend your application to receive messages. While not strictly necessary for *sending* MMS, these tools become important for two-way communication or receiving delivery reports.
How to set up a MessageBird MMS project in Node.js?
Create a Node.js project, install Express, the MessageBird SDK, and dotenv. Configure environment variables for your API key and originator number, initialize the SDK, and build an Express route to handle MMS sending.
What is the purpose of the /send-mms endpoint?
The `/send-mms` endpoint is a POST route in your Express app that receives requests to send MMS. It handles incoming recipient, message body, and media URL data, validates it, then uses the MessageBird API to send the MMS message accordingly.
What is the required format for recipient phone numbers in MessageBird?
Recipient phone numbers must be in E.164 format. This international standard ensures consistent formatting and includes the country code with a leading plus sign. Example: +12025550144.
Why does mediaUrl need to be publicly accessible?
The `mediaUrl` parameter in MessageBird must be publicly accessible without authentication. This is necessary for MessageBird's servers to directly fetch the media file and include it in the MMS message sent to recipients.
How to handle MessageBird API errors in Node.js?
The MessageBird Node.js SDK provides error objects in callbacks. Inspect the `err.errors` array for details. Handle specific error codes (e.g., invalid recipient, insufficient balance) appropriately in your app.
How to get a MessageBird access key?
Log in to your MessageBird Dashboard, navigate to the Developers section, and click the API access tab. Generate a *Live* key (not a test key) for sending real messages and keep it secure.
What is a MessageBird originator and how do I get one?
An originator is the "from" address for your MMS messages. It can be a purchased phone number or an approved alphanumeric sender ID. Purchase or configure one in your MessageBird Dashboard under Numbers, ensuring it is MMS-enabled.
How to implement rate limiting for the /send-mms route?
Use the `express-rate-limit` middleware to protect your `/send-mms` endpoint from excessive requests. Configure parameters like `windowMs` and `max` to define the rate limit window and maximum requests allowed per window.
Can I use localhost URLs for mediaUrl in MessageBird?
No, `mediaUrl` must be publicly accessible by the MessageBird servers. Localhost URLs are only accessible within your local network and therefore won't work for sending MMS with MessageBird.
What are some common troubleshooting issues with sending MMS using MessageBird?
Common problems include incorrect access keys or originator numbers, invalid recipient formats, inaccessible media URLs, and carrier-specific limitations. Carefully check these configurations and refer to the troubleshooting section for solutions.