Frequently Asked Questions
Create a Next.js API route at /api/plivo/status
to receive POST requests. Install the plivo-node
SDK to validate signatures, ensuring requests originate from Plivo. Configure your Plivo application's Message URL to point to this endpoint, selecting POST as the method.
The X-Plivo-Signature-V3
header, along with the nonce, is crucial for verifying the authenticity of incoming webhook requests from Plivo. It ensures that the request genuinely came from Plivo and hasn't been tampered with, preventing security risks.
Plivo sends status callbacks to provide real-time updates on the delivery status of your SMS messages. The initial API response only confirms Plivo accepted the message, not final delivery. Callbacks provide updates such as 'delivered', 'failed', or 'undelivered'.
Use ngrok
during local development to create a secure tunnel, allowing Plivo to reach your local server. This lets you test your webhook endpoint before deploying, ensuring it handles callbacks correctly.
Yes, the article recommends using Prisma, a database toolkit, to optionally store status updates in a database like PostgreSQL. This allows you to maintain a history of message delivery statuses for analysis or reporting.
Use the plivo.validateV3Signature
function from the plivo-node
SDK. Provide the full request URL, nonce, signature, and your Plivo Auth Token. Ensure the URL used matches your Plivo application's Message URL exactly, including https://
and the path.
APP_BASE_URL
stores the base URL of your application, crucial for constructing the full URL used in signature validation. This variable is essential for Plivo to correctly verify incoming webhook requests, and it changes depending on your environment (local vs. production).
A 403 Forbidden error usually indicates signature validation failure. Double-check your APP_BASE_URL
and PLIVO_AUTH_TOKEN
environment variables. Ensure they match your Plivo application settings and that the URL used for validation includes the full path (/api/plivo/status
).
Ensure your API route is idempotent, meaning processing the same callback multiple times doesn't have unintended side effects. Using Prisma's upsert
for database updates can achieve this. Design your logic to handle potential duplicate callbacks gracefully.
Plivo callbacks include statuses like queued
, sent
, delivered
, undelivered
, and failed
. Implement logic in your Next.js route to handle each status appropriately, such as notifying support for failed messages or retrying undelivered ones.
The Message URL in your Plivo application settings must use the POST
method because Plivo sends message status callbacks as POST requests. Ensure the method is set to POST in the Plivo console to receive delivery updates.
Verify the Message URL in your Plivo application is correct and uses HTTPS. Check your Next.js server logs and the Plivo console logs for errors. Ensure ngrok
is running if testing locally. Confirm no firewalls block incoming requests to your endpoint.
Your Next.js API route should return a 2xx status code (ideally 200 OK) to acknowledge successful receipt of the Plivo callback. Failing to return a 2xx response or timing out will cause Plivo to retry sending the callback.
Error codes provide additional context when a message status is 'failed' or 'undelivered'. Refer to the Plivo documentation for the complete list of error codes. This helps diagnose specific delivery issues and implement targeted handling mechanisms in your Next.js application.
Reliably tracking the delivery status of SMS messages is crucial for applications that depend on timely communication. When you send an SMS via Plivo, the initial API response only confirms that Plivo accepted the message, not that it reached the recipient's handset. To get real-time updates on message delivery (e.g.,
delivered
,failed
,undelivered
), you need to implement a webhook endpoint that Plivo can call back.This guide provides a step-by-step walkthrough for building a robust webhook endpoint using Next.js API Routes to receive and process Plivo's message status callbacks. We will cover project setup, secure handling of Plivo requests, data persistence (optional), error handling, deployment, and verification.
Project Goal: Build a Next.js application with a dedicated API endpoint that securely receives message status updates from Plivo, validates the requests, and optionally stores the status information.
Technologies Used:
System Architecture:
Prerequisites:
ngrok
installed globally (npm install -g ngrok
) or available vianpx
.1. Setting up the Project
Let's initialize a new Next.js project and install necessary dependencies.
Create a Next.js App: Open your terminal and run:
--typescript
) for better type safety.--app
enables the App Router, which is standard for new Next.js projects and where we'll build our API route.Navigate to Project Directory:
Install Plivo SDK:
This package provides helper functions, most importantly for validating incoming webhook requests from Plivo.
(Optional) Install Prisma for Database Persistence: If you want to store the delivery statuses:
Initialize Prisma:
This creates a
prisma
directory with aschema.prisma
file and a.env
file for your database connection string.Configure Environment Variables: Create a file named
.env.local
in the root of your project. Never commit this file to version control. Add your Plivo credentials and (if using Prisma) your database URL:.env.local
is used by Next.js for local development environment variables.APP_BASE_URL
: This is critical for Plivo signature validation, as the validation function needs the exact URL Plivo is sending the request to. We'll update this later for local testing and production.2. Implementing the Callback API Route
We'll create a Next.js API Route to handle incoming POST requests from Plivo.
Create the API Route File: Inside the
src/app/api/
directory, create the following folder structure and file:src/app/api/plivo/status/route.ts
Implement the API Logic: Paste the following code into
src/app/api/plivo/status/route.ts
:Code Explanation:
X-Plivo-Signature-V3
andX-Plivo-Signature-V3-Nonce
headers sent by Plivo. Crucially, it constructs therequestUrl
using theAPP_BASE_URL
environment variable and the route's path (/api/plivo/status
). This exact URL is required for signature validation (e.g., ifAPP_BASE_URL
ishttps://foo.ngrok.app
, the URL used must behttps://foo.ngrok.app/api/plivo/status
).application/x-www-form-urlencoded
. We userequest.formData()
to parse it into a usable JavaScript object (bodyParams
).plivo.validateV3Signature
uses the full request URL, the nonce (a unique value per request), the signature provided by Plivo, and your Plivo Auth Token to verify the request's authenticity. If validation fails, a403 Forbidden
response is returned.MessageUUID
,Status
, andErrorCode
from the validated data.upsert
operation. It tries to find a record bymessageUuid
and update its status, or creates a new record if one doesn't exist. Usingupsert
helps handle potential duplicate callbacks from Plivo gracefully (idempotency). Error handling for the database operation is included, with a note on deciding how to respond to Plivo upon database failure.200 OK
JSON response. This is critical – Plivo needs this acknowledgment to know the callback was received successfully. If Plivo receives a non-2xx
response or times out, it will retry sending the callback according to its retry policy.3. (Optional) Creating a Database Schema
If you chose to store status updates, define the schema.
Define Prisma Schema: Open
prisma/schema.prisma
and add theMessageStatus
model:messageUuid
: Marked as@unique
because it's the primary identifier from Plivo.errorCode
: Optional (?
) as it's only present for certain statuses.Run Database Migration: Apply the schema changes to your database:
This command creates an SQL migration file and applies it to your database, creating the
MessageStatus
table. It will also generate/update the Prisma Client based on your schema.Uncomment Prisma Code: Go back to
src/app/api/plivo/status/route.ts
and uncomment the Prisma-related lines (import, client initialization, and the "Store Status in Database" section). Remember to handle Prisma client instantiation appropriately for your environment (e.g., using a cached helper in serverless).4. Configuring Plivo
Now, tell Plivo where to send the status updates.
Log in to Plivo Console: Go to https://console.plivo.com/.
Navigate to Messaging -> Applications: https://console.plivo.com/messaging/application/
Create or Edit an Application:
POST
./api/plivo/status
).ngrok
. See Section 5 below.https://your-app-name.vercel.app/api/plivo/status
).(Optional) Assign a Plivo Number: If you want Plivo to automatically use this application when receiving messages on a specific Plivo number, go to Phone Numbers -> Your Numbers, select a number, and choose your newly created application from the ""Application"" dropdown. This is not strictly required for outbound message status callbacks but is good practice if you handle both inbound and outbound.
Using the Application When Sending SMS: When sending an outbound SMS using the Plivo API, you need to specify the
url
parameter in your API request, pointing to your application's Message URL or directly to your webhook endpoint URL. Using an Application is generally cleaner. If you associated a number with the app, messages sent from that number might automatically use the app's settings (check Plivo defaults), but explicitly setting theurl
parameter when sending is the most reliable way to ensure status callbacks are sent to the correct endpoint.Example (Conceptual Node.js sending code):
5. Local Development and Testing with
ngrok
Plivo needs to reach your development machine to send callbacks.
ngrok
creates a secure tunnel.Start Your Next.js Dev Server:
This usually starts the server on
http://localhost:3000
.Start
ngrok
: Open a new terminal window and run:3000
if your Next.js app runs on a different port.Get the
ngrok
URL:ngrok
will display output similar to this:Copy the
https://
URL (e.g.,https://<random-subdomain>.ngrok-free.app
). This is your temporary public URL.Update Environment Variable: Open your
.env.local
file and updateAPP_BASE_URL
with thisngrok
HTTPS URL:Restart your Next.js development server (
Ctrl+C
andnpm run dev
) for the change to take effect.Update Plivo Application: Go back to your Plivo Application settings in the Plivo Console. Paste the full
ngrok
callback URL into the Message URL field:https://<random-subdomain>.ngrok-free.app/api/plivo/status
Ensure the method isPOST
. Save the application settings.Test by Sending an SMS: Use the Plivo API (via code like the example in Section 4, the Plivo console, Postman, or
curl
) to send an SMS from a Plivo number associated with your application or by explicitly setting theurl
parameter to yourngrok
callback URL.Observe Logs:
npm run dev
). You should see logs like "Received request...", "Received body params...", "Plivo signature validated...", and potentially "Successfully saved status..." if using the database.ngrok
. You should seePOST /api/plivo/status
requests listed with200 OK
responses.6. Error Handling, Logging, and Retries
try...catch
blocks. For production, consider more specific error handling:console.log
is suitable for development. For production:2xx
response within a timeout period (typically 5 seconds). Retries happen with an exponential backoff. This means your endpoint needs to be:upsert
example helps achieve this.7. Security Considerations
X-Plivo-Signature-V3
header usingplivo.validateV3Signature
and your Auth Token. Ensure you are using the exact full URL (includinghttps://
and the path/api/plivo/status
) that Plivo is configured to call. For example, ifAPP_BASE_URL
ishttps://foo.ngrok.app
, the URL used for validation must behttps://foo.ngrok.app/api/plivo/status
..env.local
for local development and your hosting provider's mechanism (e.g., Vercel Environment Variables) for production.ngrok
provides this automatically for local testing, and platforms like Vercel enforce it for deployments.8. Handling Different Statuses and Edge Cases
queued
,sent
,delivered
,undelivered
,failed
. Your application logic might need to react differently based on the status (e.g., notify support forfailed
messages, retry sendingundelivered
messages later).failed
orundelivered
, theErrorCode
field provides more detail. Consult the Plivo Error Codes documentation to understand the reasons for failure.upsert
).delivered
status might arrive before asent
status in rare network conditions). Rely on the timestamp (updatedAt
in the Prisma schema) if strict ordering is critical, or design your logic to handle out-of-order updates gracefully.9. Performance Optimizations
200 OK
) and then trigger a background job (using services like Vercel Background Functions, BullMQ, or external queueing systems) to perform the heavy lifting.messageUuid
andstatus
) to speed up queries. The Prisma schema example includes basic indexes.10. Monitoring and Observability
/api/plivo/status
function.GET
handler in the example can serve as a basic health check, although monitoring invocation logs and error rates is usually more informative for webhook endpoints.11. Troubleshooting and Caveats
Message URL
in your Plivo Application settings. Ensure it exactly matches your public endpoint URL (ngrok
or production), includes/api/plivo/status
, and useshttps
.POST
.ngrok
: If testing locally, ensurengrok
is running and hasn't expired. Check thengrok
console for request logs.ngrok
or standard hosting platforms like Vercel, but possible in self-hosted scenarios).403 Forbidden
/ Invalid Signature Errors:APP_BASE_URL
: Ensure theAPP_BASE_URL
environment variable in your Next.js app exactly matches the base URL part of the endpoint configured in Plivo (e.g.,https://<random-subdomain>.ngrok-free.app
orhttps://your-app.vercel.app
). Remember to includehttps://
.PLIVO_AUTH_TOKEN
environment variable is correct and matches the Auth Token used in your Plivo account. The validation function uses the Auth Token, not the Auth ID.process.env.APP_BASE_URL + '/api/plivo/status'
).500 Internal Server Error
:DATABASE_URL
).400 Bad Request
/ Missing Headers:DATABASE_URL
is correct.npx prisma migrate dev
).ngrok
Free Tier Limitations: Freengrok
tunnels have rate limits and temporary URLs. For sustained testing or production, consider a paidngrok
plan or deploying to a staging environment.12. Deployment and CI/CD
Deploying a Next.js app with API routes is straightforward on platforms like Vercel or Netlify.
Deploying to Vercel (Example):
.env.local
. Use a.gitignore
file (Next.js includes a default one).PLIVO_AUTH_ID
PLIVO_AUTH_TOKEN
DATABASE_URL
(Use your production database connection string)APP_BASE_URL
(Set this to your Vercel production URL, e.g.,https://your-project-name.vercel.app
) Ensure these are configured for the ""Production"" environment (and optionally ""Preview"" and ""Development"").Message URL
in your Plivo Application settings to use this production URL (e.g.,https://your-project-name.vercel.app/api/plivo/status
). Ensure the method isPOST
.package.json
or Vercel build settings to run migrations:""build"": ""prisma generate && prisma migrate deploy && next build""
. Note: Running migrations during the build process requires careful consideration, especially regarding database credentials and permissions. Sometimes manual migration execution or a separate migration service is preferred.13. Verification and Testing
Message URL
to point to your deployed endpoint.url
parameter pointing to your deployed endpoint./api/plivo/status
endpoint with sample Plivo payloads (including valid and invalid signatures/data). Assert that your endpoint returns the correct HTTP status codes and responses. If using a database, verify data persistence.