code examples
code examples
How to Send MMS with Plivo, Node.js & Fastify: Complete 2025 Guide
Learn how to send MMS messages using Plivo API, Fastify, and Node.js. Complete tutorial with code examples, authentication, rate limiting, and error handling for multimedia messaging.
Send MMS with Plivo, Node.js & Fastify: Complete Tutorial
Learn how to build a production-ready service using Fastify and Node.js to send Multimedia Messaging Service (MMS) messages via the Plivo API. This comprehensive tutorial covers project setup, authentication, sending logic, error handling, rate limiting, and testing.
By the end of this guide, you'll have a robust Fastify application that sends MMS messages with media attachments (images, audio, video) using Plivo's reliable messaging infrastructure.
MMS vs SMS: When to Use Multimedia Messaging
MMS (Multimedia Messaging Service) allows you to send rich media content including images, audio files, videos, and longer text (up to 1600 characters), while SMS is limited to 160 characters of text only. Use MMS when you need to:
- Send visual content like product images, QR codes, or promotional graphics
- Share audio messages or video clips
- Deliver richer, more engaging marketing campaigns
- Provide visual instructions or tutorials
Note that MMS messages typically cost more than SMS and require cellular data connection on the recipient's device. Plivo supports MMS exclusively within the US and Canada.
Technologies Used:
- Node.js: The JavaScript runtime environment.
- Fastify: A high-performance, low-overhead web framework for Node.js. Chosen for its speed, extensive plugin ecosystem, and developer-friendly features like built-in validation.
- Plivo Node.js SDK: The official library for interacting with the Plivo API, simplifying MMS sending.
- dotenv: A module to load environment variables from a
.envfile, keeping sensitive credentials out of the codebase. - pino-pretty (Optional): A development dependency to make Fastify's logs more human-readable during development.
Prerequisites:
- Node.js and npm (or yarn): Ensure you have Node.js 22.x LTS installed (Active LTS until October 2025, Maintenance LTS until April 2027). Node.js 22 is the recommended version for production applications as of January 2025. Download from Node.js Downloads. Source: Node.js Release Schedule
- Plivo Account: You need an active Plivo account. (Sign up for Plivo)
- Plivo Auth ID and Auth Token: Found on your Plivo console dashboard after logging in.
- Plivo Phone Number: An MMS-enabled Plivo phone number (available for US & Canada only). Plivo supports MMS messaging exclusively within the US and Canada using local long code, toll-free, or short code numbers. You can purchase one from the Plivo console under "Phone Numbers" > "Buy Numbers". Ensure the number has MMS capabilities enabled. Source: Plivo MMS Support
- Publicly Accessible Media URL: The URL of the image or media file you want to send. Supported formats include JPEG, GIF, PNG (images), MP3 (audio), and MP4 (video). Crucially, Plivo's servers must be able to publicly fetch the media from this URL. For Canadian carriers, keep MMS attachments smaller than 1 MB to ensure broad compatibility across all networks. For testing, services like Postimages or public GitHub repository links can work. Source: Plivo MMS Country Support
- (Optional) Verified Destination Number: If using a Plivo trial account, you can only send messages to numbers verified in your Plivo console under "Messaging" > "Sandbox Numbers".
System Architecture:
(A diagram illustrating the flow from HTTP Client -> Fastify App -> Plivo API -> End User would typically be included here.)
This guide focuses on the "Fastify Application" component and its interaction with the Plivo API for sending MMS.
<!-- EXPAND: Missing actual architecture diagram or flowchart (Type: Enhancement, Priority: Medium) --> <!-- GAP: No explanation of webhook flow for delivery status (Type: Substantive) -->Setting Up Your Plivo MMS Project
Let's initialize the project, install dependencies, and set up the basic structure.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir fastify-plivo-mms cd fastify-plivo-mms -
Initialize Node.js Project: Initialize a
package.jsonfile. The-yflag accepts default settings.bashnpm init -y # or yarn init -y -
Install Dependencies: Install Fastify, the Plivo SDK, and
dotenv.bashnpm install fastify plivo dotenv # or yarn add fastify plivo dotenv
-
Install Development Dependencies (Optional): Install
pino-prettyfor better log readability during development.bashnpm install --save-dev pino-pretty # or yarn add --dev pino-pretty -
Create Project Structure: Set up a basic directory structure for better organization.
bashmkdir src mkdir src/routes touch src/app.js touch src/routes/mms.js touch .env touch .env.example touch .gitignoresrc/: Contains the main application source code.src/routes/: Will hold route definitions.src/app.js: The main Fastify application entry point.src/routes/mms.js: The route handler specifically for MMS sending..env: Stores sensitive environment variables (API keys, etc.). Never commit this file..env.example: An example file showing required environment variables (safe to commit)..gitignore: Specifies files and directories that Git should ignore.
-
Configure
.gitignore: Addnode_modulesand.envto your.gitignorefile to prevent committing them to version control.text# .gitignore node_modules .env *.log -
Configure
.env.example: Add the necessary environment variable placeholders to.env.example.dotenv# .env.example # Plivo API Credentials PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN # Plivo MMS-enabled phone number (in E.164 format, e.g., +14151234567) PLIVO_SENDER_NUMBER=YOUR_PLIVO_MMS_NUMBER # Server Configuration HOST=0.0.0.0 PORT=3000 -
Configure
.env: Copy.env.exampleto.envand fill in your actual Plivo credentials and phone number.bashcp .env.example .envNow, edit the
.envfile with your real values obtained from the Plivo console.dotenv# .env # Plivo API Credentials PLIVO_AUTH_ID=MXXXXXXXXXXXXXXXXXXD PLIVO_AUTH_TOKEN=NXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZA # Plivo MMS-enabled phone number (in E.164 format, e.g., +14151234567) PLIVO_SENDER_NUMBER=+14151112222 # Server Configuration HOST=0.0.0.0 PORT=3000Why
.env? Storing sensitive information like API keys directly in code is a major security risk..envfiles allow you to keep configuration separate from the codebase, making it easier to manage different environments (development, production) and preventing accidental exposure.
Implementing the Fastify Server for MMS
Let's set up the basic Fastify server and configure it to use the Plivo client.
-
Edit
src/app.js: This file will initialize Fastify, load environment variables, instantiate the Plivo client, register routes, and start the server.javascript// src/app.js 'use strict' const Fastify = require('fastify'); const plivo = require('plivo'); const mmsRoutes = require('./routes/mms'); require('dotenv').config(); // Load .env variables into process.env // Basic validation for essential environment variables const requiredEnv = ['PLIVO_AUTH_ID', 'PLIVO_AUTH_TOKEN', 'PLIVO_SENDER_NUMBER', 'PORT', 'HOST']; const missingEnv = requiredEnv.filter(envVar => !process.env[envVar]); if (missingEnv.length > 0) { console.error(`Error: Missing required environment variables: ${missingEnv.join(', ')}`); console.error('Please ensure they are set in your .env file or environment.'); process.exit(1); // Exit if essential config is missing } // Configure Fastify logger for development const loggerConfig = process.env.NODE_ENV === 'development' ? { transport: { target: 'pino-pretty', options: { translateTime: 'HH:MM:ss Z', ignore: 'pid,hostname', }, }, } : true; // Use default JSON logger in production // Initialize Fastify const app = Fastify({ logger: loggerConfig }); // --- Plivo Client Initialization --- // Why here? Instantiate the client once and make it available // throughout the application via Fastify's decoration feature. try { const plivoClient = new plivo.Client( process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN ); // Decorate the Fastify instance with the Plivo client // Makes `app.plivo` accessible in route handlers app.decorate('plivo', plivoClient); app.log.info('Plivo client initialized successfully.'); } catch (error) { app.log.error('Failed to initialize Plivo client:', error); process.exit(1); // Cannot proceed without a working client } // Decorate Fastify with the sender number for easy access app.decorate('plivoSenderNumber', process.env.PLIVO_SENDER_NUMBER); // --- Register Routes --- // Prefix all routes defined in mms.js with /api/mms app.register(mmsRoutes, { prefix: '/api/mms' }); // --- Health Check Route --- // Good practice for monitoring and load balancers app.get('/health', async (request, reply) => { return { status: 'ok', timestamp: new Date().toISOString() }; }); // --- Start Server --- const start = async () => { try { const port = parseInt(process.env.PORT, 10); const host = process.env.HOST; await app.listen({ port: port, host: host }); // Logger already announces the address } catch (err) { app.log.error(err); process.exit(1); } }; start();- Environment Variables:
dotenv.config()loads variables from.env. We add basic checks to ensure critical variables are present. - Logger: We configure
pino-prettyfor development for easier reading and standard JSON logging for production (better for log aggregation tools). - Plivo Client: The
plivo.Clientis instantiated using credentials fromprocess.env. - Fastify Decorators (
app.decorate): This is a key Fastify pattern. We "decorate" the Fastifyappinstance with theplivoClientandplivoSenderNumber. This makes them easily accessible within route handlers viarequest.server.plivoorreply.server.plivo(and similarly forplivoSenderNumber), avoiding the need to pass them around manually or re-initialize them. - Route Registration: We register the routes defined in
src/routes/mms.jsunder the/api/mmsprefix. - Health Check: A simple
/healthendpoint is added, which is standard practice for monitoring. - Server Start: The
app.listenfunction starts the server on the configured host and port.
- Environment Variables:
Building the MMS Sending API Endpoint
Now, let's implement the route that will handle incoming requests to send an MMS.
<!-- EXPAND: Could add request/response examples with different scenarios (Type: Enhancement) -->-
Edit
src/routes/mms.js: This file defines the API endpoint for sending MMS messages.javascript// src/routes/mms.js 'use strict'; const plivo = require('plivo'); // Required for Plivo specific error types if needed // Define the JSON schema for the request body // Why schema? Fastify uses this for automatic validation. // It's faster than manual checks and ensures data consistency. const sendMmsSchema = { body: { type: 'object', required: ['to', 'media_urls', 'text'], // Define mandatory fields properties: { to: { type: 'string', // E.164 format: + followed by 1-3 digit country code + subscriber number (max 15 digits total) pattern: '^\\+[1-9]\\d{1,14}$', description: 'Destination phone number in E.164 format (e.g., +14151234567). Must start with + followed by country code and subscriber number. Maximum 15 digits total per ITU-T E.164 specification.' // ITU-T specification reference }, media_urls: { type: 'array', minItems: 1, // Must have at least one media URL maxItems: 10, // Plivo supports up to 10 media URLs per MMS items: { type: 'string', format: 'url', // Basic URL format validation description: 'Publicly accessible URL of the media file. Supported formats: JPEG, GIF, PNG (images), MP3 (audio), MP4 (video). Keep files under 1 MB for Canadian carriers.' } }, text: { type: 'string', minLength: 1, // Text cannot be empty description: 'The text content of the MMS message' } // Optional: Add other Plivo parameters here if needed (e.g., url, method for status callbacks) }, additionalProperties: false // Disallow properties not defined in the schema }, response: { // Define expected success response structure 200: { type: 'object', properties: { message: { type: 'string' }, // Use camelCase for consistency with Node.js conventions and SDK response messageUuid: { type: 'array', items: { type: 'string' } }, apiId: { type: 'string' } } }, // Define potential error responses (Fastify handles validation errors automatically -> 400) 500: { type: 'object', properties: { statusCode: { type: 'number' }, error: { type: 'string' }, message: { type: 'string' } } }, 429: { // For rate limiting type: 'object', properties: { statusCode: { type: 'number' }, error: { type: 'string' }, message: { type: 'string' } } } // Add other specific error status codes Plivo might return if needed (e.g., 400, 401, 402) } }; async function mmsRoutes(fastify, options) { // Access the decorated Plivo client and sender number const plivoClient = fastify.plivo; const senderNumber = fastify.plivoSenderNumber; fastify.post('/send', { schema: sendMmsSchema }, async (request, reply) => { const { to, media_urls, text } = request.body; // Data is already validated by Fastify request.log.info(`Attempting to send MMS to ${to} from ${senderNumber}`); try { const params = { src: senderNumber, dst: to, // Plivo uses 'dst' for destination text: text, media_urls: media_urls, type: 'mms' // Explicitly set type to MMS // Optional: Configure status callbacks // url: 'https://your-callback-url.com/plivo-status', // Your webhook endpoint // method: 'POST' }; const response = await plivoClient.messages.create(params); request.log.info({ msg: 'MMS sent successfully', response: response }, `MMS UUID(s): ${response.messageUuid.join(', ')}`); // Send successful response back to the client (using camelCase keys) return reply.code(200).send({ message: response.message, // Success message from Plivo messageUuid: response.messageUuid, // Plivo's message identifier(s) apiId: response.apiId // Plivo's API request identifier }); } catch (error) { request.log.error({ err: error }, `Failed to send MMS to ${to}`); // Handle potential Plivo-specific errors or general errors let statusCode = 500; let errorMessage = 'An unexpected error occurred while sending the MMS.'; if (error instanceof plivo.PlivoResponseError) { // Plivo API returned an error response statusCode = error.statusCode || 500; // Use Plivo's status if available errorMessage = `Plivo API Error (${error.statusCode}): ${error.message || 'Unknown Plivo error'}`; // Common Plivo errors include 400 (e.g., invalid 'dst', number capability issues), 401 (auth), 402 (insufficient balance). // You might want more specific handling based on Plivo error codes here. } else if (error.name === 'ValidationError') { // Although Fastify handles schema validation, keep this for potential future manual checks statusCode = 400; errorMessage = `Validation Error: ${error.message}`; } // Add more specific error handling if needed (e.g., network errors) // Send error response return reply.code(statusCode).send({ statusCode: statusCode, error: error.name || 'Internal Server Error', message: errorMessage }); } }); } module.exports = mmsRoutes;- Schema Validation: We define a
sendMmsSchemausing JSON Schema. Fastify automatically validates incoming request bodies against this schema before the handler function runs. If validation fails, Fastify sends a400 Bad Requestresponse automatically. This simplifies the handler logic significantly. We specify required fields (to,media_urls,text), types, and formats (like E.164 pattern and URL format).additionalProperties: falseprevents extra, unexpected fields. Thetofield pattern was corrected, and its description enhanced. AddedmaxItems: 10to media_urls based on Plivo limits. - Route Definition:
fastify.post('/send', { schema: sendMmsSchema }, ...)defines a POST endpoint at/api/mms/send. The schema is attached directly to the route options. - Accessing Decorators:
fastify.plivoandfastify.plivoSenderNumberprovide the initialized Plivo client and sender number. - Plivo API Call: We construct the
paramsobject required byplivoClient.messages.create. Note that Plivo usesdstfor the destination number. We explicitly settype: 'mms'. async/await: The route handler is anasyncfunction, allowing us to useawaitfor the Plivo API call, making the asynchronous code cleaner.- Success Response: On success, we log the result and return a
200 OKresponse with details from the Plivo API. The response keys (messageUuid,apiId) are now consistently camelCase, matching the schema and common Node.js practice. - Error Handling: The
try...catchblock handles errors during the API call. We log the error and attempt to provide a meaningful status code and message back to the client. We check if the error is a specificPlivoResponseErrorto potentially extract more details, adding a comment about common error codes. The response schema was updated to use camelCase for consistency.
- Schema Validation: We define a
Adding Rate Limiting for Security
To prevent abuse and protect your Plivo account balance, implementing rate limiting is crucial.
<!-- DEPTH: Missing explanation of DDoS protection and additional security layers (Priority: Medium) -->-
Install Rate Limiter Plugin:
bashnpm install @fastify/rate-limit # or yarn add @fastify/rate-limit -
Register and Configure in
src/app.js: Modifysrc/app.jsto include the rate limiter.javascript// src/app.js // ... other imports const rateLimit = require('@fastify/rate-limit'); // ... after Fastify initialization // --- Rate Limiting --- // Why? Prevents abuse and controls costs. Apply globally or per-route. // Register *before* your main application routes app.register(rateLimit, { max: 100, // Max requests per time window per IP (adjust as needed) timeWindow: '1 minute', // Time window duration // Optional: Add custom key generator, redis store for distributed systems, etc. // keyGenerator: function (req) { /* ... */ }, // redis: new Redis({ host: '127.0.0.1' }), errorResponseBuilder: function (req, context) { return { statusCode: 429, error: 'Too Many Requests', message: `Rate limit exceeded. Please try again after ${context.after}.`, retryAfter: context.ttl, // Time in milliseconds until reset } }, enableDraftSpec: true, // Recommended: Set standard RateLimit-* headers }); // --- Plivo Client Initialization --- // Moved *after* rate limit registration try { const plivoClient = new plivo.Client( process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN ); // Decorate the Fastify instance with the Plivo client // Makes `app.plivo` accessible in route handlers app.decorate('plivo', plivoClient); app.log.info('Plivo client initialized successfully.'); } catch (error) { app.log.error('Failed to initialize Plivo client:', error); process.exit(1); // Cannot proceed without a working client } // Decorate Fastify with the sender number for easy access app.decorate('plivoSenderNumber', process.env.PLIVO_SENDER_NUMBER); // --- Register Routes --- // Prefix all routes defined in mms.js with /api/mms app.register(mmsRoutes, { prefix: '/api/mms' }); // --- Health Check Route --- // Good practice for monitoring and load balancers app.get('/health', async (request, reply) => { return { status: 'ok', timestamp: new Date().toISOString() }; }); // --- Start Server --- const start = async () => { try { const port = parseInt(process.env.PORT, 10); const host = process.env.HOST; await app.listen({ port: port, host: host }); // Logger already announces the address } catch (err) { app.log.error(err); process.exit(1); } }; start();- Registration: We use
app.registerto add the@fastify/rate-limitplugin. It's important to register this before the routes you want to limit. - Configuration:
max: Sets the maximum number of requests allowed within thetimeWindow. Adjust this based on expected traffic and Plivo's own rate limits.timeWindow: Defines the duration over which requests are counted (e.g., '1 minute', '1 hour',60000ms).errorResponseBuilder: Customizes the429 Too Many Requestsresponse sent when the limit is hit.enableDraftSpec: Adds standardRateLimit-*headers to responses, which is good practice.
- Scope: By registering it here before other routes, it applies globally by default (based on IP address). You can configure it more granularly per-route if needed.
- Registration: We use
Testing Your MMS Service
Now, let's run the server and test the MMS sending endpoint.
<!-- EXPAND: Could add automated testing examples with Jest or Mocha (Type: Enhancement) -->-
Add Run Scripts to
package.json:json// package.json snippet { // ... other fields like name, version, main, etc. "scripts": { "start": "node src/app.js", "dev": "NODE_ENV=development node src/app.js | pino-pretty" } // ... dependencies, etc. }start: Runs the application in production mode (default logger).dev: Runs in development mode, settingNODE_ENVand piping logs throughpino-pretty.
-
Run the Server (Development Mode):
bashnpm run dev # or yarn devYou should see output indicating the server is running, similar to:
[HH:MM:SS Z] INFO: Plivo client initialized successfully. [HH:MM:SS Z] INFO: Server listening at http://0.0.0.0:3000 -
Test with
curl: Open another terminal window. Replace placeholders with your verified destination number (for trial accounts) or any US/Canada number (for paid accounts), and a publicly accessible media URL.Success Case:
bashcurl -X POST http://localhost:3000/api/mms/send \ -H "Content-Type: application/json" \ -d '{ "to": "+1DESTINATION_NUMBER", "media_urls": ["https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Plivo_logo.png/600px-Plivo_logo.png"], "text": "Hello from Fastify and Plivo!" }'Expected Success Response (JSON - using camelCase):
json{ "message": "message(s) queued", "messageUuid": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "apiId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy" }You should also see corresponding logs in the server terminal and receive the MMS on the destination phone shortly.
**Validation Error Case (Missing `text`):**
```bash
curl -X POST http://localhost:3000/api/mms/send \
-H "Content-Type: application/json" \
-d '{
"to": "+1DESTINATION_NUMBER",
"media_urls": ["https://example.com/image.png"]
}'
```
*Expected Error Response (400 Bad Request - Handled by Fastify):*
```json
{
"statusCode": 400,
"error": "Bad Request",
"message": "body should have required property 'text'"
}
```
**Plivo API Error Case (e.g., Invalid Auth Token in `.env`):**
*(Simulate by temporarily putting an incorrect token in `.env` and restarting the server)*
```bash
# Send the valid request again after messing up the token
curl -X POST http://localhost:3000/api/mms/send \
-H "Content-Type: application/json" \
-d '{
"to": "+1DESTINATION_NUMBER",
"media_urls": ["https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Plivo_logo.png/600px-Plivo_logo.png"],
"text": "Testing error"
}'
```
*Expected Error Response (Handled by our `catch` block):*
```json
{
"statusCode": 401,
"error": "PlivoResponseError",
"message": "Plivo API Error (401): Authentication credentials were not provided or are invalid."
}
```
<!-- EXPAND: Could add Postman collection or OpenAPI spec for testing (Type: Enhancement) -->
Troubleshooting Common MMS Issues
- Authentication Errors (401): Double-check
PLIVO_AUTH_IDandPLIVO_AUTH_TOKENin your.envfile. Ensure they are copied correctly from the Plivo console. - Invalid
srcordstNumber (e.g., 400 Bad Request from Plivo):- Ensure
PLIVO_SENDER_NUMBERis an MMS-enabled number you own on Plivo. - Ensure both
srcanddstnumbers are in E.164 format (e.g.,+14151234567). Thetofield validator checks the format (^\\+[1-9]\\d{1,14}$). - If using a trial account, the
dstnumber must be verified in the Sandbox Numbers section of the Plivo console.
- Ensure
- Invalid Media URL: The
media_urlsmust point to publicly accessible URLs. Plivo's servers need to fetch the media from these URLs. Test the URL in your browser first. Supported file types include JPEG, GIF, PNG (images), MP3 (audio), and MP4 (video). For Canadian carriers, keep MMS attachments smaller than 1 MB to ensure broad compatibility across all networks. - Rate Limits (429): If you send too many requests too quickly, you'll hit the rate limit configured in Fastify (
max: 100,timeWindow: '1 minute'by default in this guide) or Plivo's own API limits. Plivo enforces a default limit of 300 API requests per 5 seconds for non-call APIs, and supports up to 100 concurrent MMS requests. When limits are exceeded, Plivo responds with HTTP 429 Too Many Requests. The error response indicates when you can retry. Source: Plivo Rate Limits and Plivo MMS Account Rate Limits - MMS Not Supported: Plivo primarily supports sending MMS between US and Canadian numbers. Sending to other countries may fail or be converted to SMS with a link. Check Plivo's documentation for current country support.
- Other Plivo API Errors (4xx/5xx): Beyond authentication (401), you might encounter errors like 400 (Bad Request - often due to invalid numbers, lack of MMS capability on the source number, or malformed requests), 402 (Payment Required - insufficient account balance), or Plivo-side server errors (5xx). The error message provided by the API is usually informative. Check the Plivo API documentation for specific error code meanings.
- Network Issues: Ensure your server has outbound internet connectivity to reach the Plivo API (
api.plivo.com). Firewalls or network configuration could block requests. - Message Queued vs. Delivered: A successful API response (
200 OK,message: "message(s) queued") only means Plivo accepted the request. It doesn't guarantee delivery to the handset. For delivery confirmation, you need to implement webhook handlers for Plivo's status callbacks (using theurlandmethodparameters in themessages.createcall). This is a more advanced topic. - Large Media Files: Very large media files might take longer to process or could potentially time out. Consider optimizing media file sizes. Check Plivo's limits on file size if you encounter issues.
Production Deployment Best Practices
- Environment Variables: In production, never commit your
.envfile. Use your hosting provider's mechanism for setting environment variables (e.g., Heroku Config Vars, AWS Systems Manager Parameter Store, Docker environment variables). - Process Management: Use a process manager like
pm2or run your application within a container (Docker) to handle restarts, clustering (for multi-core utilization), and logging.- Example with
pm2:npm install -g pm2, thenpm2 start src/app.js --name fastify-plivo-mms
- Example with
- HTTPS: Always run your production application behind a reverse proxy (like Nginx or Caddy) that handles HTTPS termination. Fastify itself can handle HTTPS, but offloading it is common practice.
- Logging: Configure production logging to output structured JSON (Fastify's default) and forward these logs to a centralized logging service (e.g., Better Stack, Datadog, ELK stack) for analysis and monitoring.
- Monitoring: Set up external uptime monitoring (e.g., Better Stack Uptime, UptimeRobot) to ping your
/healthendpoint. Monitor application performance (APM tools) and error rates.
Plivo MMS Frequently Asked Questions
How do I send MMS with Plivo and Node.js?
To send MMS with Plivo and Node.js, install the Plivo SDK (npm install plivo), initialize a Plivo client with your Auth ID and Auth Token, and call client.messages.create() with parameters including src (your Plivo number), dst (recipient in E.164 format), text, media_urls (array of public media URLs), and type: 'mms'. Plivo supports MMS only within the US and Canada.
What file formats does Plivo MMS support?
Plivo MMS supports JPEG, GIF, and PNG for images, MP3 for audio, and MP4 for video. Media files must be hosted on publicly accessible URLs that Plivo servers can fetch. For Canadian carriers, keep MMS attachments smaller than 1 MB to ensure broad compatibility across all networks. You can include up to 10 media URLs per MMS message.
<!-- DEPTH: Missing specific file size limits per format (Priority: Medium) -->What is E.164 phone number format?
E.164 is the international phone number format standardized by ITU-T. It consists of a plus sign (+) followed by a country code (1-3 digits) and subscriber number, with a maximum of 15 digits total. Example: +14151234567 for a US number. Both source and destination numbers must use E.164 format for Plivo API calls.
Why use Fastify for MMS messaging?
Fastify is a high-performance, low-overhead Node.js web framework ideal for MMS messaging services. It provides built-in JSON schema validation, automatic request/response serialization, extensive plugin ecosystem (including @fastify/rate-limit), and superior performance compared to Express. Fastify's decorator pattern simplifies sharing the Plivo client across routes.
<!-- EXPAND: Could add performance benchmarks vs Express (Type: Enhancement) -->What are Plivo API rate limits?
Plivo enforces a default limit of 300 API requests per 5 seconds for non-call APIs and supports up to 100 concurrent MMS requests. When limits are exceeded, Plivo responds with HTTP 429 Too Many Requests. Implement rate limiting in your Fastify application using @fastify/rate-limit to control costs and prevent abuse.
Can I send MMS to countries outside US and Canada with Plivo?
No. Plivo supports MMS messaging exclusively within the US and Canada. Attempts to send MMS to other countries may fail or be converted to SMS with a media link. You can only send and receive MMS using US or Canadian Plivo phone numbers (local long code, toll-free, or short code).
How do I handle Plivo MMS delivery status?
Plivo's messages.create() returns a 200 OK response when messages are queued, not delivered. For delivery confirmation, implement webhook handlers for Plivo's status callbacks by adding url and method parameters to your API call. Plivo will POST delivery status updates (delivered, failed, undelivered) to your webhook endpoint.
What Node.js version should I use for Plivo MMS?
Use Node.js 22.x LTS for production applications. Node.js 22 is in Active LTS until October 2025 and Maintenance LTS until April 2027. This version provides full support for modern JavaScript features, security updates, and optimal performance for Fastify and Plivo SDK integration.
Related Resources and Next Steps
Now that you've built a Fastify MMS service with Plivo, consider exploring these related topics:
- Plivo SMS API with Node.js - Learn how to send basic SMS messages
- Two-Way Messaging with Plivo - Handle incoming messages and build interactive experiences
- Plivo Delivery Status Webhooks - Track message delivery in real-time
- OTP and 2FA with Plivo - Implement one-time password authentication
Wrapping Up
You have now successfully built a Fastify application capable of sending MMS messages using the Plivo Node.js SDK. We covered project setup, secure credential management, API route implementation with validation, error handling, rate limiting, and essential testing procedures.
This provides a solid foundation for integrating MMS capabilities into your Node.js applications. From here, you could expand the service to include status webhooks for delivery tracking, build a frontend interface, or integrate it into a larger communication workflow.
<!-- EXPAND: Could add next steps roadmap with advanced features (Type: Enhancement) -->