How to Send SMS with Plivo in Node.js: Step-by-Step Tutorial (2025)
Learn how to integrate Plivo SMS API with Node.js Express. Step-by-step tutorial covering authentication, phone number validation, error handling, and deployment. Includes Express 4.x and Plivo SDK 4.x code examples.
This guide provides a step-by-step walkthrough for building a simple yet robust Node.js application using the Express framework to send SMS messages via the Plivo API. We'll cover everything from project setup and configuration to implementing the core sending logic, handling errors, and testing the endpoint.
Learn how to send SMS programmatically using Plivo's messaging API with Node.js and Express. This tutorial demonstrates SMS integration for authentication codes, notifications, alerts, and marketing campaigns.
Estimated Time: 20-30 minutes for complete setup and testing. Add 10-15 minutes if this is your first time working with Plivo or configuring environment variables.
Common Use Cases:
OTP (One-Time Password) delivery for user authentication
Appointment reminders and notifications
Order confirmations and shipping updates
Alert systems and emergency notifications
Marketing campaigns and promotional messages
By the end of this tutorial, you will have a functional Express API endpoint capable of accepting requests to send SMS messages. This forms a foundational building block for integrating SMS notifications, alerts, or communication features into larger applications.
Technologies Used:
Node.js: A JavaScript runtime environment for building server-side applications.
Express.js: A minimal and flexible Node.js web application framework.
Plivo: A cloud communications platform providing APIs for SMS, voice, and more.
dotenv: A module to load environment variables from a .env file into process.env.
Version Compatibility Matrix:
Component
Tested Version
Min Version
Notes
Node.js
v20.x, v22.x
v18.x
v18.x minimum for async/await and ES6+ features
Express
4.18.x
4.17.0
Express 5.x in beta; use 4.x for production
Plivo SDK
4.14.x
4.0.0
Major API changes in v4.x vs v3.x
dotenv
16.3.x
16.0.0
Stable across 16.x versions
Prerequisites:
Before you start integrating Plivo SMS API with Node.js, ensure you have:
Node.js and npm (or yarn): Installed on your development machine. You can download them from nodejs.org.
Recommended: Node.js v20.x (Maintenance LTS - Iron) or v22.x (Active LTS - Jod) as of January 2025
Minimum: Node.js v18.x or higher for ES6+ support and modern async/await features
Verify your installation:
Bash
node --version # Should output v18.x or higher
npm --version # Should output 8.x or higher
Package Manager Choice: This tutorial uses npm (bundled with Node.js). Yarn is an alternative with faster installs and better dependency resolution. Use npm for simplicity; switch to Yarn if you need workspaces or prefer its lockfile format.
Plivo Auth ID and Auth Token: Found on your Plivo console dashboard upon logging in at console.plivo.com/dashboard.
Navigate to: Dashboard → Account section → Copy Auth ID and Auth Token
A Plivo Phone Number: You need an SMS-enabled Plivo phone number to act as the sender (src). You can purchase one from the Phone Numbers → Buy Numbers section of the Plivo console.
Important: Sending SMS to the US and Canada requires using a Plivo phone number as the sender ID. Other countries may support alphanumeric sender IDs (check Plivo documentation and local regulations).
Trial Account Limitation: If using a Plivo trial account, you can only send SMS messages to phone numbers verified in your Plivo console under Phone Numbers → Sandbox Numbers.
Pricing (as of January 2025, source: Plivo Pricing):
Phone Number Rental: US local/mobile numbers: $0.50/month; US toll-free: $1.00/month
SMS Rates (US): Outbound long code: starts at $0.0070/SMS; Toll-free: $0.0072/SMS
Global rates vary by country: Check Plivo SMS Pricing for specific regions
Setting Up Your Node.js Project for SMS Integration
Estimated Time: 5-7 minutes
Let's start by creating our project directory and initializing it with npm.
Create Project Directory:
Open your terminal or command prompt and run:
Bash
mkdir node-plivo-sms
cd node-plivo-sms
Initialize Node.js Project:
This creates a package.json file to manage dependencies and project metadata.
Bash
npm init -y
The -y flag accepts all default prompts automatically. Omit -y for interactive mode where you can customize project name, version, description, entry point, etc.
Install Dependencies:
We need Express for the web server, the Plivo Node.js SDK to interact with the API, and dotenv to manage environment variables securely.
Pinned vs Latest Versions: This tutorial pins specific versions for reproducibility. For new projects, consider using latest versions: npm install express plivo dotenv. However, pinned versions ensure compatibility with this guide's code examples and prevent breaking changes from automatic updates.
Why these versions? Express 4.18.2 and Plivo 4.14.0 are stable production releases with extensive documentation. Plivo SDK 4.x introduced async/await support (v3.x used callbacks).
Project Structure:
Create the basic files and directories. Your structure should look like this:
Configure .gitignore:
Create a file named .gitignore in the project root and add the following lines to prevent committing sensitive information and unnecessary files:
Why? The .env file will contain your secret API credentials, which should never be committed to version control. node_modules contains installed dependencies, which can be reinstalled using npm install.
Configure Plivo API Credentials and Environment Variables
Securely storing your Plivo credentials is crucial. We'll use environment variables managed by the dotenv package.
Create .env file:
In the root of your project, create a file named .env.
Add Plivo Credentials:
Open the .env file and add your Plivo Auth ID, Auth Token, and your Plivo phone number. Obtain these from your Plivo Console:
.env
# Plivo Credentials - Get from https://console.plivo.com/dashboard/
PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID
PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN
# Your Plivo phone number (must be SMS enabled)
# Use E.164 format (e.g., +14155551234)
PLIVO_SENDER_NUMBER=YOUR_PLIVO_PHONE_NUMBER
Replace YOUR_PLIVO_AUTH_ID, YOUR_PLIVO_AUTH_TOKEN, and YOUR_PLIVO_PHONE_NUMBER with your actual credentials and number.
Important: Ensure the phone number is in E.164 format (includes the + and country code).
Never commit .env files: Always add .env to .gitignore
Use platform-specific secret management:
AWS: AWS Secrets Manager or Systems Manager Parameter Store
Heroku:heroku config:set PLIVO_AUTH_ID=xxx
Google Cloud: Secret Manager
Azure: Key Vault
Docker: Use --env-file flag or Docker secrets for swarm mode
Rotate credentials regularly: Change Auth Token every 90 days minimum
Principle of least privilege: Create separate Plivo sub-accounts for different environments (dev, staging, prod)
Audit access: Monitor API usage in Plivo console for suspicious activity
Implement SMS Sending with Plivo API in Express
What this code accomplishes:
Loads environment variables and validates Plivo credentials
Initializes an Express web server with JSON parsing middleware
Creates a /send-sms POST endpoint that accepts to and text parameters
Validates phone number format (E.164) and required fields
Sends SMS via Plivo API with comprehensive error handling
Returns success/failure responses with detailed error information
Now, let's write the code in server.js to set up the Express server and integrate the Plivo client.
JavaScript
// server.js'use strict'; // Enforce stricter parsing and error handling// 1. Load Environment Variablesrequire('dotenv').config(); // Load variables from .env file into process.env// 2. Import Dependenciesconst express = require('express');
const plivo = require('plivo');
// 3. Initialize Express Appconst app = express();
app.use(express.json()); // Middleware to parse JSON request bodies// 4. Validate Essential Environment Variablesconst { PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SENDER_NUMBER } = process.env;
if (!PLIVO_AUTH_ID || !PLIVO_AUTH_TOKEN || !PLIVO_SENDER_NUMBER) {
console.error(
'Error: Plivo environment variables (PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SENDER_NUMBER) are missing or empty in .env file.'
);
process.exit(1); // Exit if essential config is missing
}
// 5. Initialize Plivo Client// Ensure you handle potential errors during client initialization if necessary,// though the SDK typically doesn't throw errors here unless inputs are malformed.const plivoClient = new plivo.Client(PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN);
// 6. Define the SMS Sending Route
app.post('/send-sms', async (req, res) => {
// --- Basic Input Validation ---const { to, text } = req.body; // Get destination number and message text from request bodyif (!to || !text) {
// Use 400 Bad Request for missing client inputreturn res.status(400).json({
success: false,
error: 'Missing required fields: `to` and `text` in request body.',
});
}
// Basic validation for 'to' number format (E.164)// Note: This regex is a basic check. Full E.164 validation is more complex// and might involve library lookups or more sophisticated patterns.if (!/^\+[1-9]\d{1,14}$/.test(to)) {
return res.status(400).json({
success: false,
error: 'Invalid `to` phone number format. Use E.164 format (e.g., +14155551234).',
});
}
// --- Send SMS using Plivo ---console.log(`Attempting to send SMS to: ${to}, Text: ""${text}""`);
try {
const response = await plivoClient.messages.create({
src: PLIVO_SENDER_NUMBER, // Sender ID (Your Plivo Number)dst: to, // Destination Number (From request)text: text, // Message Text (From request)
});
console.log('Plivo API Response:', response);
// Check if message was successfully queued (basic check based on presence of message_uuid)// The exact structure might vary slightly based on SDK version or specific scenarios.// Refer to Plivo Node SDK documentation for definitive response structure.if (response && response.message_uuid) {
res.status(200).json({
success: true,
message: 'SMS sent successfully queued.',
message_uuid: response.message_uuid, // Return the message UUID (string expected)api_id: response.api_id, // Return the API ID
});
} else {
// This case might indicate an issue not caught by an exception (unlikely but possible)console.error('Plivo response indicates message queuing failed or missing expected fields:', response);
res.status(500).json({
success: false,
error: 'Failed to queue SMS via Plivo (unexpected response structure).',
details: response, // Include Plivo response for debugging
});
}
} catch (error) {
console.error('Error sending SMS via Plivo:', error);
// Provide more specific feedback based on Plivo error if possible// Plivo errors often have status codes and detailed messagesconst statusCode = error.statusCode || 500; // Default to 500 Internal Server Errorconst errorMessage = error.message || 'An unexpected error occurred.';
// Plivo error details might be in error.error or nested differently.// Check Plivo Node SDK documentation for the specific error object structure.const errorDetails = error.error || {};
res.status(statusCode).json({
success: false,
error: `Plivo API Error: ${errorMessage}`,
details: errorDetails,
});
}
});
// 7. Start the ServerconstPORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Plivo Sender Number: ${PLIVO_SENDER_NUMBER}`);
console.log('API Endpoint: POST /send-sms');
console.log('Required Body: { ""to"": ""+1XXXXXXXXXX"", ""text"": ""Your message"" }');
});
Code Explanation:
Load Environment Variables:require('dotenv').config() loads the variables defined in your .env file into process.env. This must happen before you access these variables.
Import Dependencies: Imports the express framework and the plivo SDK.
Initialize Express App: Creates an instance of the Express application and uses express.json() middleware to automatically parse incoming requests with JSON payloads (like the one we'll send).
Request Size Limit: By default, express.json() has a limit of 100kb for request bodies (Express documentation). For larger payloads, configure explicitly: app.use(express.json({ limit: '10mb' })). SMS messages are typically under 5kb, so the default is sufficient.
Validate Environment Variables: Checks if the essential Plivo credentials are loaded. If not, it logs an error and exits, preventing the server from starting without proper configuration.
Initialize Plivo Client: Creates an instance of the Plivo client using your Auth ID and Auth Token from the environment variables.
Define /send-sms Route:
Sets up a POST endpoint at /send-sms.
Uses async/await for handling the asynchronous Plivo API call cleanly.
Input Validation: Extracts to (destination number) and text (message content) from the request body (req.body). It performs basic checks to ensure both fields are present and the to number roughly matches the E.164 format.
Phone Number Validation: The regex /^\+[1-9]\d{1,14}$/ is a basic structural check. For production applications, use libphonenumber-js for comprehensive validation:
Success Response: If the API call is successful (doesn't throw an error and returns expected fields like message_uuid), it logs the Plivo response and sends a 200 OK response back to the client with the message_uuid and api_id.
Error Handling: If the Plivo API call fails (e.g., invalid credentials, insufficient funds, invalid number), the catch block executes. It logs the detailed error and sends an appropriate HTTP status code (using error.statusCode if available, otherwise 500) and an error message back to the client, potentially including details from error.error.
Start Server: Starts the Express server, listening for incoming requests on the port specified by the PORT environment variable, or defaulting to 3000.
Source number incorrectly formatted or not SMS-enabled
Verify src in E.164 format and SMS capability
50
Invalid Destination Number
Destination not SMS-enabled or incorrect format
Verify dst in E.164 format
70
Destination Permanently Unavailable
Phone number inactive
Verify number with user
80
Destination Temporarily Unavailable
Handset off or out of coverage
Retry later
110
Message Too Long
Exceeds 1,600 chars (GSM) or 737 (UCS-2)
Split message or reduce length
200
STOP Opt-out
User opted out via STOP keyword
Remove from campaign; honor opt-out
300
Failed to Dispatch
Internal Plivo error
Not charged; retry or contact support
420
Message Expired
Queued >3 hours
Ensure 10DLC registration for US traffic
450
Country Disabled
Destination country blocked
Enable in console Geo Permissions
900
Insufficient Credit
Account balance too low
Add credits to account
950
10DLC Daily Limit
T-Mobile daily brand limit reached
Resets midnight PT; increase brand limits
1000
Unknown Error
Unspecified failure
Contact support with message UUIDs
Authentication Error (401): Invalid PLIVO_AUTH_ID or PLIVO_AUTH_TOKEN. Verify credentials in console.
Test Your SMS Integration Locally
Before starting the server, verify prerequisites:
Bash
# Check Node.js and npm versions
node --version # Should be v18.x or higher
npm --version # Should be 8.x or higher# Verify dependencies are installedls node_modules | grep -E "(express|plivo|dotenv)"# Verify .env file exists and contains credentialscat .env | grep -E "(PLIVO_AUTH_ID|PLIVO_AUTH_TOKEN|PLIVO_SENDER_NUMBER)"
Now, let's run the server and test the endpoint.
Start the Server:
Open your terminal in the project directory (node-plivo-sms) and run:
Bash
node server.js
You should see output like:
Code
Server running on port 3000
Plivo Sender Number: +1XXXXXXXXXX
API Endpoint: POST /send-sms
Required Body: { ""to"": ""+1YYYYYYYYYY"", ""text"": ""Your message"" }
If server fails to start:
"Cannot find module": Run npm install to reinstall dependencies
"Environment variables missing": Verify .env file exists and contains all three required variables
"EADDRINUSE" (port already in use): Another process is using port 3000. Either:
Kill the process: lsof -ti:3000 | xargs kill (Mac/Linux)
Use a different port: PORT=3001 node server.js
"Auth ID/Token invalid": Verify credentials are correct in .env (no extra spaces or quotes)
Test with curl:
Open a new terminal window (leaving the server running) and use curl (or a tool like Postman) to send a POST request to your endpoint.
Replace +1YYYYYYYYYY with a valid destination phone number (remember the trial account restrictions: use a verified sandbox number if applicable).
Replace ""Hello from Node and Plivo!"" with your desired message text.
Bash
curl -X POST http://localhost:3000/send-sms \
-H ""Content-Type: application/json"" \
-d '{
""to"": ""+1YYYYYYYYYY"",
""text"": ""Hello from Node and Plivo!""
}'
Check Response:
Successful Request: If everything works, curl should output something like (UUIDs and IDs will vary):
JSON
{""success"":true,""message"":""SMS sent successfully queued."",""message_uuid"":""some-unique-message-uuid-string"",""api_id"":""some-unique-api-id-string""}
Typical Delivery Time: SMS messages are typically delivered within 3-10 seconds for domestic routes (e.g., US to US). International messages may take 10-60 seconds depending on carrier routing. Messages queued during carrier maintenance may take longer.
Check the server logs for the Plivo API Response. You should also receive the SMS on the destination phone shortly after.
Failed Request (e.g., Validation Error): If you send an invalid request (e.g., missing to field):
JSON
{""success"":false,""error"":""Missing required fields: `to` and `text` in request body.""}
Failed Request (e.g., Plivo API Error): If Plivo rejects the request (e.g., invalid Auth ID):
JSON
{""success"":false,""error"":""Plivo API Error: Authentication credentials invalid. Check your Auth ID and Auth Token."",""details"":{}// Plivo might provide more details here depending on the error}
Check the server logs for the detailed Error sending SMS via Plivo.
Verify SMS Delivery: Check the destination phone for the incoming SMS message. You can also check delivery status in the Plivo console:
Navigate to Messaging → Logs → SMS Logs
Filter by time range and view detailed status (queued, sent, delivered, failed)
Click on a message UUID to see full delivery report including carrier responses
Troubleshooting Common Plivo SMS API Issues
Diagnostic Checklist
Step 1: Verify Environment
Node.js version ≥ v18.x (node --version)
All dependencies installed (npm list --depth=0)
.env file exists with all three variables
No syntax errors in server.js (node --check server.js)
Step 2: Test Connectivity
Server starts without errors
Can reach http://localhost:3000 (or configured port)
Network allows outbound HTTPS to api.plivo.com
Step 3: Verify Credentials
Auth ID and Auth Token match Plivo console exactly
Sender number is SMS-enabled and owned by your account
Sender number is in E.164 format with + prefix
Step 4: Check Plivo Account
Account has sufficient credits (check console dashboard)
Destination country is enabled in Geo Permissions
For trial accounts: destination number is added to Sandbox Numbers
No rate limit warnings in console
Common Issues and Solutions
Authentication Errors (401 Unauthorized): Double-check your PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN in the .env file. Ensure they are copied correctly from the Plivo console and that the .env file is being loaded (no typos in require('dotenv').config()).
Invalid Sender ID: Ensure PLIVO_SENDER_NUMBER is a valid, SMS-enabled Plivo number you own, formatted in E.164. If sending outside the US/Canada, you might need to register an Alphanumeric Sender ID with Plivo support depending on the destination country's regulations. Check Plivo's SMS API coverage page for country-specific rules.
Invalid Destination Number (to): Ensure the to number in your request body is in E.164 format (+ followed by country code and number).
Trial Account Restrictions: Remember you can only send to verified sandbox numbers on a trial account. Add numbers under Phone Numbers → Sandbox Numbers in the Plivo console.
Insufficient Funds: If you're using a paid account, ensure you have enough credits.
Check balance via console: Navigate to Account → Overview → Account Balance
Low balance alerts: Configure in Account → Alerts to receive notifications at custom thresholds
Rate Limiting: Plivo enforces rate limits on API requests. For high-volume sending, implement proper queuing and potentially exponential backoff in your application logic (beyond the scope of this basic guide).
Best practice: Implement exponential backoff when receiving 429 errors
Encoding: Plivo handles GSM and Unicode characters (including emojis). Be aware that messages with Unicode characters have a lower character limit per SMS segment (70 vs. 160 for GSM). Long messages are automatically concatenated. See Plivo's Encoding and Concatenation guide.
GSM-7 Encoding: Single segment = 160 characters, concatenated segments = 153 characters each
Unicode (UCS-2/UTF-16): Single segment = 70 characters, concatenated segments = 67 characters each
Character Types: GSM-7 supports basic Latin characters; Unicode required for emojis, non-Latin scripts, and special symbols
Message limits: Maximum 1,600 characters (GSM) or 737 characters (Unicode) total length
Network Issues: Ensure your server can reach Plivo's API endpoints (api.plivo.com). Firewalls or network configurations could block outgoing connections.
Then test with: curl http://localhost:3000/test-plivo
SDK Response/Error Structure: The exact fields returned by the Plivo SDK upon success (response.message_uuid, response.api_id) or the structure of the error object in the catch block might change between SDK versions. Always refer to the official Plivo Node SDK documentation for the version you are using.
Deploy Your Node.js SMS Application to Production
While this guide focuses on local development, here are key points for deploying this application:
Environment Variables:Never commit your .env file. Production environments provide mechanisms to securely set environment variables directly on the platform. Configure PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_SENDER_NUMBER in your hosting provider's settings.
Platform-specific examples:
Bash
# Heroku
heroku config:set PLIVO_AUTH_ID=MA... PLIVO_AUTH_TOKEN=xxx... PLIVO_SENDER_NUMBER=+1...
# AWS Elastic Beanstalk
eb setenv PLIVO_AUTH_ID=MA... PLIVO_AUTH_TOKEN=xxx... PLIVO_SENDER_NUMBER=+1...
# Google Cloud Run
gcloud run services update SERVICE_NAME \
--set-env-vars PLIVO_AUTH_ID=MA...,PLIVO_AUTH_TOKEN=xxx...,PLIVO_SENDER_NUMBER=+1...
# Docker
docker run -e PLIVO_AUTH_ID=MA... -e PLIVO_AUTH_TOKEN=xxx... -e PLIVO_SENDER_NUMBER=+1... my-app
PORT Variable: Most hosting platforms automatically assign a PORT environment variable. The code const PORT = process.env.PORT || 3000; correctly uses the assigned port or falls back to 3000.
Process Management: Use a process manager like pm2 or rely on your platform's tools (e.g., Heroku Dynos, Docker containers) to keep your Node.js application running reliably and restart it if it crashes.
Logging: Implement more robust logging (e.g., using Winston or Pino) to capture detailed information in production, especially errors, and potentially send logs to a centralized logging service.
Security: Add rate limiting (e.g., using express-rate-limit) to your API endpoint to prevent abuse. Consider adding API key authentication if this endpoint will be exposed publicly or used by multiple clients.
CORS Configuration for Frontend Integration:
JavaScript
// Install: npm install corsconst cors = require('cors');
// Allow specific origin
app.use(cors({
origin: 'https://yourdomain.com',
methods: ['POST'],
credentials: true
}));
// Or allow all origins (development only)
app.use(cors());
HTTPS/SSL Requirements:
Production applications must use HTTPS to encrypt API credentials in transit
Webhook URLs for delivery callbacks must be HTTPS (HTTP will be rejected)
Heroku: Automatic SSL for custom domains on paid dynos
Local HTTPS testing: Use mkcert or self-signed certificates for development
Conclusion and Next Steps
You have successfully built a basic Node.js Express application capable of sending SMS messages using the Plivo API. You learned how to set up the project, manage credentials securely, implement the sending logic with error handling, and test the API endpoint.
Further Enhancements:
Receiving SMS: Implement a webhook endpoint using Express to receive incoming SMS messages sent to your Plivo number. For two-way messaging, see our Plivo inbound messaging tutorial.
Status Callbacks: Configure a url or log parameter in the messages.create call or Plivo Application settings to receive status updates (e.g., delivered, failed) for sent messages.
More Robust Validation: Implement more comprehensive validation for phone numbers (potentially using a dedicated library like libphonenumber-js) and message content.
Web Interface: Build a simple frontend (using HTML, CSS, and JavaScript or a framework like React/Vue/Angular) to interact with your API.
Database Integration: Store message logs, user data, or application state in a database.
Bulk Sending: Adapt the logic to handle sending messages to multiple recipients efficiently, potentially using Promise.all for better concurrency while respecting Plivo rate limits.
Monitoring and Analytics:
Plivo Console Analytics: View message volume, delivery rates, and error trends at Messaging → Analytics
Custom Monitoring: Implement application-level tracking with tools like:
DataDog, New Relic, or Application Insights for APM
Prometheus + Grafana for self-hosted metrics
CloudWatch (AWS), Stackdriver (GCP), or Azure Monitor for cloud-native monitoring
Key Metrics to Track:
Message success rate (delivered / total sent)
Average delivery time
Error code distribution
API response times
Account balance trends
This foundation enables you to integrate powerful SMS communication features into your Node.js applications effectively and securely.
Frequently asked questions
This guide provides a step-by-step process for sending SMS messages from a Node.js application using the Express framework and Plivo API. It involves setting up a project, installing dependencies like Express and Plivo, configuring environment variables, and implementing the send SMS logic within an Express route. You'll create an endpoint that accepts the destination number and message text to trigger SMS sending via Plivo.
Plivo is a cloud communications platform that provides APIs for various communication services including sending and receiving SMS messages. In this tutorial, Plivo's SMS API is utilized to send text messages from your Node.js application. You'll need a Plivo account, Auth ID, Auth Token, and a Plivo phone number to use the API.
Dotenv helps manage environment variables securely, loading credentials from a .env file into process.env. This prevents your sensitive API keys from being exposed in your codebase or committed to version control. It is crucial for keeping your Plivo Auth ID and Auth Token secret.
Environment variables should be validated at the start of your application, before using them. In the server.js file, the code checks for PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_SENDER_NUMBER right after loading the .env file. If these are not present, the server exits with an error.
Your Plivo sender number is configured using an environment variable. Set a PLIVO_SENDER_NUMBER variable in your .env file with your Plivo phone number (in E.164 format). This number must be an SMS-enabled number purchased through your Plivo account.
The /send-sms endpoint expects a POST request with a JSON body containing two fields: 'to' and 'text'. The 'to' field should contain the recipient's phone number in E.164 format (e.g., +14155551234). The 'text' field should contain the SMS message content as a string.
The example code includes comprehensive error handling. It validates input parameters and catches errors during the Plivo API call. If an error occurs, it logs the error details and returns an appropriate error response to the client, including the HTTP status code and error message from Plivo.
Alphanumeric sender IDs are supported by Plivo but primarily for sending outside the US and Canada. Using one depends on local regulations for the recipient's country, and might involve registration with Plivo. For US/Canada SMS, you need a Plivo phone number as the sender ID.
After starting your Node server, use a tool like curl or Postman to send POST requests to http://localhost:3000/send-sms. Replace placeholders in the example request with the recipient's phone number and your desired message. Check your server logs and the recipient's phone to confirm message delivery.
With a Plivo trial account, you can only send messages to phone numbers verified as sandbox numbers in your Plivo console under Phone Numbers -> Sandbox Numbers. This is important for testing and verifying your integration.
Plivo supports GSM and Unicode. GSM allows 160 characters, but Unicode messages (including emojis) have a lower limit of 70 characters per segment. Plivo automatically concatenates longer messages into multiple segments.
When deploying to production environments like Heroku or AWS, do not commit your .env file. Instead, utilize the platform's specific mechanisms to set your PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_SENDER_NUMBER directly within the environment.
A process manager like PM2 helps ensure your Node.js application runs continuously and restarts automatically in case of failures. This is essential for the reliability of your SMS sending service in production.
This guide provides a step-by-step walkthrough for building a simple yet robust Node.js application using the Express framework to send SMS messages via the Plivo API. We'll cover everything from project setup and configuration to implementing the core sending logic, handling errors, and testing the endpoint.
Learn how to send SMS programmatically using Plivo's messaging API with Node.js and Express. This tutorial demonstrates SMS integration for authentication codes, notifications, alerts, and marketing campaigns.
Estimated Time: 20-30 minutes for complete setup and testing. Add 10-15 minutes if this is your first time working with Plivo or configuring environment variables.
Common Use Cases:
By the end of this tutorial, you will have a functional Express API endpoint capable of accepting requests to send SMS messages. This forms a foundational building block for integrating SMS notifications, alerts, or communication features into larger applications.
Technologies Used:
.envfile intoprocess.env.Version Compatibility Matrix:
Prerequisites:
Before you start integrating Plivo SMS API with Node.js, ensure you have:
node --version # Should output v18.x or higher npm --version # Should output 8.x or highersrc). You can purchase one from the Phone Numbers → Buy Numbers section of the Plivo console.Setting Up Your Node.js Project for SMS Integration
Estimated Time: 5-7 minutes
Let's start by creating our project directory and initializing it with npm.
Create Project Directory: Open your terminal or command prompt and run:
mkdir node-plivo-sms cd node-plivo-smsInitialize Node.js Project: This creates a
package.jsonfile to manage dependencies and project metadata.-yflag accepts all default prompts automatically. Omit-yfor interactive mode where you can customize project name, version, description, entry point, etc.Install Dependencies: We need Express for the web server, the Plivo Node.js SDK to interact with the API, and
dotenvto manage environment variables securely.npm install express plivo dotenv. However, pinned versions ensure compatibility with this guide's code examples and prevent breaking changes from automatic updates.Project Structure: Create the basic files and directories. Your structure should look like this:
Quick setup commands:
touch .env .gitignore server.jsConfigure
.gitignore: Create a file named.gitignorein the project root and add the following lines to prevent committing sensitive information and unnecessary files:.envfile will contain your secret API credentials, which should never be committed to version control.node_modulescontains installed dependencies, which can be reinstalled usingnpm install.Configure Plivo API Credentials and Environment Variables
Securely storing your Plivo credentials is crucial. We'll use environment variables managed by the
dotenvpackage.Create
.envfile: In the root of your project, create a file named.env.Add Plivo Credentials: Open the
.envfile and add your Plivo Auth ID, Auth Token, and your Plivo phone number. Obtain these from your Plivo Console:YOUR_PLIVO_AUTH_ID,YOUR_PLIVO_AUTH_TOKEN, andYOUR_PLIVO_PHONE_NUMBERwith your actual credentials and number.+and country code).+prefix+symbol)+14155551234(US),+442071234567(UK),+919876543210(India)Production Security Best Practices:
.envfiles: Always add.envto.gitignoreheroku config:set PLIVO_AUTH_ID=xxx--env-fileflag or Docker secrets for swarm modeImplement SMS Sending with Plivo API in Express
What this code accomplishes:
/send-smsPOST endpoint that acceptstoandtextparametersNow, let's write the code in
server.jsto set up the Express server and integrate the Plivo client.// server.js 'use strict'; // Enforce stricter parsing and error handling // 1. Load Environment Variables require('dotenv').config(); // Load variables from .env file into process.env // 2. Import Dependencies const express = require('express'); const plivo = require('plivo'); // 3. Initialize Express App const app = express(); app.use(express.json()); // Middleware to parse JSON request bodies // 4. Validate Essential Environment Variables const { PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SENDER_NUMBER } = process.env; if (!PLIVO_AUTH_ID || !PLIVO_AUTH_TOKEN || !PLIVO_SENDER_NUMBER) { console.error( 'Error: Plivo environment variables (PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SENDER_NUMBER) are missing or empty in .env file.' ); process.exit(1); // Exit if essential config is missing } // 5. Initialize Plivo Client // Ensure you handle potential errors during client initialization if necessary, // though the SDK typically doesn't throw errors here unless inputs are malformed. const plivoClient = new plivo.Client(PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN); // 6. Define the SMS Sending Route app.post('/send-sms', async (req, res) => { // --- Basic Input Validation --- const { to, text } = req.body; // Get destination number and message text from request body if (!to || !text) { // Use 400 Bad Request for missing client input return res.status(400).json({ success: false, error: 'Missing required fields: `to` and `text` in request body.', }); } // Basic validation for 'to' number format (E.164) // Note: This regex is a basic check. Full E.164 validation is more complex // and might involve library lookups or more sophisticated patterns. if (!/^\+[1-9]\d{1,14}$/.test(to)) { return res.status(400).json({ success: false, error: 'Invalid `to` phone number format. Use E.164 format (e.g., +14155551234).', }); } // --- Send SMS using Plivo --- console.log(`Attempting to send SMS to: ${to}, Text: ""${text}""`); try { const response = await plivoClient.messages.create({ src: PLIVO_SENDER_NUMBER, // Sender ID (Your Plivo Number) dst: to, // Destination Number (From request) text: text, // Message Text (From request) }); console.log('Plivo API Response:', response); // Check if message was successfully queued (basic check based on presence of message_uuid) // The exact structure might vary slightly based on SDK version or specific scenarios. // Refer to Plivo Node SDK documentation for definitive response structure. if (response && response.message_uuid) { res.status(200).json({ success: true, message: 'SMS sent successfully queued.', message_uuid: response.message_uuid, // Return the message UUID (string expected) api_id: response.api_id, // Return the API ID }); } else { // This case might indicate an issue not caught by an exception (unlikely but possible) console.error('Plivo response indicates message queuing failed or missing expected fields:', response); res.status(500).json({ success: false, error: 'Failed to queue SMS via Plivo (unexpected response structure).', details: response, // Include Plivo response for debugging }); } } catch (error) { console.error('Error sending SMS via Plivo:', error); // Provide more specific feedback based on Plivo error if possible // Plivo errors often have status codes and detailed messages const statusCode = error.statusCode || 500; // Default to 500 Internal Server Error const errorMessage = error.message || 'An unexpected error occurred.'; // Plivo error details might be in error.error or nested differently. // Check Plivo Node SDK documentation for the specific error object structure. const errorDetails = error.error || {}; res.status(statusCode).json({ success: false, error: `Plivo API Error: ${errorMessage}`, details: errorDetails, }); } }); // 7. Start the Server const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); console.log(`Plivo Sender Number: ${PLIVO_SENDER_NUMBER}`); console.log('API Endpoint: POST /send-sms'); console.log('Required Body: { ""to"": ""+1XXXXXXXXXX"", ""text"": ""Your message"" }'); });Code Explanation:
require('dotenv').config()loads the variables defined in your.envfile intoprocess.env. This must happen before you access these variables.expressframework and theplivoSDK.express.json()middleware to automatically parse incoming requests with JSON payloads (like the one we'll send).express.json()has a limit of 100kb for request bodies (Express documentation). For larger payloads, configure explicitly:app.use(express.json({ limit: '10mb' })). SMS messages are typically under 5kb, so the default is sufficient./send-smsRoute:POSTendpoint at/send-sms.async/awaitfor handling the asynchronous Plivo API call cleanly.to(destination number) andtext(message content) from the request body (req.body). It performs basic checks to ensure both fields are present and thetonumber roughly matches the E.164 format./^\+[1-9]\d{1,14}$/is a basic structural check. For production applications, use libphonenumber-js for comprehensive validation:const { parsePhoneNumber } = require('libphonenumber-js'); try { const phoneNumber = parsePhoneNumber(to); if (!phoneNumber.isValid()) { return res.status(400).json({ error: 'Invalid phone number' }); } } catch (error) { return res.status(400).json({ error: 'Cannot parse phone number' }); }plivoClient.messages.create()to send the SMS.src: The sender number (your Plivo number from.env).dst: The destination number (from the request).text: The message content (from the request).url: Webhook URL for delivery status callbacksmethod: HTTP method for webhook (GET or POST)log: Set totrueto log message on Plivo consolepowerpack_uuid: Use Powerpack for number poolsurl: 'https://example.com/sms-status', method: 'POST', log: truemessage_uuid), it logs the Plivo response and sends a 200 OK response back to the client with themessage_uuidandapi_id.catchblock executes. It logs the detailed error and sends an appropriate HTTP status code (usingerror.statusCodeif available, otherwise 500) and an error message back to the client, potentially including details fromerror.error.PORTenvironment variable, or defaulting to 3000.Common Plivo Error Codes
Source: Plivo Error Codes Documentation
srcin E.164 format and SMS capabilitydstin E.164 formatAuthentication Error (401): Invalid
PLIVO_AUTH_IDorPLIVO_AUTH_TOKEN. Verify credentials in console.Test Your SMS Integration Locally
Before starting the server, verify prerequisites:
# Check Node.js and npm versions node --version # Should be v18.x or higher npm --version # Should be 8.x or higher # Verify dependencies are installed ls node_modules | grep -E "(express|plivo|dotenv)" # Verify .env file exists and contains credentials cat .env | grep -E "(PLIVO_AUTH_ID|PLIVO_AUTH_TOKEN|PLIVO_SENDER_NUMBER)"Now, let's run the server and test the endpoint.
Start the Server: Open your terminal in the project directory (
node-plivo-sms) and run:You should see output like:
If server fails to start:
npm installto reinstall dependencies.envfile exists and contains all three required variableslsof -ti:3000 | xargs kill(Mac/Linux)PORT=3001 node server.js.env(no extra spaces or quotes)Test with
curl: Open a new terminal window (leaving the server running) and usecurl(or a tool like Postman) to send a POST request to your endpoint.+1YYYYYYYYYYwith a valid destination phone number (remember the trial account restrictions: use a verified sandbox number if applicable).""Hello from Node and Plivo!""with your desired message text.curl -X POST http://localhost:3000/send-sms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+1YYYYYYYYYY"", ""text"": ""Hello from Node and Plivo!"" }'Check Response:
Successful Request: If everything works,
curlshould output something like (UUIDs and IDs will vary):{ ""success"": true, ""message"": ""SMS sent successfully queued."", ""message_uuid"": ""some-unique-message-uuid-string"", ""api_id"": ""some-unique-api-id-string"" }Typical Delivery Time: SMS messages are typically delivered within 3-10 seconds for domestic routes (e.g., US to US). International messages may take 10-60 seconds depending on carrier routing. Messages queued during carrier maintenance may take longer.
Check the server logs for the
Plivo API Response. You should also receive the SMS on the destination phone shortly after.Failed Request (e.g., Validation Error): If you send an invalid request (e.g., missing
tofield):{ ""success"": false, ""error"": ""Missing required fields: `to` and `text` in request body."" }Failed Request (e.g., Plivo API Error): If Plivo rejects the request (e.g., invalid Auth ID):
{ ""success"": false, ""error"": ""Plivo API Error: Authentication credentials invalid. Check your Auth ID and Auth Token."", ""details"": {} // Plivo might provide more details here depending on the error }Check the server logs for the detailed
Error sending SMS via Plivo.Verify SMS Delivery: Check the destination phone for the incoming SMS message. You can also check delivery status in the Plivo console:
Troubleshooting Common Plivo SMS API Issues
Diagnostic Checklist
Step 1: Verify Environment
node --version)npm list --depth=0).envfile exists with all three variablesserver.js(node --check server.js)Step 2: Test Connectivity
http://localhost:3000(or configured port)api.plivo.comStep 3: Verify Credentials
+prefixStep 4: Check Plivo Account
Common Issues and Solutions
Authentication Errors (401 Unauthorized): Double-check your
PLIVO_AUTH_IDandPLIVO_AUTH_TOKENin the.envfile. Ensure they are copied correctly from the Plivo console and that the.envfile is being loaded (no typos inrequire('dotenv').config()).Invalid Sender ID: Ensure
PLIVO_SENDER_NUMBERis a valid, SMS-enabled Plivo number you own, formatted in E.164. If sending outside the US/Canada, you might need to register an Alphanumeric Sender ID with Plivo support depending on the destination country's regulations. Check Plivo's SMS API coverage page for country-specific rules.Invalid Destination Number (
to): Ensure thetonumber in your request body is in E.164 format (+followed by country code and number).Trial Account Restrictions: Remember you can only send to verified sandbox numbers on a trial account. Add numbers under Phone Numbers → Sandbox Numbers in the Plivo console.
Insufficient Funds: If you're using a paid account, ensure you have enough credits.
const balance = await plivoClient.account.getBalance(); console.log(`Current balance: ${balance.cashCredits} credits`);Rate Limiting: Plivo enforces rate limits on API requests. For high-volume sending, implement proper queuing and potentially exponential backoff in your application logic (beyond the scope of this basic guide).
Encoding: Plivo handles GSM and Unicode characters (including emojis). Be aware that messages with Unicode characters have a lower character limit per SMS segment (70 vs. 160 for GSM). Long messages are automatically concatenated. See Plivo's Encoding and Concatenation guide.
Network Issues: Ensure your server can reach Plivo's API endpoints (
api.plivo.com). Firewalls or network configurations could block outgoing connections.// Add this test endpoint to server.js app.get('/test-plivo', async (req, res) => { try { const balance = await plivoClient.account.getBalance(); res.json({ success: true, connection: 'OK', balance: balance.cashCredits }); } catch (error) { res.status(500).json({ success: false, error: error.message }); } });curl http://localhost:3000/test-plivoSDK Response/Error Structure: The exact fields returned by the Plivo SDK upon success (
response.message_uuid,response.api_id) or the structure of theerrorobject in thecatchblock might change between SDK versions. Always refer to the official Plivo Node SDK documentation for the version you are using.Deploy Your Node.js SMS Application to Production
While this guide focuses on local development, here are key points for deploying this application:
Environment Variables: Never commit your
.envfile. Production environments provide mechanisms to securely set environment variables directly on the platform. ConfigurePLIVO_AUTH_ID,PLIVO_AUTH_TOKEN, andPLIVO_SENDER_NUMBERin your hosting provider's settings.Platform-specific examples:
# Heroku heroku config:set PLIVO_AUTH_ID=MA... PLIVO_AUTH_TOKEN=xxx... PLIVO_SENDER_NUMBER=+1... # AWS Elastic Beanstalk eb setenv PLIVO_AUTH_ID=MA... PLIVO_AUTH_TOKEN=xxx... PLIVO_SENDER_NUMBER=+1... # Google Cloud Run gcloud run services update SERVICE_NAME \ --set-env-vars PLIVO_AUTH_ID=MA...,PLIVO_AUTH_TOKEN=xxx...,PLIVO_SENDER_NUMBER=+1... # Docker docker run -e PLIVO_AUTH_ID=MA... -e PLIVO_AUTH_TOKEN=xxx... -e PLIVO_SENDER_NUMBER=+1... my-appPORTVariable: Most hosting platforms automatically assign aPORTenvironment variable. The codeconst PORT = process.env.PORT || 3000;correctly uses the assigned port or falls back to 3000.Process Management: Use a process manager like
pm2or rely on your platform's tools (e.g., Heroku Dynos, Docker containers) to keep your Node.js application running reliably and restart it if it crashes.PM2 Example:
# Install PM2 npm install -g pm2 # Start application pm2 start server.js --name plivo-sms # Configure auto-restart on reboot pm2 startup pm2 save # Create ecosystem.config.js for advanced configuration: module.exports = { apps: [{ name: 'plivo-sms', script: 'server.js', instances: 2, // Use 2 CPU cores exec_mode: 'cluster', env: { NODE_ENV: 'production' } }] }; # Start with config pm2 start ecosystem.config.jsSource: PM2 Documentation
Logging: Implement more robust logging (e.g., using Winston or Pino) to capture detailed information in production, especially errors, and potentially send logs to a centralized logging service.
Security: Add rate limiting (e.g., using
express-rate-limit) to your API endpoint to prevent abuse. Consider adding API key authentication if this endpoint will be exposed publicly or used by multiple clients.CORS Configuration for Frontend Integration:
// Install: npm install cors const cors = require('cors'); // Allow specific origin app.use(cors({ origin: 'https://yourdomain.com', methods: ['POST'], credentials: true })); // Or allow all origins (development only) app.use(cors());HTTPS/SSL Requirements:
mkcertor self-signed certificates for developmentConclusion and Next Steps
You have successfully built a basic Node.js Express application capable of sending SMS messages using the Plivo API. You learned how to set up the project, manage credentials securely, implement the sending logic with error handling, and test the API endpoint.
Further Enhancements:
urlorlogparameter in themessages.createcall or Plivo Application settings to receive status updates (e.g., delivered, failed) for sent messages.libphonenumber-js) and message content.Promise.allfor better concurrency while respecting Plivo rate limits.Monitoring and Analytics:
This foundation enables you to integrate powerful SMS communication features into your Node.js applications effectively and securely.