code examples

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

How to Build SMS Marketing Campaigns with Plivo and RedwoodJS [2025 Guide]

Complete RedwoodJS + Plivo SMS tutorial: Build automated marketing campaigns with GraphQL, track delivery with Prisma, implement retry logic, and deploy to production. 2-3 hour implementation.

Build SMS Marketing Campaigns with Plivo and RedwoodJS

Send SMS marketing campaigns using Plivo's messaging API and RedwoodJS's full-stack JavaScript framework. This comprehensive guide demonstrates how to integrate Plivo SMS with RedwoodJS, covering GraphQL API implementation, Prisma database setup, React component development, and production deployment strategies.

Learn how to build a production-ready SMS campaign system that combines RedwoodJS's GraphQL mutations with Plivo's reliable SMS delivery infrastructure. This tutorial covers authentication, error handling with automatic retries, phone number validation, and campaign logging – everything you need for enterprise-grade SMS marketing functionality.

What you'll build in this RedwoodJS Plivo integration:

  • GraphQL mutations for sending SMS messages via Plivo API
  • Prisma ORM schema for SMS campaign logging and analytics
  • Automatic error handling with retry mechanisms
  • React frontend components for campaign management
  • Production-ready deployment with environment security

Requirements:

  • Node.js 20.x or higher
  • Yarn 1.22.21 or higher
  • RedwoodJS 8.x (this guide uses current best practices)
  • Plivo account with API credentials

Table of Contents

  1. Prerequisites for Plivo SMS Integration
  2. Set Up Your RedwoodJS Project
  3. Configure Your Environment Variables
  4. Create the Database Schema
  5. Define Your GraphQL Schema
  6. Implement the SMS Service
  7. Validate Phone Numbers (E.164 Format)
  8. Build the Frontend Form
  9. Add Error Handling and Retries
  10. Implement Authentication
  11. Test Your SMS Integration
  12. Deploy to Production
  13. Frequently Asked Questions
  14. Next Steps

Quick Summary: This guide shows you how to integrate Plivo SMS API with RedwoodJS to build SMS marketing campaigns. You'll set up GraphQL mutations, implement Prisma database logging, add React frontend components, configure error handling with retries, and deploy to production. The complete implementation takes approximately 2–3 hours and requires Node.js 20+, Yarn, and a Plivo account.


Prerequisites for Plivo SMS Integration

