code examples

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

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.

How to Send MMS Messages with Plivo API and NestJS

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:

  • Images: JPEG, PNG, GIF
  • Videos: MP4, 3GP
  • Audio: MP3, AMR
  • vCards: Contact information
  • PDF Documents: (Carrier support varies)

Media Types and Size Limits for Plivo MMS

Per official Plivo documentation, the following limits apply:

  • Outbound MMS Limits:
    • Up to 10 attachments per message (combination of media_urls and media_ids)
    • Maximum 2 MB per attachment
    • Total message size (body text + all attachments) must be less than 5 MB
    • Message body can be up to 1,600 characters (4.8 KB)
    • Messages exceeding 5 MB will fail with Plivo error code 120
  • Carrier-Specific Recommendations (source):
    • For US carriers: Use attachments no larger than 600 KB for non-JPEG/PNG/GIF files
    • AT&T long code: 0.675 MB limit; T-Mobile: 1.5 MB limit; Verizon: 0.675 MB limit
    • For Canada: Keep all MMS attachments smaller than 1 MB across all networks
  • Media Retention (source): Plivo saves media sent to customers for 1 year. Unused media (uploaded but not sent) is deleted after 6 hours.
  • Supported Formats:
    • Images: JPEG, JPG, PNG, GIF, BMP
    • Video: MP4, 3GP, AVI, MOV
    • Audio: MP3, WAV, AMR, OGG

Set Up a NestJS Project for Plivo MMS

Step 1: Create a New NestJS Application

Open your terminal and run:

bash
npm i -g @nestjs/cli
nest new plivo-mms-app
cd plivo-mms-app

Choose your preferred package manager when prompted (npm or yarn).

Step 2: Install Plivo Node.js SDK

Install the official Plivo SDK:

bash
npm install plivo
# or
yarn add plivo

Step 3: Configure Environment Variables

Create a .env file in your project root:

env
PLIVO_AUTH_ID=your_auth_id
PLIVO_AUTH_TOKEN=your_auth_token
PLIVO_PHONE_NUMBER=your_plivo_phone_number

Install the config package to manage environment variables:

bash
npm install @nestjs/config
# or
yarn add @nestjs/config

Update app.module.ts:

typescript
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:

bash
nest generate service mms

This creates mms.service.ts and mms.service.spec.ts in the src folder.

Implement Basic MMS Functionality

Update src/mms/mms.service.ts:

typescript
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:

  • Dependency Injection: The ConfigService injects environment variables.
  • Client Initialization: Initialize the Plivo client once in the constructor.
  • Error Handling: The try-catch block captures errors and logs them.
  • Type Safety: TypeScript provides better IDE support and catches errors at compile time.

Create an MMS Controller to Handle Requests

Generate a Controller

Run:

bash
nest generate controller mms

Implement the Controller

Update src/mms/mms.controller.ts:

typescript
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:

  • 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

  1. Method: POST
  2. URL: http://localhost:3000/mms/send
  3. Headers: Content-Type: application/json
  4. 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';

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:

typescript
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:

typescript
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:

  • Check Phone Number: Verify you're using E.164 format (e.g., +1234567890).
  • Verify MMS Support: Ensure the recipient's carrier supports MMS.
  • Check Media URLs: Confirm all media URLs are publicly accessible (HTTPS recommended).
  • Size Limits: Verify total message size is under 5 MB (2 MB per attachment).
  • Carrier Routing: Some carriers have restrictions – check Plivo's carrier coverage.

Why Is Media Not Displaying in My MMS?

Symptoms: Recipient receives text but no media

Solutions:

  • File Format: Ensure media uses supported formats (JPEG, PNG, GIF, MP4).
  • URL Accessibility: Test media URLs in a browser to confirm they're publicly accessible.
  • HTTPS: Use HTTPS URLs for better carrier compatibility.
  • Content-Type Headers: Ensure your media server sends correct Content-Type headers.
  • File Size: Keep files under recommended carrier limits (600 KB for US, 1 MB for Canada).

Why Are My MMS Messages Taking Too Long to Send?

Symptoms: Messages take longer than expected to send

Solutions:

  • Media Hosting: Host media on a CDN for faster access.
  • Async Processing: Implement queue systems (Bull, RabbitMQ) for high-volume sending.
  • Connection Pooling: Reuse Plivo client instances instead of creating new ones.
  • Batch Processing: Group multiple messages when possible.

Why Is My Webhook Not Receiving Delivery Events?

Symptoms: Delivery status callback never fires

Solutions:

  • Public URL: Ensure your webhook URL is publicly accessible (use ngrok for local development).
  • HTTPS: Plivo requires HTTPS for production webhooks.
  • Response Time: Return a 200 status code within 2 seconds.
  • Signature Validation: Confirm you're not blocking requests due to invalid signatures.

Best Practices for Production Plivo MMS

1. Error Handling

Implement comprehensive error handling:

typescript
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:

bash
npm install @nestjs/throttler

Update app.module.ts:

typescript
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:

typescript
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:

typescript
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:

bash
npm install sharp

4. Logging and Monitoring

Implement structured logging:

typescript
this.logger.log({
  event: 'mms_sent',
  messageUuid: response.messageUuid,
  to,
  mediaCount: mediaUrls.length,
  timestamp: new Date().toISOString(),
});

Consider integrating with monitoring tools:

  • Sentry: Error tracking
  • Datadog: Performance monitoring
  • CloudWatch: AWS infrastructure monitoring

5. Testing

Create comprehensive tests:

typescript
// 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:

bash
npm run test
# or
yarn test

Production Deployment Checklist

Before deploying to production:

  • Environment Variables: Store credentials securely (use AWS Secrets Manager, Azure Key Vault, etc.)
  • HTTPS: Enable HTTPS for webhook endpoints
  • Error Tracking: Set up Sentry or similar service
  • 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:

  • Setting up your NestJS project with Plivo SDK
  • Implementing basic MMS sending functionality
  • Adding advanced features: validation, delivery tracking, scheduling
  • Database integration for message persistence
  • Best practices for production deployment
  • Troubleshooting common issues

Next Steps

  • Explore Plivo's Other APIs: Voice calls, Verify API for 2FA
  • Implement Message Templates: Create reusable message templates
  • Add User Interface: Build a frontend with React/Angular/Vue
  • Scale Your Application: Implement queues and worker processes for high volume
  • Monitor Performance: Set up comprehensive monitoring and alerting

Additional Resources

Frequently Asked Questions

How to send MMS messages with NestJS and Plivo?

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.

What is the Plivo Node.js SDK used for in NestJS?

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.

Why use NestJS for sending Plivo MMS messages?

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.

When should I use media_urls versus media_ids with 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.

What are the Plivo MMS size and type limits?

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.

How to set up environment variables for Plivo credentials in NestJS?

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.

How to validate incoming MMS requests in NestJS?

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.

How to handle Plivo API errors in a NestJS application?

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.

What is the purpose of the 202 Accepted status code?

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.

Can I implement retry mechanisms for sending Plivo MMS?

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.

Why is MMS sending to other countries restricted with Plivo?

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.

How to improve performance when sending MMS with Plivo?

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.

How to secure my Plivo MMS sending endpoint in NestJS?

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.

What are the Plivo trial account limitations for sending MMS?

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.