Frequently Asked Questions
Always validate Plivo webhook signatures. This crucial security step verifies the authenticity of incoming requests, ensuring that the callback data truly originated from Plivo and protecting your application from malicious actors.
Implement a webhook endpoint in your Node.js application using a framework like NestJS. This endpoint receives real-time delivery updates from Plivo, allowing you to track message statuses like 'delivered', 'failed', or 'rejected'. This guide demonstrates building a robust system for managing these callbacks.
A Plivo delivery status callback (or webhook) is a notification sent by Plivo to your application when the status of an SMS message changes. This notification contains crucial information about the message delivery outcome, such as 'sent', 'delivered', 'failed', or other statuses. Callbacks enable real-time monitoring and response to delivery events.
NestJS provides a structured and efficient way to handle Plivo SMS callbacks. Its features, like validation pipes and dependency injection, simplify development and improve code maintainability. This makes building a robust and scalable callback handling system easier.
Yes, use a tool like ngrok to expose your local development server to the internet. This allows Plivo to send webhooks to your local endpoint during development and testing. Configure your Plivo application with the ngrok URL as the callback URL.
In the Plivo console, create a new application, providing a name and configuring the 'Delivery Report URL' to point to your application's callback endpoint. Ensure the method is set to 'POST'. Optionally, link a Plivo number to the application, though specifying the callback URL per message offers greater flexibility.
Data Transfer Objects (DTOs) in NestJS define the structure of incoming callback data. They enable automatic validation using decorators from 'class-validator', ensuring data integrity and type safety in your TypeScript code.
Plivo uses signature validation to ensure the security and integrity of webhook deliveries. It verifies that requests received by your application genuinely originate from Plivo, preventing unauthorized or malicious actors from sending fake callbacks.
Store Plivo credentials (Auth ID, Auth Token) as environment variables in a `.env` file. Use the `@nestjs/config` package in your NestJS application to load these variables securely, preventing them from being hardcoded in your codebase.
SQLite is suitable for development and testing. For production, consider more robust options like PostgreSQL or MySQL. Prisma, a database toolkit for Node.js, simplifies database interactions and schema management regardless of the chosen database.
NestJS, coupled with a DTO and the 'class-transformer', automatically parses incoming JSON payloads from Plivo into strongly-typed objects. This simplifies data access and ensures type safety within your application logic.
Return a 204 No Content status code. Plivo primarily needs an acknowledgment that you received the callback. A 2xx status signals successful receipt. Returning content isn't required.
Prisma simplifies database interactions in NestJS. Use it to define your data models, manage database migrations, and perform type-safe database queries, regardless of whether you use SQLite, PostgreSQL, MySQL, or other databases.
Use the `client.messages.create()` method of the Plivo Node.js SDK. Provide your sender number, recipient number, message text, and an object with the `url` property set to your callback URL and `method` set to 'POST'.
Handling delivery statuses for SMS messages is crucial for applications that rely on reliable communication. Knowing whether a message was delivered, failed, or was rejected enables developers to build more robust error handling, reporting, and user feedback mechanisms. Plivo provides delivery status updates via webhooks (callbacks), sending real-time information about message events to a URL you specify.
This guide provides a complete walkthrough for building a production-ready system in Node.js using the NestJS framework to receive, validate, process, and store Plivo SMS delivery status callbacks. We will cover project setup, secure handling of callbacks, data storage, testing, and deployment considerations.
By the end of this guide, you will have a functional NestJS application capable of:
Technologies Used:
@nestjs/config
: For managing environment variables securely.ngrok
: (For local development) A tool to expose local servers to the internet, necessary for receiving Plivo webhooks during testing.System Architecture:
Prerequisites:
npm install -g @nestjs/cli
ngrok
(optional but recommended for local testing): Download and installngrok
to expose your local development server.1. Setting up the NestJS project
First, create a new NestJS project and navigate into the directory.
plivo
: The official Plivo Node.js SDK.@nestjs/config
: For handling environment variables.class-validator
,class-transformer
: Used by NestJS for request validation via DTOs.prisma
,@prisma/client
: The Prisma CLI and Client for database interactions.Project Structure:
NestJS provides a standard structure. We will add modules for specific features like
messaging
andcallbacks
.2. Plivo account and application setup
Before writing code, configure your Plivo account to send callbacks to your application.
Phone Numbers
->Buy Numbers
and purchase a number capable of sending SMS messages in your desired region. Note this number down.Messaging
->XML Applications
.Add New Application
.NestJS Callback App
).http://example.com/callbacks/delivery-status
. We will update this later with our actual endpoint URL (likely anngrok
URL during development).Method
toPOST
.Create Application
. Note theApp ID
generated for this application (though we won't use it directly for sending messages with callback URLs specified per message).Phone Numbers
->Your Numbers
, click on your number, select your newly created application from theApplication Type
dropdown, and update. For this guide, we will specify the callback URL directly when sending the message. This approach is highly flexible, allowing different messages (e.g., transactional vs. marketing) sent from the same number to potentially use different callback endpoints or logic if needed.3. Environment configuration
Never hardcode sensitive credentials. Use environment variables.
Create
.env
file: In the root of your project, create a file named.env
:.env
is listed in your.gitignore
file to prevent committing secrets.Configure NestJS
ConfigModule
: Update your rootAppModule
to load and manage environment variables.ConfigModule.forRoot({ isGlobal: true })
makes theConfigService
available throughout your application via dependency injection without needing to importConfigModule
in every feature module.4. Building the callback endpoint
This endpoint will receive the
POST
requests from Plivo containing delivery status information.Generate Module, Controller, Service:
Define the DTO (Data Transfer Object): Create a DTO to define the expected structure of the callback payload and enable automatic validation using
class-validator
.Why DTOs? They provide clear contracts for your API endpoints, enable automatic request body validation using decorators, and improve type safety in your TypeScript code.
Implement the Controller: Set up the route to listen for POST requests.
Why
@HttpCode(HttpStatus.NO_CONTENT)
? Plivo generally just needs acknowledgment (a 2xx status code) that you received the callback. Sending back content isn't necessary, and 204 No Content is semantically correct. WhyLogger
? NestJS provides a built-in logger that's easy to use and integrates well with the framework. Effective logging is essential for debugging webhook issues.Implement the Service: Define the business logic for handling the status update.
5. Security: Validating Plivo signatures
It's critical to verify that incoming webhook requests actually originate from Plivo. Plivo provides signatures for this purpose (using
X-Plivo-Signature-V3
andX-Plivo-Signature-V3-Nonce
headers).Create Signature Validation Helper: Based on Plivo's documentation, create a utility function.
Important: Plivo's V3 signature includes the full URL (scheme, host, path, query), the nonce, and the raw request body concatenated. Ensure you reconstruct this correctly. Getting the raw body requires specific configuration in NestJS.
Enable Raw Body Parsing: Modify
main.ts
to access the raw request body.Create a NestJS Guard: Implement the signature validation logic within a guard.
Apply the Guard: Add
@UseGuards(PlivoSignatureGuard)
to thehandleDeliveryStatus
method inCallbacksController
, as shown earlier. Ensure theCallbacksModule
importsConfigModule
if it's not global, or providesConfigService
appropriately.6. Creating a database schema and data layer (Prisma)
Let's store the delivery statuses using Prisma and SQLite.
Initialize Prisma:
This creates
prisma/schema.prisma
and updates.env
withDATABASE_URL=""file:./dev.db""
.Define Schema: Edit
prisma/schema.prisma
.Why store
rawPayload
? It's invaluable for debugging issues with callbacks or understanding unexpected data formats.Run Migration: Apply the schema to your database.
This creates the SQLite database file (
prisma/dev.db
) and generates the Prisma Client.Create Prisma Service: Create a reusable service for Prisma Client.
Make sure
CoreModule
exportsPrismaService
and is imported inAppModule
.Inject PrismaService: The
CallbacksService
shown in Step 4 already includes the injection and usage ofPrismaService
.7. Sending a message and triggering callbacks
Now, let's implement the functionality to send an SMS and tell Plivo where to send the delivery report.
Generate Messaging Module/Service/Controller:
Implement Messaging Service:
Crucial Point: The
{ url: callbackUrl }
parameter inclient.messages.create
tells Plivo where to send the delivery status for this specific message. This overrides any default URL set in the Plivo Application settings.Implement Messaging Controller (for testing): Add a simple endpoint to trigger sending an SMS.