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:
ngrokinstalled 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.--appenables 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
prismadirectory with aschema.prismafile and a.envfile for your database connection string.Configure Environment Variables: Create a file named
.env.localin the root of your project. Never commit this file to version control. Add your Plivo credentials and (if using Prisma) your database URL:.env.localis 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.tsImplement the API Logic: Paste the following code into
src/app/api/plivo/status/route.ts:Code Explanation:
X-Plivo-Signature-V3andX-Plivo-Signature-V3-Nonceheaders sent by Plivo. Crucially, it constructs therequestUrlusing theAPP_BASE_URLenvironment variable and the route's path (/api/plivo/status). This exact URL is required for signature validation (e.g., ifAPP_BASE_URLishttps://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.validateV3Signatureuses 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 Forbiddenresponse is returned.MessageUUID,Status, andErrorCodefrom the validated data.upsertoperation. It tries to find a record bymessageUuidand update its status, or creates a new record if one doesn't exist. Usingupserthelps 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 OKJSON response. This is critical – Plivo needs this acknowledgment to know the callback was received successfully. If Plivo receives a non-2xxresponse 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.prismaand add theMessageStatusmodel:messageUuid: Marked as@uniquebecause 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
MessageStatustable. It will also generate/update the Prisma Client based on your schema.Uncomment Prisma Code: Go back to
src/app/api/plivo/status/route.tsand 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
urlparameter 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 theurlparameter 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
ngrokPlivo needs to reach your development machine to send callbacks.
ngrokcreates 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:3000if your Next.js app runs on a different port.Get the
ngrokURL:ngrokwill 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.localfile and updateAPP_BASE_URLwith thisngrokHTTPS URL:Restart your Next.js development server (
Ctrl+Candnpm 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
ngrokcallback URL into the Message URL field:https://<random-subdomain>.ngrok-free.app/api/plivo/statusEnsure 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 theurlparameter to yourngrokcallback 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/statusrequests listed with200 OKresponses.6. Error Handling, Logging, and Retries
try...catchblocks. For production, consider more specific error handling:console.logis suitable for development. For production:2xxresponse within a timeout period (typically 5 seconds). Retries happen with an exponential backoff. This means your endpoint needs to be:upsertexample helps achieve this.7. Security Considerations
X-Plivo-Signature-V3header usingplivo.validateV3Signatureand 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_URLishttps://foo.ngrok.app, the URL used for validation must behttps://foo.ngrok.app/api/plivo/status..env.localfor local development and your hosting provider's mechanism (e.g., Vercel Environment Variables) for production.ngrokprovides 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 forfailedmessages, retry sendingundeliveredmessages later).failedorundelivered, theErrorCodefield provides more detail. Consult the Plivo Error Codes documentation to understand the reasons for failure.upsert).deliveredstatus might arrive before asentstatus in rare network conditions). Rely on the timestamp (updatedAtin 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.messageUuidandstatus) to speed up queries. The Prisma schema example includes basic indexes.10. Monitoring and Observability
/api/plivo/statusfunction.GEThandler 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 URLin your Plivo Application settings. Ensure it exactly matches your public endpoint URL (ngrokor production), includes/api/plivo/status, and useshttps.POST.ngrok: If testing locally, ensurengrokis running and hasn't expired. Check thengrokconsole for request logs.ngrokor standard hosting platforms like Vercel, but possible in self-hosted scenarios).403 Forbidden/ Invalid Signature Errors:APP_BASE_URL: Ensure theAPP_BASE_URLenvironment 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.apporhttps://your-app.vercel.app). Remember to includehttps://.PLIVO_AUTH_TOKENenvironment 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_URLis correct.npx prisma migrate dev).ngrokFree Tier Limitations: Freengroktunnels have rate limits and temporary URLs. For sustained testing or production, consider a paidngrokplan 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.gitignorefile (Next.js includes a default one).PLIVO_AUTH_IDPLIVO_AUTH_TOKENDATABASE_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 URLin 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.jsonor 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 URLto point to your deployed endpoint.urlparameter pointing to your deployed endpoint./api/plivo/statusendpoint 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.