Send MMS with Plivo and NestJS: Complete 2025 Developer Guide
Learn how to send MMS messages using Plivo API in NestJS. Complete tutorial with validation, delivery tracking, media optimization, and production deployment best practices.
Learn how to send MMS (Multimedia Messaging Service) messages using the Plivo API within a NestJS application. This guide walks you through the complete setup – from installing dependencies to implementing advanced features like error handling, media validation, and delivery tracking.
Prerequisites for Plivo MMS in NestJS
Before you begin, ensure you have:
Node.js v20+ (LTS version recommended) and npm/yarn installed. NestJS 11 requires Node.js v20 or higher as of 2025, following the deprecation of Node.js v16 and v18.
A Plivo account with API credentials (Auth ID and Auth Token) from plivo.com.
The Plivo Node.js SDK installed via npm install plivo. The SDK is actively maintained with regular updates.
A Plivo phone number capable of sending MMS – verify MMS support in your account dashboard.
Basic understanding of NestJS concepts: modules, services, controllers, and dependency injection.
Understanding MMS with Plivo
What Is MMS?
MMS extends traditional SMS by allowing you to send multimedia content:
DTOs (Data Transfer Objects): Define the shape of incoming request data.
Error Handling: Use HttpException to return proper HTTP status codes.
Service Separation: Keep business logic in services, not controllers.
Test Your Plivo MMS Implementation
Using cURL
Test your endpoint with this command:
Bash
curl -X POST http://localhost:3000/mms/send \
-H "Content-Type: application/json" \
-d '{
"to": "+1234567890",
"text": "Hello! This is an MMS with an image.",
"mediaUrls": ["https://example.com/image.jpg"]
}'
Using Postman
Method: POST
URL:http://localhost:3000/mms/send
Headers:Content-Type: application/json
Body (raw JSON):
JSON
{"to":"+1234567890","text":"Check out this image!","mediaUrls":["https://example.com/sample.png"]}
Expected Response
JSON
{"success":true,"messageUuid":"abc123-def456-ghi789","message":"MMS sent successfully"}
Add Advanced Features to Your Plivo MMS Service
1. Input Validation with class-validator
Install validation packages:
Bash
npm install class-validator class-transformer
# or
yarn add class-validator class-transformer
Create a DTO file src/mms/dto/send-mms.dto.ts:
TypeScript
import { IsString, IsArray, IsNotEmpty, ArrayMinSize, ArrayMaxSize, Matches, MaxLength } from'class-validator';
exportclassSendMmsDto {
@IsString()
@IsNotEmpty()
@Matches(/^\+[1-9]\d{1,14}$/, {
message: 'Phone number must be in E.164 format (e.g., +1234567890)',
})
to: string;
@IsString()
@MaxLength(1600, {
message: 'Message text cannot exceed 1,600 characters',
})
text: string;
@IsArray()
@ArrayMinSize(1, { message: 'At least one media URL is required' })
@ArrayMaxSize(10, { message: 'Maximum 10 media URLs allowed' })
@IsString({ each: true })
mediaUrls: string[];
}
Rate Limiting: Implement throttling to prevent abuse
Monitoring: Configure alerts for failed messages
Database Backups: Automate regular backups of message records
Load Testing: Test your application under expected traffic
Logging: Set up centralized logging (ELK stack, CloudWatch)
Documentation: Create API documentation (use Swagger/OpenAPI)
Security: Implement webhook signature validation
Cost Monitoring: Track Plivo usage and costs
Retry Logic: Implement exponential backoff for failed sends
Frequently Asked Questions About Plivo MMS with NestJS
What is the maximum file size for Plivo MMS attachments?
Plivo supports up to 2 MB per attachment with a maximum of 10 attachments per message. The total message size (body text plus all attachments) must be less than 5 MB (source). Messages exceeding this limit will fail with Plivo error code 120. For optimal carrier delivery, keep attachments under 600 KB for US carriers and 1 MB for Canadian carriers (source).
Does Plivo support MMS in all countries?
No, MMS availability varies by country and carrier. Plivo primarily supports MMS for US and Canadian phone numbers. Check the Plivo messaging coverage page for specific country support. For international messaging, use SMS with shortened URLs linking to media content.
How long does Plivo store MMS media files?
Plivo stores media sent to customers for 1 year from the send date. Media uploaded via the Media API but not used in a message is automatically deleted after 6 hours (source). For long-term storage, implement your own media management system using cloud storage services like AWS S3 or Google Cloud Storage.
What NestJS version is required for Plivo integration?
You need NestJS 11 which requires Node.js v20 or higher (source). Node.js v16 and v18 are no longer supported as of NestJS 11. The Plivo Node.js SDK is compatible with these versions. Ensure your development environment meets these requirements before starting implementation.
How do you handle MMS delivery failures in NestJS?
Implement comprehensive error handling with try-catch blocks, validate media URLs before sending, set up webhook endpoints to receive delivery receipts, and store message status in a database for tracking. Use TypeORM or Prisma to persist delivery status updates. Consider implementing retry logic with exponential backoff for temporary failures.
Can you send MMS with multiple images using Plivo?
Yes, Plivo supports up to 10 attachments per MMS message (source). Pass an array of media URLs to the media_urls parameter when creating a message. Each attachment must be under 2 MB, and the total message size must remain under 5 MB. Ensure all URLs are publicly accessible via HTTPS for best carrier compatibility.
What image formats does Plivo MMS support?
Plivo supports JPEG, JPG, PNG, GIF, and BMP for images. For video, supported formats include MP4, 3GP, AVI, and MOV. Audio formats include MP3, WAV, AMR, and OGG. Always use standard formats and compress files appropriately to ensure maximum carrier compatibility and faster delivery times.
How do you validate Plivo webhook signatures in NestJS?
Generate an HMAC SHA1 signature using your Plivo auth token, the webhook URI, and sorted request parameters. Compare the computed signature with the X-Plivo-Signature header. This prevents unauthorized webhook requests. Use Node.js crypto module to create the HMAC and validate before processing delivery receipts.
What is the cost of sending MMS with Plivo?
MMS pricing varies by destination country and carrier. US MMS typically costs $0.005–$0.01 per message segment, with additional media hosting fees. Check your Plivo account dashboard for specific pricing for your use case. MMS messages with multiple attachments may incur higher costs than SMS.
How do you implement rate limiting for Plivo MMS in NestJS?
Use the @nestjs/throttler package to implement rate limiting at the controller level. Configure TTL (time to live) and request limits based on your application needs. This prevents API abuse and helps manage Plivo API costs. Consider implementing queue systems like Bull or BullMQ for high-volume message sending to better control throughput.
Conclusion
You now have a robust foundation for sending MMS messages with Plivo and NestJS. This guide covered:
Integrate the Plivo Node.js SDK into your NestJS application. Create a Plivo service to handle API interactions, a controller with a POST endpoint, and a DTO for request validation. This allows you to securely send MMS messages using Plivo's robust infrastructure.
The Plivo Node.js SDK simplifies interaction with the Plivo REST API within a NestJS application. It provides convenient methods to send MMS messages, manage phone numbers, and other Plivo functionalities, streamlining the integration process.
NestJS provides a structured, modular architecture with dependency injection and TypeScript support, making it ideal for building robust and maintainable applications that interact with external APIs like Plivo.
Use media_urls when your media (images, GIFs) are publicly accessible via URLs. Use media_ids if you've pre-uploaded media to Plivo's servers, referencing their unique identifiers. Uploading to Plivo first is useful for private or larger media files.
Plivo generally allows around 2MB per media file and a maximum of 10 attachments per message. Supported types include common image formats like JPEG, PNG, and GIF. Check Plivo's official documentation for the most current limits and supported media types, as these can change.
Create a .env file in your project root and store your Plivo Auth ID, Auth Token, and sender number. Import and configure the @nestjs/config module in your app.module.ts to load these variables securely. Add .env to your .gitignore file.
Create a Data Transfer Object (DTO) and use class-validator decorators (e.g., @IsPhoneNumber, @IsUrl) to define validation rules. Apply the ValidationPipe in your controller or globally to automatically validate incoming requests.
Implement a try...catch block around your Plivo API calls in the service. Log the error details and throw a NestJS exception (e.g., InternalServerErrorException) to handle errors gracefully without exposing sensitive information to the client.
The 202 Accepted status code signifies that the request to send the MMS has been accepted and is being processed asynchronously. It does not guarantee immediate delivery but confirms that the message is queued for sending.
Yes, using libraries like async-retry can help implement retry logic with exponential backoff. This improves the reliability of MMS delivery by handling transient network or API issues.
Plivo primarily offers MMS for US and Canadian numbers. Support for other countries may be limited or result in fallback to SMS. Verify Plivo's documentation for the latest list of supported countries.
Leverage NestJS's asynchronous nature, initialize the Plivo client once, and avoid blocking operations. While caching is less applicable to sending, it could help with related tasks like user profile retrieval if required.
Use environment variables for credentials, input validation with ValidationPipe, and implement rate limiting with @nestjs/throttler. If your API is public, add authentication/authorization using Guards.
Trial accounts usually restrict MMS sending to verified numbers in your Plivo sandbox. Ensure you have added the recipient numbers to your verified list within the Plivo console.
Learn how to send MMS (Multimedia Messaging Service) messages using the Plivo API within a NestJS application. This guide walks you through the complete setup – from installing dependencies to implementing advanced features like error handling, media validation, and delivery tracking.
Prerequisites for Plivo MMS in NestJS
Before you begin, ensure you have:
npm install plivo. The SDK is actively maintained with regular updates.Understanding MMS with Plivo
What Is MMS?
MMS extends traditional SMS by allowing you to send multimedia content:
Media Types and Size Limits for Plivo MMS
Per official Plivo documentation, the following limits apply:
Set Up a NestJS Project for Plivo MMS
Step 1: Create a New NestJS Application
Open your terminal and run:
npm i -g @nestjs/cli nest new plivo-mms-app cd plivo-mms-appChoose your preferred package manager when prompted (npm or yarn).
Step 2: Install Plivo Node.js SDK
Install the official Plivo SDK:
npm install plivo # or yarn add plivoStep 3: Configure Environment Variables
Create a
.envfile in your project root:Install the config package to manage environment variables:
npm install @nestjs/config # or yarn add @nestjs/configUpdate
app.module.ts:import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, // Makes ConfigService available throughout the app }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {}Create an MMS Service in NestJS
Generate a Service
Run this command to generate a new service:
This creates
mms.service.tsandmms.service.spec.tsin thesrcfolder.Implement Basic MMS Functionality
Update
src/mms/mms.service.ts:import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as plivo from 'plivo'; @Injectable() export class MmsService { private readonly logger = new Logger(MmsService.name); private client: plivo.Client; private fromNumber: string; constructor(private configService: ConfigService) { const authId = this.configService.get<string>('PLIVO_AUTH_ID'); const authToken = this.configService.get<string>('PLIVO_AUTH_TOKEN'); this.fromNumber = this.configService.get<string>('PLIVO_PHONE_NUMBER'); this.client = new plivo.Client(authId, authToken); this.logger.log('Plivo MMS Service initialized'); } async sendMMS( to: string, text: string, mediaUrls: string[], ): Promise<any> { try { const response = await this.client.messages.create({ src: this.fromNumber, dst: to, text: text, type: 'mms', media_urls: mediaUrls, }); this.logger.log(`MMS sent successfully: ${JSON.stringify(response)}`); return response; } catch (error) { this.logger.error(`Failed to send MMS: ${error.message}`); throw error; } } }Key Points:
ConfigServiceinjects environment variables.Create an MMS Controller to Handle Requests
Generate a Controller
Run:
Implement the Controller
Update
src/mms/mms.controller.ts:import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common'; import { MmsService } from './mms.service'; interface SendMmsDto { to: string; text: string; mediaUrls: string[]; } @Controller('mms') export class MmsController { constructor(private readonly mmsService: MmsService) {} @Post('send') async sendMms(@Body() sendMmsDto: SendMmsDto) { try { const response = await this.mmsService.sendMMS( sendMmsDto.to, sendMmsDto.text, sendMmsDto.mediaUrls, ); return { success: true, messageUuid: response.messageUuid, message: 'MMS sent successfully', }; } catch (error) { throw new HttpException( { success: false, message: 'Failed to send MMS', error: error.message, }, HttpStatus.INTERNAL_SERVER_ERROR, ); } } }Best Practices:
HttpExceptionto return proper HTTP status codes.Test Your Plivo MMS Implementation
Using cURL
Test your endpoint with this command:
curl -X POST http://localhost:3000/mms/send \ -H "Content-Type: application/json" \ -d '{ "to": "+1234567890", "text": "Hello! This is an MMS with an image.", "mediaUrls": ["https://example.com/image.jpg"] }'Using Postman
http://localhost:3000/mms/sendContent-Type: application/json{ "to": "+1234567890", "text": "Check out this image!", "mediaUrls": ["https://example.com/sample.png"] }Expected Response
{ "success": true, "messageUuid": "abc123-def456-ghi789", "message": "MMS sent successfully" }Add Advanced Features to Your Plivo MMS Service
1. Input Validation with class-validator
Install validation packages:
npm install class-validator class-transformer # or yarn add class-validator class-transformerCreate a DTO file
src/mms/dto/send-mms.dto.ts:import { IsString, IsArray, IsNotEmpty, ArrayMinSize, ArrayMaxSize, Matches, MaxLength } from 'class-validator'; export class SendMmsDto { @IsString() @IsNotEmpty() @Matches(/^\+[1-9]\d{1,14}$/, { message: 'Phone number must be in E.164 format (e.g., +1234567890)', }) to: string; @IsString() @MaxLength(1600, { message: 'Message text cannot exceed 1,600 characters', }) text: string; @IsArray() @ArrayMinSize(1, { message: 'At least one media URL is required' }) @ArrayMaxSize(10, { message: 'Maximum 10 media URLs allowed' }) @IsString({ each: true }) mediaUrls: string[]; }Enable global validation in
main.ts:import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true, // Strip properties that don't have decorators forbidNonWhitelisted: true, // Throw error if non-whitelisted properties exist transform: true, // Auto-transform payloads to DTO instances })); await app.listen(3000); } bootstrap();Update the controller to use the DTO:
import { Controller, Post, Body } from '@nestjs/common'; import { MmsService } from './mms.service'; import { SendMmsDto } from './dto/send-mms.dto'; @Controller('mms') export class MmsController { constructor(private readonly mmsService: MmsService) {} @Post('send') async sendMms(@Body() sendMmsDto: SendMmsDto) { // Validation happens automatically const response = await this.mmsService.sendMMS( sendMmsDto.to, sendMmsDto.text, sendMmsDto.mediaUrls, ); return { success: true, messageUuid: response.messageUuid, }; } }Troubleshoot Common MMS Issues
Why Is My MMS Not Being Delivered?
Symptoms: Message status shows "failed" or "undelivered"
Solutions:
Why Is Media Not Displaying in My MMS?
Symptoms: Recipient receives text but no media
Solutions:
Why Are My MMS Messages Taking Too Long to Send?
Symptoms: Messages take longer than expected to send
Solutions:
Why Is My Webhook Not Receiving Delivery Events?
Symptoms: Delivery status callback never fires
Solutions:
Best Practices for Production Plivo MMS
1. Error Handling
Implement comprehensive error handling:
async sendMMS(to: string, text: string, mediaUrls: string[]): Promise<any> { try { await this.validateMediaUrls(mediaUrls); const response = await this.client.messages.create({ src: this.fromNumber, dst: to, text, type: 'mms', media_urls: mediaUrls, }); return response; } catch (error) { // Log detailed error information this.logger.error(`MMS Send Error: ${error.message}`, { to, mediaUrls, errorCode: error.code, errorDetails: error.response?.data, }); // Re-throw with user-friendly message if (error.code === 120) { throw new BadRequestException('Message size exceeds 5 MB limit'); } throw new InternalServerErrorException('Failed to send MMS'); } }2. Rate Limiting
Implement rate limiting to avoid overwhelming the Plivo API:
Update
app.module.ts:import { ThrottlerModule } from '@nestjs/throttler'; @Module({ imports: [ ThrottlerModule.forRoot([{ ttl: 60000, // 60 seconds limit: 10, // 10 requests per minute }]), // ... other imports ], }) export class AppModule {}Apply to controller:
import { UseGuards } from '@nestjs/common'; import { ThrottlerGuard } from '@nestjs/throttler'; @Controller('mms') @UseGuards(ThrottlerGuard) export class MmsController { // ... controller methods }3. Media Optimization
Optimize media before sending:
import sharp from 'sharp'; async optimizeImage(imageBuffer: Buffer): Promise<Buffer> { return await sharp(imageBuffer) .resize(1200, 1200, { fit: 'inside', withoutEnlargement: true, }) .jpeg({ quality: 80 }) .toBuffer(); }Install sharp:
4. Logging and Monitoring
Implement structured logging:
this.logger.log({ event: 'mms_sent', messageUuid: response.messageUuid, to, mediaCount: mediaUrls.length, timestamp: new Date().toISOString(), });Consider integrating with monitoring tools:
5. Testing
Create comprehensive tests:
// src/mms/mms.service.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { MmsService } from './mms.service'; import { ConfigService } from '@nestjs/config'; describe('MmsService', () => { let service: MmsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ MmsService, { provide: ConfigService, useValue: { get: jest.fn((key: string) => { const config = { PLIVO_AUTH_ID: 'test_auth_id', PLIVO_AUTH_TOKEN: 'test_auth_token', PLIVO_PHONE_NUMBER: '+1234567890', }; return config[key]; }), }, }, ], }).compile(); service = module.get<MmsService>(MmsService); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('sendMMS', () => { it('should send MMS successfully', async () => { const mockResponse = { messageUuid: 'test-uuid-123' }; jest.spyOn(service['client'].messages, 'create').mockResolvedValue(mockResponse as any); const result = await service.sendMMS( '+1234567890', 'Test message', ['https://example.com/image.jpg'], ); expect(result).toEqual(mockResponse); }); it('should throw error for invalid media URL', async () => { await expect( service.sendMMS( '+1234567890', 'Test', ['invalid-url'], ), ).rejects.toThrow(); }); }); });Run tests:
npm run test # or yarn testProduction Deployment Checklist
Before deploying to production:
Frequently Asked Questions About Plivo MMS with NestJS
What is the maximum file size for Plivo MMS attachments?
Plivo supports up to 2 MB per attachment with a maximum of 10 attachments per message. The total message size (body text plus all attachments) must be less than 5 MB (source). Messages exceeding this limit will fail with Plivo error code 120. For optimal carrier delivery, keep attachments under 600 KB for US carriers and 1 MB for Canadian carriers (source).
Does Plivo support MMS in all countries?
No, MMS availability varies by country and carrier. Plivo primarily supports MMS for US and Canadian phone numbers. Check the Plivo messaging coverage page for specific country support. For international messaging, use SMS with shortened URLs linking to media content.
How long does Plivo store MMS media files?
Plivo stores media sent to customers for 1 year from the send date. Media uploaded via the Media API but not used in a message is automatically deleted after 6 hours (source). For long-term storage, implement your own media management system using cloud storage services like AWS S3 or Google Cloud Storage.
What NestJS version is required for Plivo integration?
You need NestJS 11 which requires Node.js v20 or higher (source). Node.js v16 and v18 are no longer supported as of NestJS 11. The Plivo Node.js SDK is compatible with these versions. Ensure your development environment meets these requirements before starting implementation.
How do you handle MMS delivery failures in NestJS?
Implement comprehensive error handling with try-catch blocks, validate media URLs before sending, set up webhook endpoints to receive delivery receipts, and store message status in a database for tracking. Use TypeORM or Prisma to persist delivery status updates. Consider implementing retry logic with exponential backoff for temporary failures.
Can you send MMS with multiple images using Plivo?
Yes, Plivo supports up to 10 attachments per MMS message (source). Pass an array of media URLs to the
media_urlsparameter when creating a message. Each attachment must be under 2 MB, and the total message size must remain under 5 MB. Ensure all URLs are publicly accessible via HTTPS for best carrier compatibility.What image formats does Plivo MMS support?
Plivo supports JPEG, JPG, PNG, GIF, and BMP for images. For video, supported formats include MP4, 3GP, AVI, and MOV. Audio formats include MP3, WAV, AMR, and OGG. Always use standard formats and compress files appropriately to ensure maximum carrier compatibility and faster delivery times.
How do you validate Plivo webhook signatures in NestJS?
Generate an HMAC SHA1 signature using your Plivo auth token, the webhook URI, and sorted request parameters. Compare the computed signature with the
X-Plivo-Signatureheader. This prevents unauthorized webhook requests. Use Node.js crypto module to create the HMAC and validate before processing delivery receipts.What is the cost of sending MMS with Plivo?
MMS pricing varies by destination country and carrier. US MMS typically costs $0.005–$0.01 per message segment, with additional media hosting fees. Check your Plivo account dashboard for specific pricing for your use case. MMS messages with multiple attachments may incur higher costs than SMS.
How do you implement rate limiting for Plivo MMS in NestJS?
Use the
@nestjs/throttlerpackage to implement rate limiting at the controller level. Configure TTL (time to live) and request limits based on your application needs. This prevents API abuse and helps manage Plivo API costs. Consider implementing queue systems like Bull or BullMQ for high-volume message sending to better control throughput.Conclusion
You now have a robust foundation for sending MMS messages with Plivo and NestJS. This guide covered:
Next Steps
Additional Resources