Multimedia Messaging Service (MMS) enables sending messages that include media like images, GIFs, or videos, offering richer communication than plain SMS. This guide provides a complete walkthrough for building a Node.js application using the Express framework to send MMS messages via the Plivo communication platform.
We will build a simple REST API endpoint that accepts recipient details, message text, and a media URL, and then uses the Plivo Node.js SDK to dispatch the MMS message. This guide covers everything from project setup and core implementation to error handling, security considerations, and deployment notes, enabling you to create a robust MMS sending service.
System Architecture:
graph LR
A[User/Client] -- HTTP POST Request --> B(Node.js/Express API);
B -- Send MMS Request --> C(Plivo API);
C -- Delivers MMS --> D(Carrier Network);
D -- Delivers MMS --> E(Recipient Phone);
C -- Sends Status Callback (Optional) --> B;
Prerequisites:
- Node.js and npm (or yarn): Installed on your system. (Download: https://nodejs.org/)
- Plivo Account: A Plivo account is required. Sign up if you don't have one (https://www.plivo.com/).
- Plivo Phone Number: An MMS-enabled Plivo phone number (available for US and Canadian numbers). You can acquire one from the Plivo console under ""Phone Numbers"" > ""Buy Numbers"".
- Basic understanding of Node.js, Express, and REST APIs.
1. Project Setup
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
mkdir plivo-mms-sender cd plivo-mms-sender
-
Initialize Node.js Project: This command creates a
package.json
file to manage project dependencies and scripts.npm init -y
-
Install Dependencies: We need
express
for the web server,plivo
for the Plivo Node.js SDK, anddotenv
to manage environment variables securely. Note that modern versions of Express include middleware for parsing request bodies, sobody-parser
is no longer needed separately.npm install express plivo dotenv
-
Set Up Environment Variables: Create a file named
.env
in the root of your project directory. This file will store sensitive credentials like your Plivo Auth ID and Auth Token. Never commit this file to version control.Filename:
.env
PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN PLIVO_SENDER_NUMBER=YOUR_PLIVO_MMS_ENABLED_NUMBER SERVER_PORT=3000
PLIVO_AUTH_ID
&PLIVO_AUTH_TOKEN
: Find these credentials on the overview page of your Plivo console dashboard.PLIVO_SENDER_NUMBER
: Replace this with the MMS-enabled Plivo phone number you acquired, including the country code (e.g.,+14151234567
).SERVER_PORT
: The port your Express application will listen on.
-
Create
.gitignore
File: Ensure your.env
file andnode_modules
directory are not tracked by Git. Create a.gitignore
file in the project root:# .gitignore node_modules/ .env npm-debug.log* yarn-debug.log* yarn-error.log*
-
Basic Project Structure: Create a file named
server.js
. This will be the entry point for our application.plivo-mms-sender/ ├── node_modules/ ├── .env ├── .gitignore ├── package.json ├── package-lock.json └── server.js
2. Implementing Core Functionality: Sending MMS
Now, let's write the code to initialize Express and the Plivo client, and implement the MMS sending logic.
server.js
:
// server.js
require('dotenv').config(); // Load environment variables from .env file
const express = require('express');
const plivo = require('plivo');
// --- Configuration ---
const PORT = process.env.SERVER_PORT || 3000;
const PLIVO_AUTH_ID = process.env.PLIVO_AUTH_ID;
const PLIVO_AUTH_TOKEN = process.env.PLIVO_AUTH_TOKEN;
const PLIVO_SENDER_NUMBER = process.env.PLIVO_SENDER_NUMBER;
// --- Input Validation ---
if (!PLIVO_AUTH_ID || !PLIVO_AUTH_TOKEN || !PLIVO_SENDER_NUMBER) {
console.error(""Error: Plivo credentials or sender number are missing in .env file."");
process.exit(1); // Exit if essential config is missing
}
// --- Initialize Express App ---
const app = express();
// Use built-in Express middleware for parsing JSON and URL-encoded request bodies
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// --- Initialize Plivo Client ---
// The Plivo client is initialized once and reused for requests.
const client = new plivo.Client(PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN);
// --- Health Check Endpoint ---
// Simple endpoint to verify the server is running.
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
// --- MMS Sending Endpoint (Defined in the next section) ---
// --- Start Server ---
// Only start listening if the script is run directly (for testability)
if (require.main === module) {
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`Plivo Sender Number: ${PLIVO_SENDER_NUMBER}`);
});
}
// --- Graceful Shutdown (Optional but Recommended) ---
process.on('SIGINT', () => {
console.log('SIGINT signal received: closing HTTP server');
// Add cleanup logic if needed (e.g., close database connections)
process.exit(0);
});
// Export the app instance for testing purposes
module.exports = app;
Explanation:
require('dotenv').config();
: Loads variables from the.env
file intoprocess.env
.- Configuration Loading: Retrieves Plivo credentials and the sender number from
process.env
. Includes basic validation to ensure these critical variables are set. - Express Initialization: Sets up the Express application and uses the built-in
express.json()
andexpress.urlencoded()
middleware to handle incoming JSON and URL-encoded data. - Plivo Client Initialization: Creates an instance of the Plivo client using your Auth ID and Auth Token. This client object will be used to interact with the Plivo API.
- Health Check: A simple
/health
endpoint is included as a best practice to easily check if the service is operational. - Server Start: Starts the Express server, listening on the configured port. This is wrapped in
if (require.main === module)
so the server only starts automatically when the file is run directly withnode server.js
, not when it's required by another module (like a test file). - Graceful Shutdown: Basic signal handling for
SIGINT
(Ctrl+C) to allow for potential cleanup before exiting. module.exports = app;
: Exports the Expressapp
object, making it possible to import and use it in test files without starting the server listener.
3. Building the API Layer for Sending MMS
Let's create the API endpoint that will receive requests to send MMS messages.
Add the following route handler within server.js
, before the if (require.main === module)
block and module.exports = app;
:
server.js
(Add this section):
// server.js
// ... (previous code: imports, config, express init, plivo client init, health check)
// --- MMS Sending Endpoint ---
app.post('/send-mms', async (req, res) => {
const { destinationNumber, messageText, mediaUrl } = req.body;
// --- Basic Request Validation ---
if (!destinationNumber || !messageText || !mediaUrl) {
return res.status(400).json({
error: 'Missing required fields: destinationNumber, messageText, mediaUrl',
});
}
// Basic format check (can be improved with libraries like libphonenumber-js)
if (!/^\+\d{10,15}$/.test(destinationNumber)) {
return res.status(400).json({ error: 'Invalid destinationNumber format. Use E.164 format (e.g., +14157654321).' });
}
// Basic URL validation
try {
new URL(mediaUrl);
} catch (_) {
return res.status(400).json({ error: 'Invalid mediaUrl format.' });
}
// Using console.log here for simplicity; Section 5 discusses robust logging.
console.log(`Received request to send MMS to ${destinationNumber}`);
try {
const response = await client.messages.create(
PLIVO_SENDER_NUMBER, // src: Your Plivo number
destinationNumber, // dst: Recipient's number
messageText, // text: The text part of the MMS
{
type: 'mms', // Specify message type as MMS
media_urls: [mediaUrl], // Array of media URLs (Plivo supports multiple)
// Optional: media_ids: ['your_media_id'] // Use if media is pre-uploaded to Plivo
// Optional: log: true // Request Plivo to log the message content (default is usually true)
// Optional: url: 'YOUR_STATUS_CALLBACK_URL' // URL for delivery status updates
}
);
// Using console.log here; use a structured logger in production (see Section 5).
console.log('MMS sent successfully:', response);
res.status(202).json({ // 202 Accepted: Request received, processing initiated
message: 'MMS sending request accepted.',
plivoResponse: response, // Include Plivo's response (contains message UUID)
});
} catch (error) {
// Using console.error here; use a structured logger in production (see Section 5).
console.error('Error sending MMS via Plivo:', error);
// Provide more specific feedback based on Plivo errors if possible
let statusCode = 500; // Default to Internal Server Error
let errorMessage = 'Failed to send MMS due to an internal server error.';
if (error.statusCode) { // Check if it's a Plivo error with a status code
switch (error.statusCode) {
case 400:
statusCode = 400;
errorMessage = error.message || 'Plivo Bad Request (e.g., invalid number format, bad media URL).';
break;
case 401:
statusCode = 401;
errorMessage = error.message || 'Plivo Authentication Failed.';
break;
case 500:
case 503:
statusCode = 503; // Treat Plivo 5xx as Service Unavailable
errorMessage = error.message || 'Plivo service unavailable or internal error.';
break;
default:
// Use Plivo's code if it's 4xx/5xx but not explicitly handled
if (error.statusCode >= 400 && error.statusCode < 600) {
statusCode = error.statusCode;
errorMessage = error.message || `Plivo API error (${error.statusCode}).`;
} else {
// Unexpected Plivo status code_ treat as internal server error
errorMessage = `Unexpected Plivo status code ${error.statusCode}: ${error.message}`;
}
}
}
res.status(statusCode).json({
error: errorMessage_
details: error.message // Include Plivo's original message for debugging
});
}
});
// --- Start Server ---
// ... (if require.main === module block remains the same)
// --- Graceful Shutdown ---
// ... (process.on('SIGINT') block remains the same)
// Export the app instance for testing purposes
// module.exports = app; // Already present at the end of Section 2 code
Explanation:
- Route Definition:
app.post('/send-mms'_ ...)
defines a POST endpoint at/send-mms
. - Data Extraction: It extracts
destinationNumber
_messageText
_ andmediaUrl
from the request body (req.body
). - Validation: Performs basic checks:
- Ensures all required fields are present.
- Validates the
destinationNumber
format (expects E.164 format_ e.g._+14155551212
). You might use more sophisticated validation (likelibphonenumber-js
) in production. - Validates the
mediaUrl
format using theURL
constructor.
- Plivo API Call:
- Uses
await client.messages.create(...)
to send the message. This is an asynchronous operation. - Passes the
src
(your Plivo number)_dst
(recipient)_ andtext
. - Crucially_ includes an options object with
type: 'mms'
andmedia_urls: [mediaUrl]
. Plivo accepts an array of media URLs. - Comments highlight optional parameters like
media_ids
(if you pre-upload media via the Plivo console under Messaging > MMS Media Upload) andurl
for status callbacks.
- Uses
- Success Response: If the Plivo API call is successful (doesn't throw an error), it logs the success (using
console.log
for simplicity here) and returns a202 Accepted
status to the client, indicating the request was received and is being processed by Plivo. It includes Plivo's response, which contains themessage_uuid
. - Error Handling: If
client.messages.create
throws an error (e.g., invalid credentials, invalid number, Plivo service issue), thecatch
block executes.- It logs the detailed error to the console (using
console.error
for simplicity here). - It attempts to determine an appropriate HTTP status code based on
error.statusCode
from Plivo, defaulting to 500 for unknown or non-Plivo errors. - It sends a JSON error response back to the client.
- It logs the detailed error to the console (using
- Logging Note: The code uses
console.log
andconsole.error
for demonstration purposes. Production applications should implement structured logging using libraries like Winston or Pino, as detailed in Section 5. - Advanced Error Handling: This basic
try...catch
is a starting point. Section 5 discusses more robust strategies, including retries for transient errors.
Testing the Endpoint:
You can test this endpoint using curl
or a tool like Postman.
Using curl
:
curl -X POST http://localhost:3000/send-mms \
-H "Content-Type: application/json" \
-d '{
"destinationNumber": "+14157654321",
"messageText": "Hello from Node.js! Check out this image.",
"mediaUrl": "https://media.giphy.com/media/26gscSULUcfKU7dHq/source.gif"
}'
Replace +14157654321
with a valid destination number (remember trial account limitations). Replace the mediaUrl
with any publicly accessible image or GIF URL.
Note: http://localhost:3000
is the URL for testing locally. Replace this with your application's deployed URL when testing in staging or production.
Expected Success Response (Status 202):
{
"message": "MMS sending request accepted.",
"plivoResponse": {
"message": "message(s) queued",
"messageUuid": [
"a1b2c3d4-e5f6-7890-1234-567890abcdef"
],
"apiId": "x1y2z3w4-v5u6-t7s8-r9q0-p1o2n3m4l5k6"
}
}
Expected Error Response (e.g., Status 400 - Missing Field):
{
"error": "Missing required fields: destinationNumber, messageText, mediaUrl"
}
4. Integrating with Plivo Service Details
We've already integrated the core Plivo SDK. Let's reiterate where to find the necessary credentials and settings in the Plivo console.
-
Auth ID and Auth Token (
PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
):- Purpose: These credentials authenticate your application's requests to the Plivo API.
- How to Obtain:
- Log in to your Plivo Console (https://console.plivo.com/).
- The Auth ID and Auth Token are displayed prominently on the main Overview dashboard page.
- Copy these values directly into your
.env
file.
- Security: Treat these like passwords. Keep them secure and never hardcode them directly into your source code. Use environment variables.
-
Plivo Sender Number (
PLIVO_SENDER_NUMBER
):- Purpose: This is the phone number that will appear as the sender of the MMS messages. It must be MMS-enabled.
- How to Obtain/Verify:
- In the Plivo Console, navigate to Phone Numbers > Your Numbers.
- You will see a list of your purchased numbers.
- Check the Capabilities column. Ensure the number you want to use has ""MMS"" listed.
- If you don't have an MMS-enabled number, go to Phone Numbers > Buy Numbers, filter by ""MMS"" capability, and purchase a suitable number (currently US and Canada only for sending MMS via Plivo).
- Copy the full number in E.164 format (e.g.,
+14151234567
) into your.env
file.
-
Trial Account Limitations:
- If using a Plivo trial account, you can only send MMS messages to verified phone numbers.
- How to Verify: Navigate to Phone Numbers > Sandbox Numbers in the console to add and verify destination numbers for testing.
-
Media Upload (Optional):
- Instead of providing public
media_urls
, you can upload media files directly to Plivo. - How to Upload: Go to Messaging > MMS Media Upload.
- After uploading, Plivo provides a
media_id
. You can use this ID in theclient.messages.create
call instead ofmedia_urls
:// Example using media_id const response = await client.messages.create( PLIVO_SENDER_NUMBER, destinationNumber, messageText, { type: 'mms', media_ids: ['801c2056-33ab-499c-80ef-58b574a462a2'] // Replace with your actual media_id } );
- Instead of providing public
5. Error Handling, Logging, and Retries
Our current error handling (Section 3) is basic. Production applications require more robust strategies, enhancing the structure presented earlier.
-
Consistent Error Handling: The
try...catch
block in Section 3 provides a decent starting point. Ensure all API interactions that can fail are wrapped similarly. Standardize error response formats. -
Logging:
console.log
andconsole.error
(used in Section 3 for simplicity) are insufficient for production.- Recommendation: Use a dedicated logging library like Winston or Pino.
- Configuration:
- Configure different log levels (e.g.,
info
,warn
,error
,debug
). - Log in a structured format (like JSON) for easier parsing by log management systems (e.g., Datadog, Splunk, ELK stack).
- Include contextual information in logs (request ID, timestamp, relevant data points).
- Log successful operations (e.g., MMS request queued) at
info
level and errors aterror
level.
- Configure different log levels (e.g.,
Example using Winston (Conceptual - Enhance Section 3 implementation):
npm install winston
// server.js (add near the top) const winston = require('winston'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', // Control log level via env var format: winston.format.combine( winston.format.timestamp(), winston.format.json() // Log as JSON ), transports: [ new winston.transports.Console(), // Log to console // Add file or remote transports for production // new winston.transports.File({ filename: 'error.log', level: 'error' }), // new winston.transports.File({ filename: 'combined.log' }) ], }); // --- Replace console.log/console.error in Section 3 with logger --- // Example in /send-mms endpoint: // logger.info(`Received request to send MMS to ${destinationNumber}`, { recipient: destinationNumber }); // logger.info('MMS sent successfully', { plivoResponse: response }); // logger.error('Error sending MMS via Plivo', { error: error.message, statusCode: error.statusCode, stack: error.stack });
-
Retry Mechanisms: Network issues or temporary Plivo outages might cause sending failures. Implement a retry strategy, typically with exponential backoff, to enhance the basic error handling in Section 3.
- Caution: Only retry on transient errors (e.g., 5xx status codes from Plivo, network timeouts). Do not retry on permanent errors (e.g., 4xx codes like invalid number, insufficient funds, auth failure).
- Libraries: Use libraries like
async-retry
or implement manually.
Example using
async-retry
(Conceptual - Wrap Plivo call from Section 3):npm install async-retry
// server.js (add near imports) const retry = require('async-retry'); // Assume 'logger' from Winston example above is defined // --- Inside /send-mms endpoint, replace the basic try/catch around Plivo call --- try { const response = await retry(async (bail, attempt) => { logger.info(`Attempt ${attempt} to send MMS to ${destinationNumber}`); try { // The actual Plivo API call from Section 3 return await client.messages.create( PLIVO_SENDER_NUMBER, destinationNumber, messageText, { type: 'mms', media_urls: [mediaUrl] } // ... other options ); } catch (error) { logger.warn(`Attempt ${attempt} failed: ${error.message}`, { statusCode: error.statusCode }); // Don't retry on client errors (4xx) or specific conditions if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) { // bail stops retrying and throws a wrapped error bail(new Error(`Permanent error (${error.statusCode}): ${error.message}`)); return; // Must return here after bail } // For other errors (e.g._ 5xx_ network issues)_ throw to trigger retry throw error; } }_ { retries: 3_ // Number of retries factor: 2_ // Exponential backoff factor minTimeout: 1000_ // Initial timeout (ms) maxTimeout: 5000_ // Max timeout between retries onRetry: (error_ attempt) => { logger.warn(`Retrying MMS send (attempt ${attempt}) due to error: ${error.message}`, { statusCode: error.statusCode }); }, }); logger.info('MMS sent successfully after retries:', { plivoResponse: response }); res.status(202).json({ message: 'MMS sending request accepted.', plivoResponse: response }); } catch (error) { // This catches the final error after retries or from bail() logger.error('Failed to send MMS via Plivo after retries:', { error: error.message, details: error.originalError || error }); let statusCode = 500; let errorMessage = 'Failed to send MMS after multiple attempts.'; // Use the error passed by bail (if permanent) or the last attempt's error const finalError = error.originalError || error; if (finalError.statusCode) { // Use Plivo's status code if available from the final error switch (finalError.statusCode) { case 400: statusCode = 400; break; case 401: statusCode = 401; break; // Add others as needed default: statusCode = (finalError.statusCode >= 400 && finalError.statusCode < 500) ? finalError.statusCode : 503; // Default non-client errors to 503 } errorMessage = `Plivo API error occurred: ${finalError.message}`; } else if (error.message.startsWith('Permanent error')) { // Handle errors passed explicitly to bail (often derived from 4xx) // Try to extract original status code if possible_ otherwise default to 400 const match = error.message.match(/Permanent error \((\d{3})\)/); statusCode = match ? parseInt(match[1]_ 10) : 400; errorMessage = error.message; } res.status(statusCode).json({ error: errorMessage_ details: finalError.message || error.message // Show the most relevant message }); }
-
Testing Error Scenarios:
- Use invalid Plivo credentials (
.env
) to test 401 errors. - Send to an invalidly formatted number to test 400 errors.
- Send from a non-MMS enabled number.
- Use an invalid
mediaUrl
. - Temporarily disconnect network to simulate transient errors (if testing retries).
- Check Plivo Debug Logs (Console -> Logs -> Messaging Logs) for detailed error information from Plivo's side.
- Use invalid Plivo credentials (
6. Database Schema and Data Layer (Conceptual)
While not strictly required for just sending an MMS, if you need to track message status, history, associate messages with users, or handle asynchronous status callbacks from Plivo, you'll need a database.
-
Choice of Database: PostgreSQL, MySQL, MongoDB are common choices.
-
ORM/ODM: Use an Object-Relational Mapper (ORM) like Sequelize or Prisma (for SQL) or an Object-Document Mapper (ODM) like Mongoose (for MongoDB) to simplify database interactions.
-
Example Schema (Conceptual - using Prisma syntax):
// schema.prisma datasource db { provider = ""postgresql"" // or ""mysql"", ""mongodb"" url = env(""DATABASE_URL"") } generator client { provider = ""prisma-client-js"" } model MessageLog { id String @id @default(cuid()) plivoMessageUuid String? @unique // Store Plivo's message_uuid sender String // Your Plivo number recipient String // Destination number text String? mediaUrls String[] // Store array of media URLs sent status String @default(""queued"") // e.g., queued, sent, delivered, failed, undelivered plivoStatusInfo String? // Store detailed status from Plivo callback errorCode String? // Store Plivo error code on failure createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Optional: Link to a User model // userId String? // user User? @relation(fields: [userId], references: [id]) } // Optional: User model if tracking per-user messages // model User { // id String @id @default(cuid()) // // ... other user fields // messages MessageLog[] // }
-
Implementation Steps:
- Choose DB and ORM/ODM.
- Install necessary packages (
npm install @prisma/client
,npm install pg
etc.). - Define schema (
schema.prisma
). - Run migrations (
npx prisma migrate dev
). - Initialize Prisma Client (
const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient();
). - Before calling
client.messages.create
, create a record inMessageLog
with statusqueued
. - After successfully calling
client.messages.create
, update the record with theplivoMessageUuid
returned by Plivo. - Implement a separate webhook endpoint (e.g.,
/plivo-status-callback
) to receive delivery status updates from Plivo (configured via theurl
parameter in thecreate
call or on the Plivo Application). This endpoint would find the corresponding message log using theMessageUUID
from the callback and update itsstatus
,plivoStatusInfo
, anderrorCode
.
-
Performance: Index
plivoMessageUuid
for efficient lookups when processing callbacks. Consider database connection pooling.
7. Adding Security Features
Protecting your application and API is crucial.
-
Secure Credentials: Already covered: Use environment variables (
.env
) and.gitignore
. Never commit secrets. Use secrets management systems (like AWS Secrets Manager, HashiCorp Vault) in production deployments. -
Input Validation and Sanitization:
- Validation: We added basic format validation for
destinationNumber
andmediaUrl
(Section 3). For production, use robust libraries:- Phone Numbers:
libphonenumber-js
for comprehensive E.164 validation and formatting. - General Validation:
joi
orzod
for defining and validating request body schemas.
- Phone Numbers:
- Sanitization: While less critical for
destinationNumber
andmediaUrl
(as they aren't typically rendered as HTML), always sanitize user-provided text (messageText
) if it might be stored and displayed elsewhere later, to prevent Cross-Site Scripting (XSS). Use libraries likedompurify
(if rendering in a browser context) or perform basic sanitization on the server.
- Validation: We added basic format validation for
-
Rate Limiting: Protect your API from abuse and control costs by limiting the number of requests a client can make.
- Library: Use
express-rate-limit
. - Implementation: Apply middleware globally or to specific routes.
npm install express-rate-limit
// server.js (add near middleware setup) 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 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers message: 'Too many requests from this IP, please try again after 15 minutes', }); // Apply the rate limiting middleware to API routes // Option 1: Apply to all routes starting with /api (if you structure that way) // app.use('/api', apiLimiter); // Option 2: Apply specifically to the send-mms route app.use('/send-mms', apiLimiter); // ... rest of the server.js code (before defining routes)
- Library: Use
-
Authentication/Authorization (If Applicable): If this API is not public, protect the
/send-mms
endpoint.- Methods: API Keys, JWT (JSON Web Tokens), OAuth.
- Implement middleware to verify credentials before allowing access to the sending endpoint.
-
Webhook Security (If Receiving Status Callbacks): When receiving callbacks from Plivo, verify they genuinely originated from Plivo.
- Method: Plivo includes an
X-Plivo-Signature-V3
header and nonce in its requests. Use theplivo.validateV3Signature
utility function from the SDK along with your Auth Token and the webhook URL to validate the signature. Reject requests with invalid signatures. See Plivo documentation on Webhook Security.
- Method: Plivo includes an
8. Handling Special Cases for MMS
MMS involves media and carrier specifics.
- Supported Countries: Plivo currently supports sending MMS via long codes primarily to the US and Canada. Sending to other countries may not work or may fall back to SMS. Verify Plivo's latest country support documentation. Receiving MMS is also typically limited to US/Canada numbers.
- Media File Types and Size Limits:
- Plivo supports common image types (JPEG, PNG, GIF) and some video/audio formats.
- There are size limits per message and per file, which can vary slightly by carrier. Generally, aim for files under 1MB, ideally under 600KB, for best compatibility.
- Action: Check the latest Plivo MMS documentation for supported MIME types and size recommendations. Add validation to check
mediaUrl
content type or file size if possible before sending to Plivo.
- Media URL Accessibility: The
mediaUrl
provided must be publicly accessible over the internet without authentication. Plivo's servers need to fetch this URL to attach the media. Private or localhost URLs will fail. - Multiple Media Files: Plivo allows sending multiple images/GIFs in one MMS by providing an array of URLs in
media_urls
. Be mindful of total message size limits. - Character Limits: While MMS doesn't have the strict 160-character limit of SMS, excessively long
messageText
combined with large media can still cause delivery issues on some handsets or networks. Keep text reasonably concise. - Fallback: Consider implementing logic to fall back to SMS (using Plivo's SMS API) if MMS sending fails for certain reasons (e.g., non-MMS capable number detected via lookup, specific error codes) or if the destination is outside supported MMS regions.