This guide provides a step-by-step walkthrough for integrating Plivo's MMS API into your RedwoodJS application. We'll build a simple service and GraphQL mutation to send MMS messages, covering setup, implementation, error handling, and deployment best practices.
By the end of this tutorial, you'll have a functional RedwoodJS backend capable of sending MMS messages, including images or other media, via Plivo to recipients in the US and Canada. This enables richer communication for user engagement, notifications, or marketing campaigns directly from your application.
Last Updated: October 26, 2023
Project Overview and Goals
Goal: To implement a feature within a RedwoodJS application that allows sending Multimedia Messaging Service (MMS) messages using the Plivo communications platform.
Problem Solved: This addresses the need to send messages containing media (images, GIFs, etc.) in addition to text, which standard SMS cannot handle effectively. It provides a programmatic way to enhance user communication directly from the application backend.
Technologies Used:
- RedwoodJS: A full-stack JavaScript/TypeScript framework for the web. We leverage its API structure (services, GraphQL) for clean implementation.
- Plivo: A cloud communications platform providing APIs for SMS, MMS, Voice, and more. We'll use their Node.js SDK.
- Node.js: The runtime environment for the RedwoodJS API side.
- Yarn: Package manager used by RedwoodJS.
- GraphQL: Query language for the API layer, standard in RedwoodJS.
System Architecture:
[Client (Web/Mobile)] <--(GraphQL Request)--> [RedwoodJS API]
|
| 1. Call Plivo Service
v
[Plivo MMS Service (`plivoMms.ts`)]
|
| 2. Use Plivo Node.js SDK
v
[Plivo API] --(MMS Delivery)--> [Recipient Mobile Device]
- The client triggers the MMS send via a GraphQL mutation to the RedwoodJS API.
- The RedwoodJS API's GraphQL resolver calls a dedicated service (
plivoMms.ts
). - The
plivoMms
service uses the Plivo Node.js SDK, configured with API credentials and sender details, to make an API request to Plivo. - Plivo handles the processing and delivery of the MMS message to the recipient's device (US/Canada only).
Prerequisites:
- Node.js (v18 or later recommended)
- Yarn (v1 or v3+)
- RedwoodJS CLI installed (
yarn global add redwoodjs
) - A Plivo account (Sign up here)
- A Plivo phone number purchased from your Plivo account that is MMS-enabled and suitable for sending to the US/Canada (usually a US or Canadian number).
- Basic familiarity with RedwoodJS concepts (services, GraphQL SDL, environment variables).
1. Setting up the Project
We'll start with a fresh RedwoodJS project. If you have an existing project, adapt the steps accordingly.
-
Create RedwoodJS App: Open your terminal and run:
yarn create redwood-app ./redwood-plivo-mms
-
Navigate to Project Directory:
cd redwood-plivo-mms
-
Install Plivo Node.js SDK: Install the official Plivo helper library specifically into the
api
workspace.yarn workspace api add plivo
Why
yarn workspace api add
? RedwoodJS uses Yarn workspaces. This command ensures theplivo
package is added as a dependency only for the API side, where it's needed. -
Configure Environment Variables: Create a
.env
file in the root of your project. This file stores sensitive credentials and configuration securely, keeping them out of version control.touch .env
Add the following variables to your
.env
file:# .env PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN PLIVO_SENDER_ID=YOUR_PLIVO_MMS_ENABLED_NUMBER
-
How to get these values:
PLIVO_AUTH_ID
&PLIVO_AUTH_TOKEN
: Log in to your Plivo Console. Your Auth ID and Auth Token are displayed prominently on the main dashboard overview page.PLIVO_SENDER_ID
: This must be a Plivo phone number you have purchased or ported to your account. Navigate to Phone Numbers -> Your Numbers in the Plivo console.- Select a number that has MMS capability enabled (check the ""Capabilities"" column).
- Ensure it's a US or Canadian number if sending to the US/Canada.
- Copy the number in E.164 format (e.g.,
+14155551234
).
-
Security: Ensure your
.env
file is listed in your.gitignore
file (RedwoodJS includes this by default) to prevent accidentally committing secrets. -
Note: RedwoodJS automatically loads variables from the
.env
file into the Node.jsprocess.env
object, making them accessible in your API-side code (likeprocess.env.PLIVO_AUTH_ID
).
-
-
Project Structure: We will primarily work within the
api
directory:api/src/lib/logger.ts
: Redwood's built-in logger.api/src/services/
: Where our Plivo MMS service will reside.api/src/graphql/
: Where the GraphQL schema definition (SDL) for our mutation will live.api/src/functions/graphql.ts
: The main GraphQL endpoint handler (often requires no changes for basic service/SDL usage)..env
: Stores environment variables (already created).
2. Implementing Core Functionality (Plivo Service)
We'll create a RedwoodJS service to encapsulate the logic for sending MMS messages via Plivo.
-
Generate the Service: Use the Redwood CLI to generate the service files:
yarn rw g service plivoMms
This creates:
api/src/services/plivoMms/plivoMms.ts
: The service logic file.api/src/services/plivoMms/plivoMms.scenarios.ts
: For seeding data during tests.api/src/services/plivoMms/plivoMms.test.ts
: The unit test file.
-
Implement the
sendMms
Function: Openapi/src/services/plivoMms/plivoMms.ts
and replace its contents with the following:// api/src/services/plivoMms/plivoMms.ts import * as plivo from 'plivo' import type { MessageCreateParams } from 'plivo/dist/resources/messages' // Import type for clarity import { logger } from 'src/lib/logger' interface SendMmsArgs { to: string // Recipient phone number in E.164 format mediaUrl: string // Publicly accessible URL of the media file text?: string // Optional text body } interface SendMmsResponse { success: boolean message: string messageUuid?: string | null // Plivo's identifier for the message error?: string | null } /** * Sends an MMS message using the Plivo API. * Requires PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_SENDER_ID environment variables. * MMS sending is only supported to US and Canada numbers via Plivo. * * @param to - The recipient phone number in E.164 format (e.g., +15551234567). * @param mediaUrl - A *publicly accessible* URL to the media file (image, gif, etc.). * @param text - Optional text to include with the MMS. * @returns {Promise<SendMmsResponse>} - An object indicating success or failure. */ export const sendMms = async ({ to, mediaUrl, text, }: SendMmsArgs): Promise<SendMmsResponse> => { logger.info({ to, mediaUrl }, 'Attempting to send Plivo MMS') // --- Input Validation --- if (!process.env.PLIVO_AUTH_ID || !process.env.PLIVO_AUTH_TOKEN || !process.env.PLIVO_SENDER_ID) { logger.error('Plivo credentials or sender ID missing in environment variables.') return { success: false, message: 'Server configuration error: Plivo credentials missing.', error: 'Missing environment variables', } } if (!to || !mediaUrl) { logger.warn('Missing required arguments: `to` or `mediaUrl`.') return { success: false, message: 'Recipient number (to) and media URL (mediaUrl) are required.', error: 'Missing required arguments', } } // Basic E.164 format check (adjust regex for stricter validation if needed) if (!/^\+\d{11,15}$/.test(to)) { logger.warn({ recipient: to }, 'Invalid recipient phone number format.') return { success: false, message: 'Invalid recipient number format. Use E.164 (e.g., +15551234567).', error: 'Invalid E.164 format', } } // Check if mediaUrl looks like a valid URL (basic check) try { new URL(mediaUrl); } catch (_) { logger.warn({ mediaUrl }, 'Invalid media URL format.'); return { success: false, message: 'Invalid media URL format.', error: 'Invalid URL', } } // --- Plivo Client Initialization --- const client = new plivo.Client( process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN ) // --- Plivo API Call --- try { const params: MessageCreateParams = { src: process.env.PLIVO_SENDER_ID, // Sender ID from .env dst: to, // Recipient number text: text || ' ', // Plivo requires non-empty text, even for MMS. Use a space if no text provided. type: 'mms', // Specify MMS type media_urls: [mediaUrl], // Array containing the media URL // Optional: Add URL for delivery status callbacks // url: 'YOUR_STATUS_CALLBACK_URL', // method: 'POST', } logger.debug({ params }, 'Sending request to Plivo API') const response = await client.messages.create(params) logger.info({ plivoResponse: response }, `Plivo MMS queued successfully to ${to}`) // Plivo typically returns message_uuid in an array const messageUuid = response.messageUuid?.[0] || null return { success: true, message: response.message || 'MMS successfully queued for sending.', messageUuid: messageUuid, } } catch (error) { logger.error({ error, to, mediaUrl }, 'Error sending Plivo MMS') // Attempt to extract a more specific error message from Plivo's response const plivoErrorMessage = error?.message || 'Unknown Plivo API error.' return { success: false, message: `Failed to send MMS: ${plivoErrorMessage}`, messageUuid: null, error: plivoErrorMessage, // Include Plivo's error if available } } }
- Why this approach?
- Encapsulation: Keeps Plivo-specific logic isolated within this service.
- Type Safety: Uses TypeScript interfaces (
SendMmsArgs
,SendMmsResponse
) for better code clarity and safety. - Validation: Includes checks for environment variables and basic input formats (
to
,mediaUrl
). - Clear Logging: Uses Redwood's
logger
for different levels (info, warn, error, debug) to aid troubleshooting. - Error Handling: Wraps the Plivo API call in a
try...catch
block and returns a structured response indicating success or failure, including potential error messages from Plivo. - Configuration: Reads sensitive credentials and the sender ID directly from environment variables.
text: ' '
: Plivo's API often requires thetext
field even for MMS. Sending a single space satisfies this requirement if no specific text is provided.media_urls
: Note the parameter name used by the Plivo Node.js SDK ismedia_urls
, expecting an array of strings.
- Why this approach?
3. Building a Complete API Layer (GraphQL)
Now, let's expose our sendMms
service function through the RedwoodJS GraphQL API.
-
Define the GraphQL Mutation (SDL): Create a new file
api/src/graphql/plivoMms.sdl.ts
and add the following schema definition:// api/src/graphql/plivoMms.sdl.ts export const schema = gql` type PlivoMmsResponse { success: Boolean! message: String! messageUuid: String error: String } type Mutation { """""" Sends an MMS message via Plivo to a US or Canadian number. Requires a publicly accessible media URL. """""" sendPlivoMms( to: String! mediaUrl: String! text: String ): PlivoMmsResponse! @requireAuth # Or use @skipAuth if authentication is not required for this endpoint } `
PlivoMmsResponse
Type: Defines the structure of the data returned by the mutation. It mirrors theSendMmsResponse
interface from our service.sendPlivoMms
Mutation: Defines the mutation name, its input arguments (to
,mediaUrl
,text
), and the response type (PlivoMmsResponse!
).to: String!
: Recipient number (required).mediaUrl: String!
: Media URL (required).text: String
: Optional text body.
@requireAuth
: This directive enforces that the user must be authenticated to call this mutation. Change to@skipAuth
if this specific mutation should be publicly accessible, but be cautious about potential abuse. Adjust authentication based on your application's needs. Redwood automatically wires this SDL definition to the corresponding service function (sendMms
inplivoMms.ts
).
-
Testing with GraphQL Playground: Once you run
yarn rw dev
, RedwoodJS provides a GraphQL Playground (usually athttp://localhost:8911/graphql
) where you can test this mutation.Example Mutation Request:
mutation SendMyMms { sendPlivoMms( to: ""+15551234567"" # Replace with a valid E.164 test number mediaUrl: ""https://media.giphy.com/media/26gscSULUcfKU7dHq/source.gif"" # Example public GIF URL text: ""Check out this cool GIF!"" ) { success message messageUuid error } }
Expected Success Response (Example):
{ ""data"": { ""sendPlivoMms"": { ""success"": true, ""message"": ""MMS successfully queued for sending."", ""messageUuid"": ""xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"", ""error"": null } } }
Expected Failure Response (Example - Invalid Number):
{ ""data"": { ""sendPlivoMms"": { ""success"": false, ""message"": ""Invalid recipient number format. Use E.164 (e.g., +15551234567)."", ""messageUuid"": null, ""error"": ""Invalid E.164 format"" } } }
Expected Failure Response (Example - Plivo API Error):
{ ""data"": { ""sendPlivoMms"": { ""success"": false, ""message"": ""Failed to send MMS: Authentication credentials invalid <or other Plivo error>"", ""messageUuid"": null, ""error"": ""Authentication credentials invalid <or other Plivo error>"" } } }
4. Integrating with Plivo (Configuration Recap)
This section summarizes the critical Plivo configuration steps already covered.
- Plivo Account: Ensure you have an active Plivo account. Trial accounts have limitations (see Caveats).
- API Credentials:
- Navigate to the Plivo Console Dashboard.
- Copy your
Auth ID
andAuth Token
. - Store these securely in your
.env
file asPLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
. Never commit these to version control.
- MMS-Enabled Sender ID (Phone Number):
- Go to Phone Numbers -> Your Numbers in the Plivo Console.
- Verify you have a US or Canadian number with ""MMS"" listed under ""Capabilities"". Purchase one if necessary (""Buy Numbers"" section).
- Copy the number in E.164 format (e.g.,
+14155551234
). - Store this in your
.env
file asPLIVO_SENDER_ID
.
- Media URL: The
mediaUrl
you provide must be publicly accessible over the internet. Plivo's servers need to fetch the media from this URL to attach it to the MMS. Private or localhost URLs will not work.
5. Error Handling, Logging, and Retry Mechanisms
- Error Handling: Our service function (
sendMms
) implements basic error handling:- Checks for missing configuration (
.env
variables). - Performs basic input validation (required fields,
to
format,mediaUrl
format). - Uses a
try...catch
block around theclient.messages.create
call to capture exceptions from the Plivo SDK or API. - Returns a structured
SendMmsResponse
object containingsuccess: false
and anerror
message upon failure.
- Checks for missing configuration (
- Logging: RedwoodJS's built-in
logger
is used:logger.info
: Records initiation attempts and successful queuing.logger.warn
: Records non-critical issues like invalid input formats before attempting the API call.logger.error
: Records exceptions caught during the Plivo API interaction, including the error object.logger.debug
: Can be used for more verbose logging during development (e.g., logging the exact params sent to Plivo). Adjust log levels inapi/src/lib/logger.ts
or via environment variables (LOG_LEVEL
) for different environments.
- Retry Mechanisms:
- For transient network errors or temporary Plivo API issues, implementing a retry strategy can improve reliability.
- Common approach: Exponential backoff (e.g., retry after 1s, then 2s, then 4s).
- Libraries like
async-retry
can simplify this. - To add retries, first install the package:
yarn workspace api add async-retry
- Example (Conceptual usage within the
sendMms
function):// Inside sendMms function, replacing the try...catch block around client.messages.create import retry from 'async-retry'; // ... other imports and code ... try { // The actual Plivo API call is wrapped in the retry logic const response = await retry( async (bail, attemptNum) => { // Use attemptNum from retry try { const params: MessageCreateParams = { src: process.env.PLIVO_SENDER_ID, dst: to, text: text || ' ', type: 'mms', media_urls: [mediaUrl], }; logger.debug({ params, attempt: attemptNum }, 'Sending request to Plivo API'); const result = await client.messages.create(params); // Optional: Check for specific Plivo errors that shouldn't be retried // if (result.error && result.error === 'some_permanent_error_code') { // bail(new Error('Permanent error from Plivo, not retrying.')); // return; // bail throws, so this isn't strictly needed // } return result; // Success, return the Plivo response } catch (error) { // Don't retry certain client-side or fatal errors if (error.message.includes('invalid') || error.message.includes('Authentication')) { logger.warn({ error, attempt: attemptNum }, 'Non-retriable error detected, stopping retries.'); bail(error); // Use bail to stop retrying for these errors return; // Exit the async function for bail } // Log the retry attempt and throw the error to trigger the next retry logger.warn({ error, attempt: attemptNum }, 'Plivo API call failed, retrying...'); throw error; } }, { retries: 3, // Total number of attempts = 1 initial + 3 retries = 4 factor: 2, // Exponential backoff factor (1s, 2s, 4s) minTimeout: 1000, // Initial timeout in ms onRetry: (error, attempt) => { // This callback is invoked before each retry logger.warn(`Retrying Plivo MMS send (attempt ${attempt}) due to error: ${error.message}`); } } ); // If retry is successful, process the response logger.info({ plivoResponse: response }, `Plivo MMS queued successfully to ${to} after retries`); const messageUuid = response.messageUuid?.[0] || null; return { success: true, message: response.message || 'MMS successfully queued for sending after retries.', messageUuid: messageUuid, }; } catch (error) { // This catch block executes if all retries fail logger.error({ error, to, mediaUrl }, 'Error sending Plivo MMS after all retries'); const plivoErrorMessage = error?.message || 'Unknown Plivo API error after retries.'; return { success: false, message: `Failed to send MMS after retries: ${plivoErrorMessage}`, messageUuid: null, error: plivoErrorMessage, }; }
- Caution: Be careful not to retry errors that are definitely not transient (e.g., invalid credentials, invalid destination number). Use the
bail
function inasync-retry
to prevent retries for such cases.
6. Database Schema and Data Layer
For this specific task of sending an MMS, a database isn't strictly required by the core Plivo integration itself. However, in a real-world application, you would likely want to:
- Log Message Status: Store the
messageUuid
returned by Plivo along with recipient details, timestamp, and initial status (queued
). - Handle Delivery Reports: Plivo can send webhook callbacks to your application with delivery status updates (e.g.,
sent
,delivered
,failed
). You'd need:- A database table to store message logs (
MessageLog
schema). - A separate RedwoodJS function (webhook handler) to receive these callbacks.
- Logic to update the status of the corresponding message in your
MessageLog
table using themessageUuid
.
- A database table to store message logs (
Example Prisma Schema (Conceptual):
// api/db/schema.prisma
model PlivoMessageLog {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
to String
from String // The Plivo sender ID used
text String?
mediaUrl String?
messageUuid String? @unique // Plivo's unique ID
status String @default(""queued"") // e.g., queued, sent, delivered, failed, undelivered
plivoError String? // Store error message from Plivo on failure
direction String @default(""outbound"") // Could also log inbound messages
// Optional: Link to user or other relevant models
// userId String?
// user User? @relation(fields: [userId], references: [id])
}
Implementing the webhook handler and database logging is beyond the scope of this initial setup guide but is a recommended enhancement for production applications.
7. Security Features
- API Key Security:
- Plivo
Auth ID
andAuth Token
are stored securely in.env
and accessed as environment variables. .env
is excluded from Git via.gitignore
.- Environment variables are configured securely in deployment environments (see Section 12).
- Plivo
- Input Validation:
- The
sendMms
service performs basic checks on theto
number format (E.164) andmediaUrl
format. Robust validation (e.g., using a library likelibphonenumber-js
for phone numbers) is recommended for production. - Sanitize any
text
input if it originates directly from users to prevent injection attacks (though less critical when sending out via Plivo, still good practice).
- The
- Authentication/Authorization:
- The GraphQL mutation
sendPlivoMms
uses@requireAuth
(by default). Ensure your RedwoodJS app has authentication set up (yarn rw setup auth ...
) if needed. Only authenticated and authorized users should be able to trigger MMS sends if the action is tied to user activity. - If the endpoint needs to be public (
@skipAuth
), implement other safeguards like API keys or stricter rate limiting.
- The GraphQL mutation
- Rate Limiting:
- To prevent abuse (accidental or malicious), implement rate limiting on the
sendPlivoMms
mutation. - This can be done using RedwoodJS directives, API gateway features (if applicable), or middleware. Limit requests per user/IP address over a specific time window.
- To prevent abuse (accidental or malicious), implement rate limiting on the
- Media URL Security: Ensure the system generating
mediaUrl
values does not allow users to input arbitrary URLs pointing to internal resources or malicious sites. Validate the source of media URLs.
8. Handling Special Cases
- US/Canada Limitation: Plivo MMS sending is only supported to US and Canada numbers. Attempts to send elsewhere will fail. Your application logic should account for this, potentially disabling the feature or warning users if the recipient is outside these regions.
- E.164 Format: Plivo requires destination (
dst
) numbers in E.164 format (+
followed by country code and number, e.g.,+14155551234
). Ensure your frontend or backend properly formats numbers before passing them to the service. - Public Media URL: The
mediaUrl
must be publicly accessible. Plivo needs to download the content. Localhost URLs, URLs behind firewalls, or URLs requiring authentication will not work. Consider using cloud storage (like AWS S3, Google Cloud Storage) with public read access for the media files. - File Size/Type Limits: Plivo imposes limits on the size and types of media files supported via MMS. Refer to the latest Plivo MMS documentation regarding supported file types and sizes for current restrictions. Handle potential errors related to unsupported file types or sizes within your application logic if possible, or rely on Plivo's API error response.
- Trial Account Limitations: Plivo trial accounts can typically only send messages (SMS/MMS) to phone numbers that have been verified (sandboxed) within the Plivo console (""Phone Numbers"" -> ""Sandbox Numbers""). Sending to unverified numbers will fail until the account is upgraded.
9. Performance Optimizations
For sending single MMS messages triggered by user actions, performance is usually less critical than reliability. However, consider:
- Asynchronous Sending: The current implementation is already asynchronous (
async/await
). ThesendMms
function returns quickly after queueing the message with Plivo; it doesn't wait for final delivery. - Batching: Plivo's standard
messages.create
API sends one message per request. For high-volume sending, investigate Plivo's Powerpack feature or bulk messaging capabilities if available, which might offer more optimized throughput, though potentially with different API interactions. - Media Hosting: Ensure your media hosting solution (where
mediaUrl
points) is performant and scalable to handle Plivo fetching the files quickly. Using a Content Delivery Network (CDN) for media files is recommended. - Database Writes: If logging messages to a database (Section 6), ensure database writes are efficient and don't block the main request thread for too long, especially if handling status callbacks. Consider background job queues for logging if performance becomes an issue.
10. Monitoring, Observability, and Analytics
- Logging: We've implemented structured logging using Redwood's
logger
. Ensure logs are aggregated in your production environment (e.g., using services like Datadog, Logtail, Papertrail, or cloud provider logging). Monitor logs for error patterns (level: 'error'
orlevel: 'warn'
). - Plivo Logs: The Plivo Console provides detailed logs for all messages (""Messaging"" -> ""Logs""). Use this to track message statuses (
Queued
,Sent
,Delivered
,Failed
,Undelivered
), troubleshoot delivery issues, and view costs. Filter bymessageUuid
. - Error Tracking: Integrate an error tracking service (e.g., Sentry, Bugsnag) with your RedwoodJS API side. This will capture unhandled exceptions and provide detailed context (stack traces, request data). RedwoodJS has integrations for some providers (
yarn rw setup monitoring ...
). - Health Checks: Implement a basic health check endpoint in your RedwoodJS API (e.g.,
/health
) that verifies database connectivity and potentially checks if essential environment variables (PLIVO_...
) are loaded. - Key Metrics (via Plivo Dashboard/API):
- Delivery Rate (% Delivered)
- Failure Rate (% Failed/Undelivered)
- Latency (Time from Queueing to Sent/Delivered - available in logs)
- Costs
- Application Metrics: Monitor the performance of the
sendPlivoMms
GraphQL mutation (request rate, error rate, duration) using your Application Performance Monitoring (APM) tool or logging platform.
11. Troubleshooting and Caveats
- Error:
Authentication credentials invalid
- Cause: Incorrect
PLIVO_AUTH_ID
orPLIVO_AUTH_TOKEN
in.env
. - Solution: Double-check credentials in the Plivo Console dashboard and update
.env
. Ensure the environment variables are correctly loaded in your deployment environment.
- Cause: Incorrect
- Error:
Invalid 'src' parameter
orThis caller ID is not allowed...
- Cause: The
PLIVO_SENDER_ID
in.env
is not a valid Plivo number associated with your account, is not MMS-enabled, or is potentially of a type not allowed for sending MMS (e.g., a short code not provisioned for MMS). - Solution: Verify the number in the Plivo Console (Phone Numbers -> Your Numbers). Ensure it's E.164 format, has MMS capability enabled, and is associated with your Auth ID.
- Cause: The
- Error:
Invalid 'dst' parameter
orDestination number is not valid for MMS
- Cause: The
to
number is not in valid E.164 format, or you are attempting to send MMS outside the US/Canada. - Solution: Ensure
to
number starts with+
and country code. Verify the recipient is in the US or Canada.
- Cause: The
- Error:
Media URL could not be downloaded
or similar media error- Cause: The
mediaUrl
provided is not publicly accessible, is malformed, points to an unsupported file type, or exceeds size limits. - Solution: Verify the URL is correct and publicly reachable (test in a browser incognito window). Check Plivo's media requirements (size, type). Ensure the hosting server is responding correctly.
- Cause: The
- MMS Not Received (but Plivo logs show
Sent
orDelivered
)- Cause: Carrier filtering, recipient device issues (MMS disabled, poor signal, blocking), incorrect destination number format (even if validated by Plivo initially).
- Solution: Verify the recipient number exactly. Check recipient device settings. Consult carrier-specific MMS troubleshooting guides. Check Plivo logs for detailed error codes if status becomes
Failed
orUndelivered
.
- Trial Account Limitation: Messages fail to send to numbers not verified in the Plivo Console (""Sandbox Numbers""). Solution: Verify the recipient number in the sandbox or upgrade your Plivo account.
- Environment Variables Not Loaded: Service fails with ""credentials missing"" errors, especially after deployment. Solution: Ensure
.env
variables are correctly configured in your hosting provider's environment variable settings (Vercel, Netlify, Render, etc.). Do not commit the.env
file.
12. Deployment and CI/CD
Deploying your RedwoodJS application with Plivo integration follows standard Redwood procedures, with a key focus on environment variables.
- Choose a Hosting Provider: Select a provider compatible with RedwoodJS (e.g., Vercel, Netlify, Render, AWS Serverless).
- Configure Environment Variables:
- Crucial Step: In your hosting provider's dashboard/settings, securely add the same environment variables defined in your local
.env
file:PLIVO_AUTH_ID
PLIVO_AUTH_TOKEN
PLIVO_SENDER_ID
- Do NOT commit your
.env
file to Git. Use the hosting provider's mechanism for managing secrets.
- Crucial Step: In your hosting provider's dashboard/settings, securely add the same environment variables defined in your local
- Build Command: Typically
yarn rw build
. - Deployment: Follow your hosting provider's instructions for deploying a RedwoodJS application. This usually involves connecting your Git repository and configuring build settings.
- CI/CD: Integrate deployment into your CI/CD pipeline (e.g., GitHub Actions, GitLab CI). Ensure the pipeline has secure access to the production environment variables during the build/deploy process if needed, but preferably configure them directly in the hosting environment.
- Testing in Production: After deployment, perform a test send from your live application to ensure the configuration is correct and messages are being sent successfully. Check Plivo logs for confirmation.