Frequently Asked Questions
Implement Plivo webhooks with a Fastify server to receive real-time delivery updates. By specifying a webhook URL in your Plivo SMS API requests, the system automatically sends status updates to the specified URL as the message progresses through various stages, from queued to delivered or failed.
The Plivo Node SDK (`@plivo/node-sdk`) simplifies interaction with the Plivo API within your Node.js application. It handles authentication, API requests, and responses. The project goals of this article are to install dependencies needed for plivo, like: fastify, node.js, etc.
Fastify is a high-performance web framework for Node.js, offering speed and a developer-friendly experience. Its efficiency makes it well-suited for handling real-time updates from Plivo with minimal overhead.
Signature validation is paramount for security and should be performed at the very beginning of your `/plivo/status` route handler. This verification confirms that incoming requests genuinely originate from Plivo and haven't been tampered with.
Create a dedicated route (e.g., `/plivo/status`) in your Fastify application. This endpoint will receive `POST` requests from Plivo with message status updates like `queued`, `sent`, `delivered`, or `failed`.
Ngrok creates a secure tunnel to your local development server, making it publicly accessible. This is essential because Plivo webhooks require an HTTPS URL, even during development. ngrok fulfills this requirement, enabling status updates from plivo.
Always respond with a `200 OK` to Plivo, even if your internal processing encounters errors. Log those errors for later investigation, but the immediate `200 OK` prevents Plivo from continuously retrying the webhook.
The webhook payload includes essential information such as `MessageUUID`, `Status` (e.g., 'sent', 'delivered', 'failed'), `Timestamp`, `From`, `To`, and `ErrorCode` (in case of failures). This data helps in updating internal systems and triggering appropriate actions.
Yes, use a tool like ngrok to create a public HTTPS URL for your local server. Then, configure your Plivo SMS API requests to use this ngrok URL as the webhook URL, enabling Plivo to reach your local endpoint.
Implement robust signature validation using the `X-Plivo-Signature-V3` and `X-Plivo-Signature-V3-Nonce` headers. This ensures only legitimate requests from Plivo are processed. Always use HTTPS and validate all incoming data.
Verify ngrok's status, ensure the `BASE_URL` in your `.env` file matches the ngrok URL, check server and Plivo console logs, examine firewalls, and confirm the URL path used in the API call to Plivo.
A 403 Forbidden error usually indicates a signature validation failure. Double-check your Plivo Auth Token, URL construction (especially behind proxies), proper handling of the nonce and raw request body, and ensure a timing-safe string comparison is used.
Design database updates to be idempotent. Use unique constraints based on MessageUUID and check timestamps to prevent older status updates from overwriting newer ones. This ensures data consistency even if Plivo sends duplicate webhooks.
Use a table with columns for `message_uuid` (unique), `sender_number`, `recipient_number`, `message_body`, `initial_status`, `current_status`, `status_timestamp`, `error_code`, and standard timestamps (`created_at`, `updated_at`). Index `message_uuid` for efficient lookups.
Real-time SMS Delivery Status: Implementing Plivo Webhooks with Fastify
Tracking the delivery status of SMS messages is crucial for applications that rely on timely communication. Knowing whether a message was successfully delivered, failed, or is still queued enables developers to build more robust and reliable systems, trigger follow-up actions, and provide better user feedback.
This guide provides a complete walkthrough for building a Fastify application that receives and processes SMS delivery status updates from Plivo via webhooks. We'll cover everything from initial project setup to deployment considerations, ensuring you have a production-ready solution.
Project Goals:
Technology Stack:
@plivo/node-sdk
).env
file.System Architecture:
The basic flow is as follows:
url
parameter pointing to your webhook endpoint.queued
,sent
,delivered
,failed
), Plivo sends an HTTPPOST
request containing the status details to the specified webhook URL.POST
request, processes the payload (e.g., logs the status), and responds to Plivo.(Note: The diagram above uses Mermaid syntax. You may need a Markdown previewer or environment that supports Mermaid to render it correctly.)
Prerequisites:
ngrok
or a similar tunneling service installed for local development testing.Final Outcome:
By the end of this guide, you will have a running Fastify application capable of sending SMS messages through Plivo and receiving real-time delivery status updates at a dedicated webhook endpoint. You'll also understand the essential considerations for making this system robust and secure.
1. Setting up the Project
Let's start by creating our project directory and setting up the basic structure and dependencies.
Step 1: Create Project Directory
Open your terminal and create a new directory for the project, then navigate into it.
Step 2: Initialize Node.js Project
Initialize the project using npm. The
-y
flag accepts the default settings.Step 3: Install Dependencies
We need Fastify for the web server, the Plivo Node SDK to interact with the Plivo API, and
dotenv
to manage environment variables.Step 4: Install Development Dependencies
We'll use
nodemon
to automatically restart the server during development when files change andpino-pretty
for more readable logs.Step 5: Configure
package.json
ScriptsOpen your
package.json
file and add the following scripts to the""scripts""
section. This allows us to easily start the server in development (withnodemon
and pretty logs) or production mode.Step 6: Create
.gitignore
FileCreate a
.gitignore
file in the root of your project to prevent sensitive information and unnecessary files from being committed to version control.Step 7: Create
.env
File for Environment VariablesCreate a file named
.env
in the project root. This file will store sensitive credentials and configuration. Never commit this file to Git.Step 8: Obtain Plivo Credentials and Configure
.env
Plivo Auth ID & Token:
PLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
fields in your.env
file.Plivo Phone Number:
+14155551212
).PLIVO_PHONE_NUMBER
field in your.env
file.Recipient Phone Number:
PHONE_NUMBER_TO
to your own mobile number in E.164 format (e.g.,+14155552323
) for testing.Base URL:
BASE_URL
blank for now. We will fill this in when we runngrok
later (Section 4). This URL tells Plivo where to send the status updates. It must be publicly accessible. Plivo requires HTTPS for webhooks in production, and it's highly recommended even for development.ngrok
provides HTTPS URLs automatically.Project Structure:
Your project should now look like this:
2. Implementing Core Functionality
Now_ let's create the Fastify server and implement the routes for sending SMS and receiving status updates.
Step 1: Create
server.js
Create a file named
server.js
in the project root.Step 2: Basic Server Setup and Environment Loading
Add the following code to initialize Fastify_ load environment variables_ and configure the Plivo client.
dotenv
first to ensure environment variables are available.logger: true
).BASE_URL
check is moved, as it's only needed when sending the first SMS that requires a callback URL.start
function attempts to listen on the specified port (defaulting to 3000) and logs success or failure. Listening on0.0.0.0
makes the server accessible from outside the container/machine if needed.Step 3: Implement the Webhook Endpoint (
/plivo/status
)This route will listen for
POST
requests from Plivo containing message status updates.Add the following code inside
server.js
, before the// --- Start the server ---
comment:/plivo/status
(POST
):request.body
.MessageUUID
,Status
).200 OK
response promptly. Heavy processing should occur after this response./health
(GET
): Standard health check endpoint./send-sms
(GET
):BASE_URL
before attempting to send, returning an error if it's missing.webhookUrl
.plivoClient.messages.create
with theurl
andmethod
for the callback.3. Building the API Layer (Webhook Focus)
For this guide, the primary ""API"" is the webhook endpoint
/plivo/status
designed to be consumed by Plivo.X-Plivo-Signature-V3
header and your Auth Token.zod
for more complex checks.POST /plivo/status
application/x-www-form-urlencoded
orapplication/json
. Example Payload (Form-urlencoded):X-Plivo-Signature-V3
,X-Plivo-Signature-V3-Nonce
.200 OK
: Success. Body like""Webhook received successfully""
.401
/403 Forbidden
: If signature validation fails.4xx
/5xx
: Plivo may retry. Avoid unless necessary.4. Integrating with Plivo (Setup Recap & Ngrok)
We've configured the Plivo client. Now, make your local server accessible using
ngrok
.Step 1: Start Ngrok
Open a new terminal window. Run
ngrok
to expose your Fastify port (default 3000).Step 2: Get Ngrok URL
Copy the HTTPS forwarding URL provided by
ngrok
(e.g.,https://xxxxxxxxxxxx.ngrok.io
).Step 3: Update
.env
FilePaste the copied HTTPS
ngrok
URL into theBASE_URL
variable in your.env
file.Step 4: Restart Fastify Server
Stop your Fastify server (
Ctrl+C
) and restart it (npm run dev
) to load the updated.env
file.Your server is now running, and the
BASE_URL
is set, allowing the/send-sms
route to work correctly and Plivo to reach your/plivo/status
endpoint via thengrok
URL.5. Error Handling, Logging, and Retry Mechanisms
fastify.log.info
,.warn
,.error
). Log context, especially error objects:fastify.log.error({ err: error }, '...')
. Configure production logging appropriately./plivo/status
):try...catch
for internal logic (DB updates, etc.).200 OK
to Plivo even if internal processing fails (log the internal error). Handle internal failures separately (alerts, queues).401
/403
only if signature validation fails./send-sms
):plivoClient.messages.create
intry...catch
.500
,502
) to the client triggering the send.2xx
responses or timeouts. Respond200 OK
quickly unless the request is invalid/unauthorized.plivoClient.messages.create
fails due to transient issues.6. Database Schema and Data Layer (Conceptual)
A production system needs to store status updates.
Conceptual Schema (PostgreSQL):
Data Layer (
/plivo/status
):200 OK
(or asynchronously):MessageUUID
,Status
,Timestamp
,ErrorCode
.UPDATE
statement based onmessage_uuid
, potentially checkingstatus_timestamp
to avoid overwriting newer updates with older ones.Data Layer (
/send-sms
):plivoClient.messages.create
:INSERT
a new record with status'pending'
.UPDATE
the record with the returnedMessageUUID
andinitial_status
(e.g.,'queued'
).7. Adding Security Features
Securing your webhook endpoint is critical.
Webhook Signature Validation (CRITICAL):
X-Plivo-Signature-V3
,X-Plivo-Signature-V3-Nonce
.https://yourdomain.com/plivo/status
). Note: If behind a proxy, this might require checking headers likeX-Forwarded-Proto
andHost
, not justrequest.url
. Consult Plivo's signature documentation for specifics on URL construction.X-Plivo-Signature-V3-Nonce
.addContentTypeParser
to handleapplication/x-www-form-urlencoded
and store the raw buffer, or use apreParsing
hook. Consult Fastify documentation for accessing the raw request body.url + nonce
.(url + nonce)
using yourPLIVO_AUTH_TOKEN
as the key. Base64 encode the result.X-Plivo-Signature-V3
header. Use a timing-safe comparison function.401
or403
.@plivo/node-sdk
provides a validation utility function. If not, use Node.js's built-incrypto
module./plivo/status
: Implement this validation at the very beginning of the route handler.(Note: Accessing
request.rawBody
requires explicit setup in Fastify. Consult Fastify's documentation on body parsing and accessing the raw body.)HTTPS: Enforce HTTPS.
ngrok
provides it for development. Use a reverse proxy (Nginx, Caddy) or load balancer for TLS termination in production.Input Validation: Sanitize/validate data before DB operations, even after signature validation.
Rate Limiting: Consider
fastify-rate-limit
as a defense-in-depth measure.Secrets Management: Use secure methods for managing
PLIVO_AUTH_ID
,PLIVO_AUTH_TOKEN
in production (environment variables via platform secrets management, Vault, etc.). Keep.env
out of Git.8. Handling Special Cases
queued
,sent
,failed
,delivered
,undelivered
,rejected
.ErrorCode
field forfailed
/undelivered
statuses. Refer to Plivo docs for code meanings.TIMESTAMPTZ
for storage.UPDATE ... WHERE message_uuid = ? AND (current_status != ? OR status_timestamp < ?)
). Check timestamps to avoid overwriting newer statuses with delayed older ones.9. Implementing Performance Optimizations
200 OK
immediately.message_uuid
.info
orwarn
in production.10. Adding Monitoring_ Observability_ and Analytics
/health
for basic monitoring.11. Troubleshooting and Caveats
ngrok
status/URL_BASE_URL
in.env
_ server logs_ Plivo console logs (Monitor -> Logs), firewalls, URL path in API call.PLIVO_AUTH_TOKEN
, URL construction (check Plivo docs, consider proxies), raw body access, nonce handling.localtunnel
(use with caution), Cloudflare Tunnel, or deploy to a cloud service (even a cheap VM) for persistent development/staging URLs.12. Deployment and CI/CD
BASE_URL
to your production URL.pm2
, systemd, or container orchestration (Docker, Kubernetes) for process lifecycle management.Dockerfile
:npm ci
), linting/testing, Docker build/push, and deployment (injecting secrets).13. Verification and Testing
Manual Verification Checklist:
npm run dev
).ngrok
running,BASE_URL
updated and server restarted./health
-> shows{""status"":""ok""}
./send-sms
.ngrok
web UI (http://127.0.0.1:4040
): SeePOST
s to/plivo/status
with200 OK
.4xx
error.Automated Testing (Integration): Use Fastify's testing capabilities.
npm install --save-dev tap
server.js
for Testability: Modifyserver.js
to export a function that builds and returns the Fastify app instance, instead of starting it directly.test/routes.test.js
):