Frequently Asked Questions
Start by creating a new RedwoodJS project, installing the Plivo Node.js SDK, and setting up environment variables for your Plivo Auth ID, Auth Token, and Plivo phone number. You'll then create a RedwoodJS API function to handle incoming webhooks from Plivo.
Plivo is a cloud communications platform that provides the SMS API for sending and receiving messages. It handles the actual SMS delivery and interacts with your RedwoodJS application via webhooks.
RedwoodJS offers a full-stack, serverless framework with built-in tools for APIs, databases, and web frontends. This simplifies development and deployment of complex SMS applications.
Signature validation is crucial for security and should be performed at the very beginning of your webhook handler function, before processing any data from the request. This prevents unauthorized access to your application.
Yes, you can use ngrok to create a public URL that tunnels requests to your local development server. Configure your Plivo application to send webhooks to this ngrok URL, allowing you to test the integration locally before deploying.
Implement robust error handling using try-catch blocks around body parsing, database operations, and Plivo SDK calls. Log errors using Redwood's logger and return a 200 OK response to Plivo, even if an error occurs, to prevent retries. For signature validation failures, return a 403 Forbidden response.
The MessageUUID is a unique identifier assigned by Plivo to each incoming SMS message. Use this UUID to check for duplicate webhook deliveries and ensure idempotent processing, preventing duplicate database entries or other side effects.
Use the Plivo Node.js SDK to construct an XML response containing a `` element with the `src` (your Plivo number) and `dst` (recipient's number). The text of the reply is set within the `` element. Return this XML in the 200 OK response to the Plivo webhook.
While Prisma supports various databases, PostgreSQL is generally recommended for production use due to its reliability and features. SQLite can be used for development or simpler projects.
Define a Prisma schema in `api/db/schema.prisma` with fields like `fromNumber`, `toNumber`, `text`, `plivoMessageUuid`, `status`, and a `Direction` enum. Then run `yarn rw prisma migrate dev` to apply the schema to your database and generate the Prisma Client.
The webhook receives an HTTP POST request with form-urlencoded data containing parameters like `From`, `To`, `Text`, `MessageUUID`, `Type`, and `Event` from Plivo. The request also includes signature headers if enabled in your Plivo application.
Plivo uses XML (Plivo XML or PHLO) to control communication flows. The XML response you return from your webhook instructs Plivo on the next action, such as sending an SMS reply, playing audio, or gathering user input.
Plivo sends webhook data as application/x-www-form-urlencoded. Use `URLSearchParams` or similar methods in your RedwoodJS function to parse the request body string into a JavaScript object. Be sure to handle potential errors during this process and check for all essential parameters.
This guide provides a complete walkthrough for building a RedwoodJS application capable of receiving incoming SMS messages via Plivo and responding automatically, enabling two-way SMS communication. We'll cover everything from initial project setup and Plivo configuration to database integration, security best practices, deployment, and troubleshooting.
By the end of this tutorial, you will have a RedwoodJS application with an API endpoint that acts as a webhook for Plivo. This endpoint will receive SMS messages sent to your Plivo number, log them to a database, and send an automated reply back to the sender. This forms the foundation for building more complex SMS-based features like customer support bots, notification systems, or interactive services.
Project Overview and Goals
Goal: To create a RedwoodJS application that can:
Problem Solved: Enables businesses to engage with users via SMS, providing automated responses or triggering backend processes based on received messages. It establishes a robust foundation for two-way SMS communication within a modern full-stack JavaScript application.
Technologies Used:
System Architecture:
message_url
you configure.From
,To
,Text
).Prerequisites:
ngrok
installed for local development testing (Download ngrok).1. Setting Up the Project
Let's start by creating a new RedwoodJS project and installing the necessary dependencies.
Create RedwoodJS App: Open your terminal and run:
Choose your preferred database during setup (PostgreSQL is recommended for production). For this guide, we'll assume PostgreSQL.
Install Plivo SDK: The Plivo SDK is needed on the API side to interact with Plivo APIs and generate response XML.
Environment Variables: Plivo requires an Auth ID and Auth Token for authentication. Never hardcode these in your source code. We'll use environment variables.
.env
file in the root of your RedwoodJS project (if it doesn't exist)..env
file using the standardKEY=VALUE
format:PLIVO_NUMBER
is stored here for easy reference when constructing replies or sending outbound messages.Git Initialization: It's good practice to initialize Git early.
Make sure
.env
is included in your.gitignore
file (RedwoodJS adds it by default).2. Implementing Core Functionality (Webhook Handler)
We need an API endpoint (a RedwoodJS function) that Plivo can call when an SMS is received. This function will process the incoming message and generate the reply.
Generate API Function: Use the RedwoodJS CLI to generate a new serverless function:
This creates
api/src/functions/plivoSmsWebhook.js
.Implement Webhook Logic: Open
api/src/functions/plivoSmsWebhook.js
and replace its contents with the following code:db
).handler
function receives the HTTP request (event
).event.body
, expecting form-urlencoded data from Plivo (From
,To
,Text
,MessageUUID
).URLSearchParams
is used for parsing.plivo.Response()
.response.addMessage(replyText, params)
adds a<Message>
element to the XML response.src
is your Plivo number (toNumber
from the incoming request), anddst
is the original sender's number (fromNumber
).response.toXML()
generates the final XML string.Content-Type
set toapplication/xml
and the generated XML as the body. This tells Plivo to send the reply SMS.3. Building a Complete API Layer
For this specific use case (receiving and replying to SMS via webhook), the API function
plivoSmsWebhook.js
is the primary API layer interacting directly with Plivo.Authentication/Authorization: The security of this endpoint relies on validating the webhook signature from Plivo (covered in Section 7), not typical user authentication. Anyone knowing the URL could potentially call it, hence the signature validation is critical.
Request Validation: Basic validation (checking for
From
,To
,Text
) is included. More complex business logic validation (e.g., checking user status based onfromNumber
) would be added here or in a dedicated service.API Endpoint Documentation:
/api/plivoSmsWebhook
(relative to your deployed base URL)POST
(Typically, but Plivo allowsGET
too – configure in Plivo App)application/x-www-form-urlencoded
From
: Sender's phone number (E.164 format)To
: Your Plivo phone number (E.164 format)Text
: The content of the SMS message (UTF-8 encoded)Type
:sms
MessageUUID
: Plivo's unique identifier for the incoming messageEvent
:message
(for standard incoming messages)200 OK
application/xml
200 OK
with an empty<Response/>
or log the error internally and potentially send an error SMS if appropriate. If signature validation fails, return403 Forbidden
.Testing with cURL/Postman: You can simulate Plivo's request locally (once
ngrok
is running, see Section 4). Note: You will need to replace the placeholder values (YOUR_NGROK_URL
,+1SENDERNUMBER
,+1YOURPLIVONUMBER
) with your actual ngrok URL and phone numbers.You should receive the XML response back in the terminal.
4. Integrating with Plivo (Configuration)
Now, let's configure Plivo to send webhooks to our local development server and then to our deployed application.
Start Local Development Server:
Your RedwoodJS app (including the API function) is now running, typically accessible via
http://localhost:8910
for the web side andhttp://localhost:8911
for the API/functions (check your terminal output). Our webhook is athttp://localhost:8911/plivoSmsWebhook
.Expose Local Server with ngrok: Plivo needs a publicly accessible URL to send webhooks. Open a new terminal window and run:
8911
is the default for RedwoodJS API functions. Verify this in yourredwood.toml
or dev server output if needed.ngrok
will display a Forwarding URL (e.g.,https://abcdef123456.ngrok.io
). This URL tunnels requests to yourlocalhost:8911
. Copy thehttps
version of this URL.Configure Plivo Application:
Redwood Dev SMS Handler
).ngrok
forwarding URL, appending the function path:https://abcdef123456.ngrok.io/api/plivoSmsWebhook
POST
.Assign Application to Plivo Number:
XML Application
.Redwood Dev SMS Handler
).Test Locally:
yarn rw dev
. You should see logs:INFO: Received Plivo SMS webhook request
INFO: Message received - From: +YOURMOBILE, To: +PLIVONUMBER, Text: YOURMESSAGE
INFO: Sending XML Response to Plivo: <Response>...
Thanks for your message! You said: ""YOURMESSAGE""
ngrok
. You should seePOST /api/plivoSmsWebhook 200 OK
requests logged.5. Implementing Error Handling and Logging
Robust error handling and logging are essential for production systems. The following sections (6 and 7) will add database interaction and security validation, which should also be wrapped in error handling as shown here.
RedwoodJS Logger: We are already using
logger
fromsrc/lib/logger
. This provides basic logging capabilities. You can configure log levels (e.g.,trace
,debug
,info
,warn
,error
,fatal
) inapi/src/lib/logger.js
. For production, you'll want to integrate with a dedicated logging service (like Logflare, Datadog, etc.).Webhook Error Handling Strategy:
400 Bad Request
.From
,To
,Text
). Log a warning and return200 OK
with an empty<Response/>
to prevent Plivo retries for malformed (but technically valid) requests.db.smsMessage.create
) in atry...catch
block. Log the error. Decide on the behavior (reply anyway, fail silently, reply with error).response.addMessage()
andresponse.toXML()
intry...catch
. Log the error and return200 OK
with empty<Response/>
.403 Forbidden
.Refined Webhook Code with Error Handling Structure: This version incorporates the error handling structure. The specific database and security logic will be added in Sections 6 and 7 where the
TODO
comments indicate.Retry Mechanisms & Idempotency: Plivo has its own webhook retry mechanism on
5xx
errors or timeouts. Returning200 OK
prevents retries. To handle potential duplicate deliveries (e.g., due to network issues or retries before a200 OK
was received), make your webhook idempotent. This means processing the sameMessageUUID
multiple times should not cause duplicate side effects. The database check shown in Section 6 helps achieve idempotency for message logging.6. Creating a Database Schema and Data Layer
Let's store the incoming messages in our database using Prisma.
Define Prisma Schema: Open
api/db/schema.prisma
and add a model to store SMS messages:id
,createdAt
,updatedAt
: Standard audit fields.direction
: Tracks if the message was incoming or outgoing using a Prisma Enum.fromNumber
,toNumber
,text
: Core message details.plivoMessageUuid
: Plivo's unique identifier. Making it@unique
allows easy lookup and helps prevent duplicate processing (idempotency).status
: Optional field to track delivery status (requires setting up Plivo status callbacks).rawPayload
: Storing the raw JSON payload from Plivo can be useful for debugging.Create and Apply Migration: Run the Prisma migrate command to generate SQL and apply it to your database:
This creates a new migration file in
api/db/migrations/
and updates your database schema.Generate Prisma Client: RedwoodJS usually handles this automatically after migration, but you can run it manually if needed:
Update Webhook to Save Message: Modify
api/src/functions/plivoSmsWebhook.js
within thetry...catch
block added in Section 5 to use thedb
client to save the incoming message. This includes an idempotency check usingplivoMessageUuid
.Data Access Patterns/Services (Optional but Recommended): For more complex applications, isolating database logic into RedwoodJS services is highly recommended for better organization, testability, and reusability. While this guide uses direct
db
calls in the function for simplicity, here's how you might structure it using a service:api/src/services/smsMessages/smsMessages.js
:db
calls remain functional for this basic example.7. Adding Security Features
Securing your webhook endpoint is critical to prevent unauthorized access and ensure data integrity. This section shows how to integrate signature validation into the handler function.
Webhook Signature Validation (Essential): Plivo signs webhook requests using your Auth Token. Verifying this signature is crucial.
X-Plivo-Signature-V3
,X-Plivo-Signature-V3-Nonce
, andX-Plivo-Signature-V3-Timestamp
headers to its requests.validateV3Signature
utility.Integrate Validation into
plivoSmsWebhook.js
: Add the following validation logic at the very beginning of thehandler
function, before parsing the body.