Frequently Asked Questions
You can send SMS messages by creating a RedwoodJS Service and a GraphQL mutation that uses the Plivo Node.js SDK. The service function will interact with the Plivo API to send messages and store message details in a database. The GraphQL mutation will provide an interface for your web application to trigger sending messages.
The Plivo callback mechanism allows your RedwoodJS application to receive real-time delivery status updates for sent SMS messages. Plivo sends these updates as HTTP POST requests (webhooks) to a designated endpoint in your RedwoodJS application. This enables you to track message status (e.g., queued, sent, delivered, failed) and update your application accordingly.
Plivo requires signature validation to ensure the security and authenticity of the callback requests. This verification step confirms that the webhook originated from Plivo and prevents malicious actors from spoofing status updates. The X-Plivo-Signature-V3
header is used for this validation process.
First, install the Plivo Node.js library. Then, configure environment variables for your Plivo Auth ID, Auth Token, source phone number, and callback URL. Set up a Prisma schema to store message data. Create a RedwoodJS Service to handle sending messages and a RedwoodJS Function to handle incoming Plivo webhooks.
Use ngrok http 8911
to create a public URL pointing to your local Redwood API server. Update the PLIVO_CALLBACK_BASE_URL
environment variable with your ngrok HTTPS URL and restart your RedwoodJS server. Plivo can then send webhooks to your local machine during development.
Prisma is used as the Object-Relational Mapper (ORM) to interact with your database. It allows you to define your database schema (including a Message model) and easily perform database operations (create, update, query) within your RedwoodJS services and functions to store and retrieve message information.
Update PLIVO_CALLBACK_BASE_URL
every time you start ngrok for local development. When deploying to production, update it to your deployed API's public HTTPS URL. This ensures that Plivo can always reach your webhook endpoint.
While you can set up the system and test sending messages, Plivo callbacks require sending to valid phone numbers. Use a real phone number you have access to during testing to observe actual delivery statuses from Plivo's network.
The callback URL should consist of your PLIVO_CALLBACK_BASE_URL
followed by the path to your RedwoodJS function, typically /plivoCallback
. For example: https://your-api-url.com/plivoCallback
. This assumes you named your Redwood function plivoCallback
.
Signature validation is crucial for security. It verifies that incoming webhook requests genuinely originate from Plivo and prevents unauthorized or malicious actors from tampering with your message status data. Without this check, your application could be vulnerable to attacks.
You track message status by implementing a RedwoodJS function that handles the incoming Plivo webhook. This function parses the webhook payload, validates the signature, and updates the corresponding message's status in the database based on the data received from Plivo.
Common errors include invalid signatures (403 Forbidden) caused by incorrect configuration of the PLIVO_AUTH_TOKEN
or requestUrl
, and message not found issues, often due to timing discrepancies between the webhook arrival and the message creation in the database.
The callback function should check the "Content-Type" header. Parse the payload as JSON if "Content-Type" is "application/json"; parse using "URLSearchParams" if "Content-Type" is "application/x-www-form-urlencoded". Include robust logging for debugging unexpected scenarios.
Initialize the Plivo client in your service file using new Plivo.Client(process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN)
. Ensure these environment variables are correctly set in your .env
file and loaded into the RedwoodJS API side.
This guide provides a step-by-step walkthrough for integrating Plivo SMS services into your RedwoodJS application, focusing specifically on sending messages and reliably tracking their delivery status using Plivo's callback mechanism. We'll build a system that sends an SMS, stores its initial details, and updates its status in real-time as Plivo provides delivery reports via webhooks.
By the end of this tutorial, you will have:
Technologies Used:
System Architecture:
Prerequisites:
ngrok
installed for local development testing.1. Setting up the RedwoodJS Project
First, create a new RedwoodJS project if you don't have one already. We'll use TypeScript for this guide.
Environment Variables:
Plivo requires authentication credentials and a source phone number. We also need a base URL for our callback endpoint, especially during development with ngrok.
Create a
.env
file in the root of your project and add the following variables. You can find your Plivo Auth ID and Auth Token in the Plivo Console dashboard under "API" -> "Keys & Credentials". Your Plivo phone number is listed under "Phone Numbers".PLIVO_AUTH_ID
/PLIVO_AUTH_TOKEN
: Your API credentials for authenticating requests to Plivo. Find these on the Plivo Console.PLIVO_SOURCE_NUMBER
: The Plivo phone number you will send messages from.PLIVO_CALLBACK_BASE_URL
: The public base URL where your API is accessible. Plivo will POST status updates to a path under this URL (e.g.,${PLIVO_CALLBACK_BASE_URL}/plivoCallback
). This must be updated to usehttps://
for ngrok or production.RedwoodJS Configuration:
Ensure your
redwood.toml
includes the API environment variables:2. Creating the Database Schema and Data Layer
We need a database table to store information about the messages we send, including their Plivo ID and delivery status.
Define the Prisma Schema:
Open
api/db/schema.prisma
and add aMessage
model:plivoMessageId
: Stores the unique identifier (message_uuid
) returned by Plivo upon successful submission. This is crucial for correlating callbacks.status
: Tracks the delivery status reported by Plivo.plivoErrorCode
: Stores the Plivo error code if the message fails.Apply Migrations:
Generate and apply the database migration:
This command creates a migration file in
api/db/migrations
and updates your database schema.3. Implementing Core Functionality: Sending SMS
We'll create a RedwoodJS Service and a GraphQL mutation to handle sending SMS messages via Plivo.
Create the Messages Service:
Generate the service and SDL files for messages:
Implement the
sendMessage
Service Function:Edit
api/src/services/messages/messages.ts
to include the logic for sending SMS via Plivo and saving the initial record to the database.sendMessage
function takesto
andbody
as input.callbackUrl
usingPLIVO_CALLBACK_BASE_URL
and append/plivoCallback
(this must match the filename of our webhook function later). This URL is passed in theurl
parameter of theplivoClient.messages.create
call.message_uuid
.Message
table with theplivoMessageId
and an initialstatus
of'queued'
.Define the GraphQL Schema (SDL):
Update
api/src/graphql/messages.sdl.ts
to define the mutation and types.This defines the
Message
type mirroring our Prisma model, a query to fetch messages, and thesendMessage
mutation which accepts theSendMessageInput
and returns the createdMessage
. We've added@requireAuth
as a placeholder; adjust authentication as needed for your application.4. Building the Plivo Callback API Endpoint
Plivo will send HTTP POST requests to the
url
we provided when sending the message. We need a RedwoodJS Function to receive and process these requests.Create the Plivo Callback Function:
This creates
api/src/functions/plivoCallback.ts
.Implement the Callback Handler:
Edit
api/src/functions/plivoCallback.ts
to handle incoming Plivo webhooks, validate signatures, and update the database.X-Plivo-Signature-V3
andX-Plivo-Signature-V3-Nonce
headers.requestUrl
that Plivo used when sending the webhook.Plivo.validateV3Signature
with the request method (POST
), URL, nonce, your Auth Token, the received signature, and the raw request body string.403 Forbidden
response.Content-Type
header to decide between parsing JSON orx-www-form-urlencoded
data (usingURLSearchParams
). It also handles cases where the body might already be parsed by the environment.MessageUUID
,MessageStatus
, andErrorCode
from the payload.db.message.update
with awhere
clause targeting theplivoMessageId
(which corresponds to Plivo'sMessageUUID
).status
andplivoErrorCode
.200 OK
to Plivo to acknowledge successful receipt and processing. If signature validation fails, return403
. If payload processing fails, return500
to potentially trigger Plivo retries (check Plivo's retry policy). Return200
even if the message isn't found immediately to avoid unnecessary retries for timing issues.5. Local Development and Testing with ngrok
To receive Plivo webhooks on your local machine, you need to expose your local Redwood API server to the public internet.
ngrok
is perfect for this.Steps:
Start your Redwood development server:
Your API server will typically run on
http://localhost:8911
.Start ngrok: Open a new terminal window and run ngrok, telling it to forward traffic to your Redwood API port (8911):
Get the ngrok URL: ngrok will display output similar to this:
Copy the
https
forwarding URL (e.g.,https://<UNIQUE_ID>.ngrok-free.app
). Using HTTPS is strongly recommended.Update
.env
: Modify thePLIVO_CALLBACK_BASE_URL
in your.env
file to use the ngrok HTTPS URL:Restart Redwood: Stop (
Ctrl+C
) and restart your Redwood dev server (yarn rw dev
) to pick up the changed environment variable.Test Sending: Use Redwood's GraphQL Playground (usually
http://localhost:8911/graphql
) or a tool like Postman/curl to send thesendMessage
mutation:Method:
POST
URL:http://localhost:8911/graphql
Headers:Content-Type: application/json
,auth-provider: dbAuth
(or your auth header), etc. Body:Observe Callbacks:
yarn rw dev
. You should see logs from thesendMessage
service.plivoCallback
function indicating receipt of the webhook and signature validation status.http://127.0.0.1:4040
) to inspect the incoming requests from Plivo.yarn rw prisma studio
) to see theMessage
record being created and then itsstatus
field updating (e.g., from 'queued' to 'sent', then 'delivered' or 'failed').6. Error Handling and Logging
sendMessage
service includes basictry...catch
blocks. Log detailed errors usinglogger.error({ error, ...context })
. Consider mapping specific Plivo API errors to user-friendly messages if necessary.plivoCallback
function logs errors during signature validation and payload processing. Returning appropriate HTTP status codes (403
,500
,200
) helps Plivo manage retries.db.message.create
,db.message.update
) intry...catch
within both the service and the callback function to handle potential database connectivity or constraint issues. Log these errors clearly.info
,warn
,error
) appropriately.info
for standard operations,warn
for unexpected but recoverable situations (like missing headers or message not found),error
for failures.7. Security Considerations
X-Plivo-Signature-V3
header. This prevents attackers from sending fake or malicious status updates to your endpoint, ensuring the data genuinely originates from Plivo. EnsurePLIVO_AUTH_TOKEN
is kept secret and is not exposed client-side.sendMessage
mutation (validate
function) to prevent invalid data (e.g., malformed phone numbers, overly long messages) from reaching the service or Plivo.sendMessage
mutation endpoint if abuse is a concern. Plivo also has its own API rate limits.PLIVO_AUTH_TOKEN
directly into your code repository. Use environment variables managed securely by your deployment platform.PLIVO_CALLBACK_BASE_URL
), both locally with ngrok and especially in production. This encrypts the data in transit, protecting sensitive information within the callback payload.8. Troubleshooting and Caveats
PLIVO_CALLBACK_BASE_URL
in your.env
is correct (usinghttps://
for ngrok/prod) and publicly accessible (check ngrok status or production deployment URL).sendMessage
function'surl
parameter exactly matches the expected endpoint (<BASE_URL>/plivoCallback
). Check thesendMessage
logs.PLIVO_AUTH_TOKEN
in your.env
exactly matches the one in your Plivo console.requestUrl
constructed inplivoCallback.ts
exactly matches the URL Plivo is calling (includinghttps
, the domain, and the path/plivoCallback
). Check ngrok/server logs for the exact incoming request details.Plivo.validateV3Signature
. Middleware or framework auto-parsing might alter it before validation occurs. The current implementation passesevent.body
which should be the raw string in standard Lambda proxy integrations.event.httpMethod
is correctly passed (should bePOST
).sendMessage
service finishes writing the initial record. Returning200 OK
allows Plivo to stop retrying while your system catches up. Add robust logging to monitor this.MessageUUID
from the Plivo callback payload matches theplivoMessageId
stored in the database. Check for typos or case sensitivity issues.queued
,sent
,delivered
,undelivered
,failed
) and potential error codes to handle them appropriately in your application logic or UI. For example, anErrorCode
might indicate an invalid destination number or carrier issue. See Plivo Docs for Message States and Error Codes.Content-Type
header (application/json
vsapplication/x-www-form-urlencoded
) sent by Plivo and ensure your parsing logic in the callback handler matches. Check Plivo documentation or inspect incoming requests if you encounter unexpected formats.9. Deployment
DATABASE_URL
,PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
, andPLIVO_SOURCE_NUMBER
in your hosting provider's environment variable settings. Never hardcode these.PLIVO_CALLBACK_BASE_URL
to your deployed API's public HTTPS URL. For example, if your API is deployed athttps://api.myapp.com
, setPLIVO_CALLBACK_BASE_URL="https://api.myapp.com"
. The callback function will then be accessible athttps://api.myapp.com/plivoCallback
.plivoCallback
function) is deployed correctly and accessible.10. Verification and Testing
sendMessage
service, mocking the Plivo client (plivo.messages.create
) anddb
calls (db.message.create
). Verify input validation and correct data saving.plivoCallback
function. Mockdb
calls (db.message.update
). Create mockAPIGatewayEvent
objects with valid and invalid signatures (usingjest.spyOn
to mockPlivo.validateV3Signature
), different payloads (delivered, failed, missing fields), and different Content-Types (application/json
,application/x-www-form-urlencoded
) to test validation and processing logic thoroughly.plivoMessageId
?plivoCallback
function (check logs/ngrok/production function logs)?plivoErrorCode
stored correctly for failed/undelivered messages?This guide provides a robust foundation for integrating Plivo SMS sending and delivery status tracking into your RedwoodJS application. Remember to adapt the error handling, logging, and security measures to the specific needs and scale of your project. Happy coding!