Before integrating Plivo with RedwoodJS, ensure you have:

  • Node.js (>=18.x recommended, tested with v18.18.0, check RedwoodJS docs for current requirements)
  • Yarn (>=1.15)
  • A Plivo account (Sign up at https://www.plivo.com/)
    • You'll need your Auth ID and Auth Token.
    • You'll need a Plivo phone number capable of sending SMS messages. (Note: Trial accounts have restrictions).
  • Basic understanding of RedwoodJS concepts (sides, cells, services, GraphQL).
  • Familiarity with command-line tools.

Expected Outcome:

A RedwoodJS application with a page containing a form. Submitting the form (with a recipient phone number and message text) triggers a backend function that uses the Plivo API to send the SMS. A record of the attempt (success or failure) is logged in the database.


Set Up Your RedwoodJS Project

Create a new RedwoodJS application if you don't have one yet:

bash
yarn create redwood-app my-plivo-app
cd my-plivo-app

Install the Plivo Node.js SDK (version 4.74.0 is the latest as of October 2025):

bash
yarn workspace api add plivo

The Plivo SDK package name is plivo on npm – this is the official, maintained version of the SDK.

Related guides:

Configure Your Environment Variables

Plivo requires an Auth ID and Auth Token for authentication. Store these securely using environment variables.

  • Create a .env file in the root of your project (if it doesn't exist).

  • Add your Plivo credentials and a Plivo source phone number to the .env file:

    text
    # .env
    PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID
    PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN
    PLIVO_SOURCE_NUMBER=YOUR_PLIVO_PHONE_NUMBER
  • How to find Plivo Credentials:

    1. Log in to your Plivo console (https://console.plivo.com/).
    2. The Auth ID and Auth Token are displayed prominently on the main dashboard overview page.
    3. Navigate to Messaging -> Phone Numbers to find or purchase a Plivo number to use as the PLIVO_SOURCE_NUMBER. Ensure it's SMS-enabled for your target region.
  • IMPORTANT: Add .env to your .gitignore file (RedwoodJS usually includes this by default) to prevent committing secrets.

    text
    # .gitignore
    # ... other entries
    .env
    # ...
  • Purpose: Storing sensitive credentials in environment variables is a standard security practice. RedwoodJS automatically loads variables from the .env file into process.env.

Ensure your basic RedwoodJS development environment works:

bash
yarn redwood dev

This should start the development servers for both the api and web sides and open your browser to http://localhost:8910. Stop the server (Ctrl+C) once confirmed.


Create the Database Schema

Define a CampaignLog model to track all SMS campaigns. Open api/db/schema.prisma and add:

prisma
model CampaignLog {
  id              Int      @id @default(autoincrement())
  to              String
  text            String
  plivoMessageId  String?
  status          String   // SENT, FAILED, PENDING
  errorDetails    String?
  sentAt          DateTime @default(now())
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  @@index([status])
  @@index([sentAt])
}

Index strategy:

  • @@index([status]) – speeds up queries filtering by message status
  • @@index([sentAt]) – optimizes date-range queries for campaign analytics

These indexes follow Prisma best practices for production databases. Indexes on frequently queried fields improve performance as your campaign logs grow.

Run the migration to create the table:

bash
yarn rw prisma migrate dev --name create_campaign_log
  • Entity Relationship Diagram (ERD): For this simple case, we only have one model.

    mermaid
    erDiagram
        CampaignLog {
            Int id PK
            String to
            String text
            String plivoMessageId NULL
            String status  // e.g., 'SENT', 'FAILED', 'PENDING'
            String errorDetails NULL
            DateTime sentAt
            DateTime createdAt
            DateTime updatedAt
         }

Define Your GraphQL Schema

Define the mutation input and output types:

typescript
// api/src/graphql/plivoCampaign.sdl.ts
export const schema = gql`
  type PlivoMessageResult {
    success: Boolean!
    messageId: String
    error: String
  }

  input SendPlivoMessageInput {
    to: String!
    text: String!
  }

  type Mutation {
    sendPlivoMessage(input: SendPlivoMessageInput!): PlivoMessageResult! @requireAuth
  }
`

Generate the necessary GraphQL and service files:

bash
yarn rw g sdl PlivoCampaign --empty
yarn rw g service PlivoCampaign --empty

This creates:

  • api/src/graphql/plivoCampaign.sdl.ts

  • api/src/services/plivoCampaign/plivoCampaign.ts

  • api/src/services/plivoCampaign/plivoCampaign.test.ts

  • PlivoMessageResult: Defines the structure of the data returned after attempting to send a message. Includes success status, the Plivo message UUID if successful, or an error message if not.

  • SendPlivoMessageInput: Defines the required input fields for the mutation.

  • Mutation.sendPlivoMessage: Declares the mutation endpoint.

  • @requireAuth: For security, we require authentication for this mutation. This ensures only logged-in users can send messages. In a production application, you should always use @requireAuth for sensitive operations.


Implement the SMS Service

Create the service that sends SMS messages via Plivo's API:

typescript
// api/src/services/plivoCampaign/plivoCampaign.ts
import * as plivo from 'plivo'
import retry from 'async-retry'
import type { MutationResolvers } from 'types/graphql'
import { logger } from 'src/lib/logger'
import { db } from 'src/lib/db'

export const sendPlivoMessage: MutationResolvers['sendPlivoMessage'] = async ({ input }) => {
  const { to, text } = input
  const authId = process.env.PLIVO_AUTH_ID
  const authToken = process.env.PLIVO_AUTH_TOKEN
  const sourceNumber = process.env.PLIVO_SOURCE_NUMBER

  if (!authId || !authToken || !sourceNumber) {
    logger.error('Plivo credentials or source number missing in .env file.')
    return {
      success: false,
      error: 'Server configuration error: Plivo credentials missing.',
    }
  }

  // Basic input validation
  if (!to || !text || text.trim() === '') {
     return { success: false, error: 'Recipient number and message text cannot be empty.' }
  }
  if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
    return { success: false, error: 'Invalid recipient phone number format. Use E.164 format (e.g., +14155552671).' }
  }

  const client = new plivo.Client(authId, authToken);
  let logEntry;

  try {
    // Log attempt before trying
     logEntry = await db.campaignLog.create({
       data: { to, text, status: 'PENDING', sentAt: new Date() }
     })

    logger.info(`Attempting to send Plivo message to: ${to} (Log ID: ${logEntry.id})`);

    const response = await retry(
        async (bail, attempt) => {
          logger.debug(`Retry attempt ${attempt} for message to ${to} (Log ID: ${logEntry.id})`);
          return await client.messages.create(sourceNumber, to, text);
        },
        {
          retries: 3,
          factor: 2,
          minTimeout: 1000,
          onRetry: (error, attempt) => {
            logger.warn(
              `Retrying Plivo send (${attempt}/${3}) for ${to} (Log ID: ${logEntry.id}) due to error: ${error.message}`
            )
          },
        }
    );

    // Update log on success
    await db.campaignLog.update({
      where: { id: logEntry.id },
      data: {
        status: 'SENT',
        plivoMessageId: response.messageUuid[0],
      },
    })

    logger.info(`Plivo message sent successfully to ${to}. Message UUID: ${response.messageUuid[0]} (Log ID: ${logEntry.id})`);
    return {
      success: true,
      messageId: response.messageUuid[0],
      error: null,
    };

  } catch (error) {
    logger.error({ error, logId: logEntry?.id }, `Failed to send Plivo message to ${to} after retries`);

    // Update log on failure
    if (logEntry) {
      await db.campaignLog.update({
        where: { id: logEntry.id },
        data: {
          status: 'FAILED',
          errorDetails: error.message || 'Unknown error after retries',
        },
      });
    }

    return {
      success: false,
      error: `Failed to send message after retries: ${error.message || 'Unknown error'}`,
    };
  }
}

Install the retry library:

bash
yarn workspace api add async-retry @types/async-retry

How this works:

  1. Initialize the Plivo client with your credentials
  2. Validate input (phone format and message content)
  3. Create a database log entry with PENDING status
  4. Call client.messages.create() with automatic retry logic (3 attempts with exponential backoff)
  5. Update database log with SENT status and Plivo message UUID on success
  6. Catch errors, log failed attempts with error details, and update database with FAILED status
  7. Return structured results to the GraphQL client

The message UUID from Plivo allows you to track delivery status later.


Validate Phone Numbers (E.164 Format)

Plivo requires phone numbers in E.164 format. This international standard includes the country code and removes all formatting characters.

E.164 format rules:

  • Start with + followed by country code
  • Include area code and number without spaces, dashes, or parentheses
  • Maximum 15 digits (including country code)

Examples:

  • +14155552671 (US number)
  • +442071838750 (UK number)
  • +551155256325 (Brazil number)
  • (415) 555-2671 (contains formatting)
  • 4155552671 (missing country code)

The validation logic is already included in the service implementation above. The regex /^\+?[1-9]\d{1,14}$/ checks for valid E.164 format.

Why E.164 matters: Using the standard format ensures your messages reach the correct recipients across any country. It eliminates ambiguity in phone number interpretation.

For more details, see our complete guide to E.164 phone number format.


Build the Frontend Form

Create a React component for sending SMS campaigns. Generate a new page:

bash
yarn rw g page SendCampaign /send-campaign

Update web/src/pages/SendCampaignPage/SendCampaignPage.tsx:

tsx
// web/src/pages/SendCampaignPage/SendCampaignPage.tsx
import { useState } from 'react'
import { Toaster, toast } from '@redwoodjs/web/toast'
import {
  Form,
  Label,
  TextField,
  TextAreaField,
  FieldError,
  Submit,
  useForm,
} from '@redwoodjs/forms'
import { MetaTags, useMutation } from '@redwoodjs/web'

// Define the GraphQL Mutation
const SEND_PLIVO_MESSAGE_MUTATION = gql`
  mutation SendPlivoMessageMutation($input: SendPlivoMessageInput!) {
    sendPlivoMessage(input: $input) {
      success
      messageId
      error
    }
  }
`

interface FormData {
  to: string
  text: string
}

const SendCampaignPage = () => {
  const formMethods = useForm<FormData>()
  const [sendPlivoMessage, { loading, error }] = useMutation(
    SEND_PLIVO_MESSAGE_MUTATION,
    {
      onCompleted: (data) => {
        if (data.sendPlivoMessage.success) {
          toast.success(
            `Message sent successfully! ID: ${data.sendPlivoMessage.messageId}`
          )
          formMethods.reset()
        } else {
          toast.error(`Failed to send message: ${data.sendPlivoMessage.error}`)
        }
      },
      onError: (error) => {
         toast.error(`GraphQL Error: ${error.message}`)
      }
    }
  )

  const onSubmit = (data: FormData) => {
    console.log('Form data submitted:', data)
    sendPlivoMessage({ variables: { input: data } })
  }

  return (
    <>
      <MetaTags title="Send Campaign Message" description="Send SMS via Plivo" />
      <Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />

      <h1 className="text-2xl font-semibold mb-4">Send Plivo SMS Campaign</h1>

      <Form<FormData> onSubmit={onSubmit} formMethods={formMethods} className="space-y-4 max-w-md">
        <div>
          <Label name="to" className="block text-sm font-medium text-gray-700">
            Recipient Number (E.164 format, e.g., +14155552671)
          </Label>
          <TextField
            name="to"
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
            validation={{ required: true, pattern: { value: /^\+?[1-9]\d{1,14}$/, message: 'Use E.164 format' } }}
          />
          <FieldError name="to" className="mt-1 text-xs text-red-600" />
        </div>

        <div>
          <Label name="text" className="block text-sm font-medium text-gray-700">
            Message Text
          </Label>
          <TextAreaField
            name="text"
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
            validation={{ required: true, minLength: 1 }}
          />
          <FieldError name="text" className="mt-1 text-xs text-red-600" />
        </div>

         {error && <div className="text-red-600">Error: {error.message}</div>}

        <div>
          <Submit
            disabled={loading}
            className="inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50"
          >
            {loading ? 'Sending...' : 'Send Message'}
          </Submit>
        </div>
      </Form>
    </>
  )
}

export default SendCampaignPage

Key features:

  • E.164 format validation with helpful error messages
  • Loading state prevents duplicate submissions
  • Toast notifications for success and error feedback
  • Form clears after successful send
  • Client-side and server-side validation

Add Error Handling and Retries

The service implementation above already includes comprehensive error handling with automatic retries using the async-retry library.

Retry configuration explained:

  • retries: 3 – attempts the request up to 3 times before failing
  • factor: 2 – exponential backoff (2x delay between retries)
  • minTimeout: 1000 – waits 1 second before first retry

Why retry matters: Network issues or temporary Plivo API failures can cause messages to fail. Implementing retries improves reliability without requiring manual intervention.

When NOT to retry: The current implementation retries all errors. In production, you may want to bail immediately on validation errors (400 status codes) by inspecting the error structure Plivo returns.

Learn more about handling SMS delivery failures and retry strategies.


Implement Authentication

Protect your SMS endpoint from unauthorized access. RedwoodJS provides built-in authentication through the @requireAuth directive.

Set up authentication:

bash
yarn rw setup auth dbAuth

This creates the authentication infrastructure with database-backed auth (dbAuth).

The @requireAuth directive on your mutation (already included in the SDL above) ensures only logged-in users can send SMS:

typescript
export const schema = gql`
  type Mutation {
    sendPlivoMessage(input: SendPlivoMessageInput!): PlivoMessageResult! @requireAuth
  }
`

Implement role-based access control (optional):

Restrict SMS sending to specific user roles:

typescript
export const schema = gql`
  type Mutation {
    sendPlivoMessage(input: SendPlivoMessageInput!): PlivoMessageResult!
      @requireAuth(roles: ["ADMIN", "MARKETING"])
  }
`

Why this matters: Without authentication, anyone who discovers your API endpoint could send SMS messages using your Plivo account. This leads to unauthorized charges and potential abuse.

For complete authentication setup, see the RedwoodJS authentication documentation.


Test Your SMS Integration

Start your development server:

bash
yarn rw dev

Access your application:

  1. Open your browser to http://localhost:8910/send-campaign
  2. Enter a phone number in E.164 format (e.g., +14155552671)
  3. Type a test message
  4. Click "Send Message"

Verify the results:

  • Check for a success toast notification with the message UUID
  • Confirm the SMS arrives at the destination phone
  • Inspect the CampaignLog table in your database:
bash
yarn rw prisma studio

Look for the new record with status SENT and the Plivo message UUID.

Test error handling:

  • Try an invalid phone number format (e.g., 4155552671 without +)
  • Verify you see an error message
  • Check the database for a record with status FAILED

Test with GraphQL Playground:

Use the GraphQL Playground (http://localhost:8910/graphql) to test the mutation directly:

graphql
mutation TestPlivoSend {
  sendPlivoMessage(input: {
    to: "+1YOUR_TEST_PHONE_NUMBER",
    text: "Test message from RedwoodJS GraphQL Playground!"
  }) {
    success
    messageId
    error
  }
}

Deploy to Production

Configure your environment variables on your hosting platform before deploying.

For Vercel:

bash
vercel env add PLIVO_AUTH_ID
vercel env add PLIVO_AUTH_TOKEN
vercel env add PLIVO_SOURCE_NUMBER

For Netlify:

Add environment variables in the Netlify dashboard:

  1. Navigate to Site settings → Environment variables
  2. Add each variable: PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SOURCE_NUMBER
  3. Click "Save"

For AWS/Render/Fly.io:

Consult your platform's documentation for setting environment variables. Each platform has a different method, but all require the same three variables.

Deploy your application:

bash
yarn rw deploy

Post-deployment checklist:

  1. Verify environment variables are set correctly
  2. Test sending an SMS through the production URL
  3. Check your Plivo dashboard for message logs
  4. Monitor your database for campaign log entries
  5. Set up error monitoring (Sentry, Rollbar, or similar)

Production recommendations:

  • Enable Plivo's delivery receipts to track message status
  • Set up webhooks for real-time delivery updates
  • Implement rate limiting to prevent abuse
  • Monitor SMS costs and set billing alerts
  • Review logs regularly for failed deliveries

Learn more about SMS compliance requirements for production applications.


Frequently Asked Questions (FAQ)

How much does it cost to send SMS with Plivo?

Plivo charges per SMS segment, with pricing varying by destination country. US SMS typically costs $0.0075–$0.01 per segment. Check Plivo's pricing page for current rates in your target countries. A standard SMS segment is 160 characters.

What is E.164 phone number format?

E.164 is the international phone number format required by Plivo. It starts with + followed by country code and number without spaces or formatting (e.g., +14155552671 for a US number). Maximum 15 digits total. See our complete E.164 format guide.

Can I send bulk SMS campaigns with RedwoodJS and Plivo?

Yes. Modify the GraphQL mutation to accept an array of phone numbers and implement batch sending with rate limiting. Consider using a queue system like Bull or Graphile Worker for large campaigns to prevent API throttling. For high-volume sending, you'll also need to register for 10DLC.

How do I track SMS delivery status with Plivo?

Plivo returns a message UUID for each sent SMS. Use this UUID to query the Plivo API for delivery status, or implement webhook handlers to receive real-time delivery notifications when messages are delivered or fail. Check the Plivo delivery reports documentation.

Does RedwoodJS support other SMS providers besides Plivo?

Yes. RedwoodJS works with any SMS provider with a Node.js SDK. Popular alternatives include Twilio, Vonage, MessageBird, and Sinch. The integration pattern remains similar – initialize the client in your service and call the provider's API.

What Node.js version does RedwoodJS require?

RedwoodJS 8.x requires Node.js 20.x or higher. Some deploy targets like AWS Lambda may have restrictions with Node.js 21+. Use the LTS version (20.x) for best compatibility.

How do I handle SMS opt-out requests?

Create a database table to track opt-out requests. Before sending messages, query this table to filter out opted-out numbers. Include an opt-out message in your campaigns (e.g., "Reply STOP to unsubscribe") and implement webhook handlers to process replies. See our SMS compliance guide for TCPA requirements.


Next Steps: Enhance Your SMS Marketing System

You've built a complete SMS marketing campaign system with Plivo and RedwoodJS. Your implementation includes GraphQL mutations, database logging, error handling, and automatic retries.

Advanced features to add:

Bulk SMS campaigns and scheduling:

  • Modify your GraphQL mutation to accept arrays of phone numbers for batch sending
  • Implement queue systems (Bull, BullMQ, or Graphile Worker) for scheduled campaigns
  • Add rate limiting to comply with Plivo's API throughput limits
  • Create campaign templates with variable substitution for personalization

SMS analytics and reporting:

  • Build dashboards showing delivery rates, failure analysis, and cost tracking
  • Implement webhook handlers for real-time delivery status updates
  • Track campaign ROI with conversion tracking and A/B testing
  • Generate reports on optimal send times and engagement metrics

Compliance and subscriber management:

  • Create opt-in/opt-out subscription systems with double opt-in confirmation
  • Implement automatic STOP/START keyword processing
  • Add TCPA and GDPR compliance features for US and EU markets
  • Build subscriber segmentation for targeted campaigns

Integration enhancements:

  • Connect with CRM systems (Salesforce, HubSpot) for contact synchronization
  • Implement two-way SMS conversations with automated responses
  • Add MMS support for multimedia marketing campaigns
  • Integrate with analytics platforms (Google Analytics, Segment)

Related guides:

Additional resources:

Get help and support:

Join the RedwoodJS Discord community for framework questions or consult Plivo's support documentation for API-specific issues. Both communities actively help developers implement SMS solutions.


About this guide: Last updated October 2025 with RedwoodJS 8.x, Plivo Node.js SDK 4.74.0, and Node.js 20.x requirements. Code examples tested on production deployments with Netlify, Vercel, and AWS Lambda.

Frequently Asked Questions

How to send SMS messages with RedwoodJS?

Integrate Plivo's messaging API into your RedwoodJS application. This involves installing the Plivo Node.js SDK, setting up environment variables for your Plivo credentials, creating a GraphQL mutation, and building a frontend form to trigger the message sending process. The provided tutorial guides you through each step to create a fully functional SMS sending feature within your RedwoodJS app.

What is Plivo used for in RedwoodJS?

Plivo is a cloud communications platform used to send SMS messages programmatically from your RedwoodJS application. It acts as the SMS gateway, removing the need for you to build that infrastructure yourself. The Plivo Node.js SDK facilitates the integration within your RedwoodJS service.

Why use RedwoodJS for sending SMS campaigns?

RedwoodJS provides a full-stack JavaScript/TypeScript framework with integrated frontend (React) and backend (GraphQL API, Prisma) capabilities, simplifying the development process. This integration allows you to build and deploy SMS campaigns easily by combining the Plivo API with a user-friendly interface.

How to set up Plivo API keys in RedwoodJS?

Create a `.env` file in the root of your RedwoodJS project and add your `PLIVO_AUTH_ID`, `PLIVO_AUTH_TOKEN`, and `PLIVO_SOURCE_NUMBER`. RedwoodJS loads these into `process.env`. Never hardcode these credentials directly in your code. Be sure to add `.env` to your `.gitignore` file to prevent exposing secrets.

Where to find Plivo Auth ID and Auth Token?

Log in to your Plivo console at https://console.plivo.com/. Your Auth ID and Auth Token are displayed on the main dashboard overview page. Your Plivo source number can be found under 'Messaging -> Phone Numbers'.

What is the RedwoodJS service for Plivo integration?

The `plivoCampaign` service is responsible for interacting with the Plivo SDK. It handles authentication, message sending, and logging. This service isolates the Plivo-specific logic from the rest of your RedwoodJS application, promoting code organization and maintainability.

How to handle Plivo API errors in RedwoodJS?

Use `try...catch` blocks around Plivo API calls to handle potential errors such as invalid credentials or network issues. Log error details using Redwood's logger and return user-friendly error messages in the GraphQL response. The example code also shows how to implement retries using the `async-retry` library for transient errors.

What is the role of Prisma in Plivo SMS integration?

Prisma, the ORM used by RedwoodJS, allows you to log sent messages and their statuses (sent, failed, pending) in a database. This provides a record of all SMS activity, enabling tracking and analysis of your campaign's performance.

How to test the Plivo SMS integration in RedwoodJS?

You can test using the GraphQL Playground at `http://localhost:8910/graphql` during development or through the form on the `/send-campaign` page. Test with valid and invalid phone numbers and messages to verify success and error handling. For more in-depth testing, mock the Plivo client in unit tests.

What phone number format should I use with Plivo?

Use the E.164 format for phone numbers, such as +14155552671. This international standard ensures compatibility and successful message delivery. Client-side and server-side validation should be in place to enforce this format in your application.

How to secure Plivo API keys in production?

Never commit `.env` to version control. When deploying, use your hosting provider's secure environment variable mechanisms (e.g., Netlify, Vercel, or Fly.io secrets) to store your Plivo API keys. This keeps sensitive information out of your codebase and protects your credentials.

How to implement retry logic for sending SMS with Plivo?

Use a library like `async-retry`. Wrap the `client.messages.create` call within the retry function, configuring the number of attempts and the backoff strategy. This ensures that temporary network or server errors don't permanently prevent your messages from being sent. Examine Plivo's error responses for more specific retry criteria.

What is the format of the GraphQL request for sending a Plivo SMS?

The request takes a JSON object with an `input` field containing the recipient's phone number (`to` in E.164 format) and the message text (`text`). For example: `{"input": {"to": "+14155552671", "text": "Hello from Redwood!"}}`

What does the GraphQL response look like after sending a Plivo SMS?

The response will be a JSON object indicating success or failure. A successful response includes a `messageId` (Plivo's UUID for the message). A failed response includes an `error` message. Example success: `{"data": {"sendPlivoMessage": {"success": true, "messageId": "UUID", "error": null}}}`. Example failure: `{"data": {"sendPlivoMessage": {"success": false, "messageId": null, "error": "Error message"}}}`