Frequently Asked Questions
You need Node.js, npm (or yarn), access to a PostgreSQL database, a Redis instance, a Plivo account, and a basic understanding of Node.js, Express, REST APIs, and databases. ngrok is essential for local development and testing.
Utilize the Plivo Node.js SDK along with a job queue system like BullMQ and Redis. This allows asynchronous sending of bulk SMS messages directly from your Node.js backend, ensuring reliable and scalable delivery, especially for personalized campaigns. The provided example uses a job queue to handle sending in the background, improving responsiveness.
Express.js acts as the API layer, handling HTTP requests and responses. It routes incoming requests to appropriate controllers, which then interact with services for business logic and Plivo for SMS functionality. Express.js provides the framework for structuring your backend application and routes communication through clear entrypoints like controllers.
PostgreSQL is a relational database used to store subscriber data, campaign details, and message logs, offering structured data management. Prisma simplifies database interactions by providing a modern ORM (Object-Relational Mapper), making it easier to work with the database in Node.js and providing typesafe methods for database access.
ngrok is crucial during development to expose your local server to the internet so Plivo webhooks can reach it. Plivo needs a public URL for incoming messages and status updates, hence ngrok acts as the temporary tunnel during development. Remember to replace the ngrok URL with your production URL when deploying.
Yes, for supported countries outside the US and Canada, you can use an Alphanumeric Sender ID instead of a phone number. Configure this in your Plivo account under Messaging > Sender IDs. Alphanumeric Sender IDs enable message personalization and branding but are limited by country support, so US/Canada usage generally requires a phone number instead.
The subscriberService.setOptOutStatus
function handles opt-outs by updating the isActive
flag in the subscriber record. The system can process incoming "STOP" messages via webhooks and automatically opt-out users. Ensure E.164 format is used consistently for matching.
BullMQ is a powerful job queue system that handles the asynchronous sending of SMS messages using Redis. By offloading message sending to a queue, the API remains responsive and avoids blocking on potentially long-running SMS operations, especially during bulk campaigns.
The plivoService.validateWebhookSignature
function validates incoming webhooks using Plivo's signature. This is crucial to prevent unauthorized requests. The function requires access to the raw (unparsed) request body. The code example shows how to configure this, but it relies on the req.rawBody
being populated correctly by middleware before any JSON body parsing.
Start by creating a directory, initializing npm, and installing required packages like Express, Plivo SDK, Prisma, BullMQ, Redis, and dotenv. Then, initialize Prisma, configure environment variables in the .env file, and set up nodemon
for development. The article provides detailed commands for this initial setup.
Create a Plivo Application in the Plivo console (Messaging > Applications > XML), set the Message URL to your webhook endpoint (use ngrok during development), and link your purchased Plivo number to this application. This enables Plivo to forward incoming messages to your backend.
A production webhook URL is essential because ngrok URLs are temporary. For your deployed application, you need a stable, publicly accessible URL (provided by your hosting platform) so Plivo can consistently reach your webhooks for incoming messages and status updates.
Plivo sends message status updates (e.g., sent, failed, delivered) to the Delivery Report URL you configured in your Plivo Application settings. Your application receives these updates via webhooks, and you can then update the status of SentMessage
records for logging and monitoring.
The SentMessage
model logs individual message attempts, associating them with a specific Campaign
and Subscriber
. It stores Plivo's messageUuid
, delivery status, and timestamps, enabling detailed tracking and reporting. The article also recommends including the full callback payload for status updates from Plivo via statusCallback
.
Build Production-Ready SMS Campaigns with Node.js, Express, and Plivo
Build a robust SMS marketing campaign application using Node.js, the Express framework, and the Plivo communication platform API. You'll learn how to set up the project, send bulk SMS messages, handle replies and opt-outs, manage subscribers, and deploy your application securely and reliably.
You'll build a system capable of managing subscriber lists, creating SMS campaigns, sending messages asynchronously via a job queue, handling incoming messages (like replies or STOP requests), and logging message statuses. This approach solves the challenge of sending personalized or bulk SMS messages reliably and scalably, directly from your application backend.
Technologies Used:
npm list plivo
and consult Plivo's migration guide if using v5+..env
file.System Architecture:
Prerequisites:
ngrok
installed for local development webhook testing.ngrok
's free tier URLs are temporary and not suitable for production.By the end of this guide_ you'll have a functional backend application capable of managing and executing SMS campaigns_ ready for further enhancement and deployment.
1. Project Setup and Configuration
Initialize the Node.js project and install necessary dependencies.
Create Project Directory:
Initialize Node.js Project:
Install Dependencies:
Version Notes:
plivo@4
: Ensures SDK v4.x compatibility with code examples (v5+ has breaking changes)bullmq@4
: Stable version compatible with the worker patterns shown (v5+ changes queue initialization)package.json
for stabilityInstall Development Dependencies:
prisma
: For Prisma CLI commands (migrations_ generation).nodemon
: Automatically restarts the server during development.jest
_supertest
: For testing.Initialize Prisma:
This creates a
prisma
directory with aschema.prisma
file and a.env
file.Configure Environment Variables (
.env
): Open the.env
file created by Prisma and add the following variables. Remove any unnecessary quotes.DATABASE_URL
: Connection string for your PostgreSQL database. Replace placeholders with your actual credentials. Use quotes if your password or user contains special characters.PLIVO_AUTH_ID
_PLIVO_AUTH_TOKEN
: Your Plivo API credentials.PLIVO_SENDER_ID
: The Plivo phone number (in E.164 format_ e.g._+14155551212
) or Alphanumeric Sender ID you'll use to send messages. Numbers are required for US/Canada.REDIS_URL
: Connection string for your Redis instance.PORT
: Port your Express application will run on.API_KEY
: A simple secret key for basic API authentication (use more robust methods like JWT for production).BASE_URL
: The public-facing base URL of your application. Critical for webhook configuration. Use yourngrok
URL during development_ and your production URL otherwise.Add
nodemon
script topackage.json
: Update thescripts
section in yourpackage.json
:Create Project Structure: Organize your project for maintainability:
Create these directories.
Basic Server Setup (
src/server.js
):Basic App Setup (
src/app.js
):This initial setup provides a solid foundation with essential dependencies, environment configuration, and a basic project structure.
2. Integrating with Plivo
Configure Plivo and set up the necessary components to interact with its API.
Sign Up/Log In to Plivo:
""[Plivo Trial]""
prefix added to messages. Purchase credits to remove these limitations.Get API Credentials:
.env
file forPLIVO_AUTH_ID
andPLIVO_AUTH_TOKEN
. Keep these secret! Do not commit them to version control.Get a Plivo Phone Number:
+14155551212
) and add it to your.env
file asPLIVO_SENDER_ID
.Configure Plivo Application for Webhooks: Plivo uses webhooks to notify your application about incoming messages and delivery status updates. You need to create a Plivo Application and link your Plivo number to it.
ngrok
.ngrok
:ngrok http 3000
(or yourPORT
).ngrok
(e.g.,https://<unique_id>.ngrok.io
).https://<unique_id>.ngrok.io/webhooks/plivo/incoming
(we'll create this endpoint later)..env
BASE_URL
to this ngrok URL during development.POST
.https://<unique_id>.ngrok.io/webhooks/plivo/status
. Set method toPOST
.Create Plivo Service (
src/services/plivoService.js
): This service encapsulates interaction with the Plivo SDK..env
.sendSms
handles sending a single message, including optional status callback configuration.createReplyXml
generates the XML Plivo expects for automatic replies via webhooks.validateWebhookSignature
secures your webhook endpoints. Implementation Note: This function requires the raw request body for validation to work correctly with POST requests. Ensure your middleware setup provides this (see Section 6). The URL construction usingBASE_URL + req.originalUrl
might also need adjustment if your application runs behind complex proxies.3. Database Schema and Data Layer
Use Prisma to define your database schema and interact with PostgreSQL.
Define Schema (
prisma/schema.prisma
): Update your schema file with models for Subscribers, Campaigns, and potentially Sent Messages for tracking.isActive
).messageUuid
, and tracking delivery status. AddedonDelete: Cascade
for referential integrity.Run Database Migration: Apply the schema changes to your database. Prisma creates SQL migration files.
Generate Prisma Client: Whenever you change your schema, regenerate the Prisma Client.
This updates the
@prisma/client
library with typesafe methods based on your schema.Database Client Configuration (
src/config/db.js
):This sets up a singleton Prisma client instance and includes basic query logging.
4. Implementing Core Functionality (API & Services)
Build the API endpoints and service logic for managing subscribers and campaigns, and for sending messages.
Structure:
src/routes/
): Define API endpoints and link them to controllers. Useexpress.Router
.src/controllers/
): Handle HTTP requests, perform validation, call services, and send responses.src/services/
): Contain the core business logic, interacting with the database (Prisma) and external services (Plivo).4.1. Subscriber Management
Subscriber Routes (
src/routes/subscriberRoutes.js
):Subscriber Service (
src/services/subscriberService.js
):Subscriber Controller (
src/controllers/subscriberController.js
):Deploy this application to production:
Frequently Asked Questions About Plivo SMS Marketing Campaigns
How do I set up Plivo for SMS campaigns in Node.js?
Set up Plivo SMS campaigns by: (1) creating a Plivo account and obtaining API credentials (Auth ID and Auth Token), (2) purchasing a Plivo phone number ($0.80–$2.00/month), (3) installing Plivo SDK v4 with
npm install plivo@4
, (4) initializing the client withnew plivo.Client(authId, authToken)
, and (5) implementing subscriber management with PostgreSQL/Prisma and job queuing with BullMQ. This guide provides complete code examples for production-ready implementation.What is TCPA compliance and do I need it for SMS marketing?
TCPA (Telephone Consumer Protection Act) compliance is required when sending marketing SMS to US numbers. Requirements include: (1) obtaining prior express written consent from recipients, (2) providing clear opt-out instructions in every message (e.g., "Reply STOP to unsubscribe"), (3) maintaining consent records with timestamps, and (4) honoring opt-outs immediately. Non-compliance carries penalties up to $1,500 per violation. Similar regulations exist globally: GDPR in EU and CASL in Canada. Consult legal counsel for compliance guidance.
How much does Plivo SMS cost?
Plivo phone numbers cost $0.80–$2.00/month depending on country. SMS rates vary by destination: US domestic SMS costs approximately $0.0035 per segment (160 characters), while international rates range $0.005–$0.20 per segment. Trial accounts include 20 free messages but have restrictions (verified numbers only, "[Plivo Trial]" prefix). Check Plivo's pricing page for current rates and purchase credits to remove trial limitations.
What's the difference between Plivo SDK v4 and v5?
Plivo SDK v5+ introduces breaking changes in client initialization and API methods compared to v4. This guide uses SDK v4 (plivo@4.x.x) syntax. If you install v5+, client initialization changes from
new plivo.Client()
to different patterns, and method signatures differ. Verify your installed version withnpm list plivo
. Consult Plivo's migration guide when upgrading from v4 to v5. Pin versions inpackage.json
for production stability.How do I handle SMS opt-outs with Plivo webhooks?
Handle opt-outs by: (1) configuring a webhook endpoint in your Plivo number settings, (2) implementing Express.js POST route to receive inbound messages, (3) validating webhook signatures using Plivo's signature verification, (4) checking message content for opt-out keywords (STOP, UNSUBSCRIBE, CANCEL, END, QUIT), (5) updating subscriber status to 'unsubscribed' in database, and (6) sending confirmation reply. This guide includes complete webhook implementation with signature validation and automatic opt-out processing.
Can I use Plivo trial account for production?
No, trial accounts have restrictions unsuitable for production: (1) SMS only sends to numbers verified in Plivo Console > Phone Numbers > Sandbox Numbers, (2) all messages include "[Plivo Trial]" prefix, and (3) limited to 20 free messages. Purchase Plivo credits to remove restrictions and enable production use. Trial accounts work well for development and testing before deploying to production.
How do I validate phone numbers for Plivo SMS?
Validate phone numbers using E.164 format:
+
followed by country code and subscriber number (1-15 digits total). Basic regex validation:/^\+[1-9]\d{1,14}$/
checks format but doesn't validate country code validity or number length per country. For production, uselibphonenumber-js
library for comprehensive validation including country-specific rules. This guide includes Express.js validation middleware withexpress-validator
for API endpoints.What is BullMQ and why use it for SMS campaigns?
BullMQ is a Redis-based job queue for Node.js that handles asynchronous SMS sending. Use BullMQ to: (1) process bulk messages without blocking API responses, (2) implement retry logic with exponential backoff for failed deliveries, (3) rate limit API calls to comply with Plivo's throttling, (4) scale horizontally by adding worker processes, and (5) track job status and delivery attempts. This guide uses BullMQ v4 (bullmq@4) with complete worker implementation and error handling.
How do Plivo webhooks work for delivery status?
Plivo webhooks send HTTP POST requests to your configured endpoint when message status changes. Configure webhooks in Plivo Console > Phone Numbers > select number > Message URL. Your endpoint receives delivery status (queued, sent, delivered, undelivered, failed) and metadata (MessageUUID, timestamp, error codes). Always validate webhook signatures using Plivo's signature verification to prevent unauthorized requests. This guide includes complete webhook handling with Express.js routes and signature validation.
What database should I use for SMS campaigns?
Use PostgreSQL for SMS campaigns due to: (1) robust ACID compliance for transactional data, (2) excellent support for JSON data types for campaign metadata, (3) powerful indexing for phone number lookups, (4) reliable connection pooling for high concurrency, and (5) strong ecosystem with ORMs like Prisma. This guide uses PostgreSQL with Prisma ORM for type-safe database queries, complete schema definitions for subscribers and campaigns, and proper indexing for performance.
How do I deploy Plivo SMS campaigns to production?
Deploy to production by: (1) using environment variables for all credentials (never commit .env files), (2) implementing process managers like PM2 for automatic restarts, (3) configuring SSL/TLS certificates for webhook endpoints, (4) setting up monitoring with Winston logging and health check endpoints, (5) implementing rate limiting with express-rate-limit, (6) using connection pooling for PostgreSQL and Redis, and (7) setting up automated database backups. Deploy to platforms like AWS, DigitalOcean, or Heroku with managed PostgreSQL and Redis services.
Can I schedule SMS campaigns for specific times?
Yes, schedule campaigns using BullMQ's delayed job feature. Add jobs with delay option:
await smsQueue.add('send-sms', data, { delay: millisecondsUntilSend })
. Calculate delay based on target timestamp minus current time. For recurring campaigns, implement CRON-based scheduling or use BullMQ's repeat option. Store campaign schedules in PostgreSQL campaigns table with sendAt timestamps and process with scheduled workers. This approach handles timezone conversions and respects user preferences.Conclusion
You've built a production-ready SMS marketing campaign system using Plivo, Node.js, Express, PostgreSQL, and BullMQ. This implementation includes:
Next Steps:
This foundation provides the essential building blocks for a scalable SMS marketing platform. Customize and extend these components to meet your specific business requirements while maintaining code quality and compliance standards.