code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

Send SMS with Plivo API in Node.js Express: Complete 2025 Guide

Build a production-ready SMS API using Plivo and Node.js Express. Complete tutorial with authentication, error handling, rate limiting, and deployment best practices.

<!-- GAP: Title/description mentions Express but content title references WhatsApp integration incorrectly (Type: Critical, Priority: High) --> <!-- DEPTH: Introduction lacks specific learning outcomes and time estimate (Priority: Medium) -->

You'll build a Node.js application using the Express framework to send SMS messages via the Plivo API. This tutorial covers everything from initial project setup to deploying a functional API endpoint.

By the end, you'll have a simple but robust Express application capable of accepting API requests to send SMS messages, complete with basic error handling and considerations for production environments.

Project Overview and Goals

<!-- DEPTH: Missing time estimate, difficulty level, and prerequisites summary (Priority: High) -->

Goal: Create a backend service using Node.js and Express that exposes an API endpoint for sending SMS messages through Plivo.

Problem Solved: This service enables your applications to programmatically send SMS notifications, alerts, or messages without handling the complexities of SMS gateway protocols directly.

Technologies Used:

  • Node.js: A JavaScript runtime environment ideal for building scalable, non-blocking, event-driven servers.
  • Express.js: A minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
  • Plivo Node.js SDK: A library provided by Plivo to simplify interactions with their SMS API. Available as the plivo package on npm. Note: If you have legacy version 0.4.1 installed, uninstall it before installing the current version. The SDK is actively maintained at github.com/plivo/plivo-node.
  • Plivo: A cloud communications platform providing APIs for SMS, voice, and more.
  • dotenv: A zero-dependency module that loads environment variables from a .env file into process.env.

System Architecture:

<!-- EXPAND: Architecture diagram could benefit from request/response flow details and error handling paths (Priority: Medium) -->
text
+-------------+        +------------------------+        +--------------+        +------------------+
| User / App  | ---->  | Node.js/Express Server | ---->  | Plivo SDK    | ---->  | Plivo API Server |
| (API Client)|        | (Your Application)     |        | (HTTP Calls) |        | (Sends SMS)      |
+-------------+        +------------------------+        +--------------+        +------------------+
      |                        |                                                        |
      | POST /send-sms         | Uses Plivo Credentials                                 | Delivers SMS
      | { "to": …, "text": … } |                                                        | to Carrier
      |                        | Handles request,                                       |
      |                        | calls Plivo SDK                                        |
      +------------------------+--------------------------------------------------------+

What Are the Prerequisites for Plivo SMS with Node.js?

<!-- DEPTH: Prerequisites section lacks verification steps and troubleshooting for common setup issues (Priority: Medium) -->

Prerequisites:

  • Node.js and npm (or yarn): Install from nodejs.org. Node.js 18+ is required. As of January 2025, Node.js 22 is the current LTS version recommended for production applications. Node.js 18.x reaches end-of-life on April 30, 2025.
  • Plivo Account: Create a free trial at plivo.com.
  • Plivo Auth ID and Auth Token: Find these on your Plivo Console dashboard after logging in.
  • Plivo Phone Number or Sender ID:
    • US/Canada: You must use a Plivo phone number capable of sending SMS. Purchase one via the Plivo Console (Phone Numbers → Buy Numbers).
    • Other Countries: You might be able to use a registered Alphanumeric Sender ID (check Plivo documentation and local regulations) or a Plivo phone number.
  • Verified Destination Number (Trial Accounts): If using a Plivo trial account, you can only send SMS to numbers verified in your Plivo Console (Phone Numbers → Sandbox Numbers).
  • Note: This guide uses Express.js 4.x. Express 5.x (released October 2024) is now stable and includes built-in async error handling, but requires Node.js 18+.
<!-- GAP: Missing verification steps to confirm Node.js version and successful Plivo account setup (Type: Substantive, Priority: High) -->

How Do You Set Up a Node.js Express Project for Plivo?

1. Setting Up the Project

Initialize your Node.js project and install the necessary dependencies.

  1. Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.

    bash
    mkdir plivo-sms-sender
    cd plivo-sms-sender
  2. Initialize Node.js Project: Initialize the project using npm. The -y flag accepts the default settings.

    bash
    npm init -y

    This creates a package.json file.

  3. Install Dependencies: Install Express for the web server, the Plivo SDK for interacting with the API, and dotenv for managing environment variables securely.

    bash
    npm install express plivo dotenv
<!-- DEPTH: Missing explanation of dependency version locking and package-lock.json importance (Priority: Low) -->
  1. Create Project Structure: Create the main application file and a file for environment variables.

    bash
    touch app.js .env .gitignore

    Your basic structure should look like:

    text
    plivo-sms-sender/
    ├── app.js
    ├── node_modules/
    ├── package.json
    ├── package-lock.json
    ├── .env
    └── .gitignore
  2. Configure .gitignore: Add node_modules and .env to your .gitignore file to prevent committing dependencies and sensitive credentials.

    Code
    node_modules
    .env

    Why .gitignore? It prevents accidentally publishing sensitive information (like API keys in .env) and unnecessary large directories (node_modules) to version control systems like Git.

How Do You Configure Environment Variables for Plivo?

2. Environment Configuration

Store your sensitive Plivo credentials securely using environment variables.

  1. Edit the .env file: Open the .env file you created and add your Plivo credentials and sender information.

    Code
    # Plivo API Credentials – Find these on your Plivo Console Dashboard
    PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID
    PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN
    
    # Plivo Sender Information
    # Use a Plivo phone number (E.164 format, e.g., +14155551234) for US/Canada
    # Or use a registered Alphanumeric Sender ID for supported countries
    PLIVO_SENDER_ID=+1XXXXXXXXXX
    
    # Server Configuration
    PORT=3000
  2. Replace Placeholders:

    • Replace YOUR_PLIVO_AUTH_ID and YOUR_PLIVO_AUTH_TOKEN with the actual values from your Plivo Console.
    • Replace +1XXXXXXXXXX with your SMS-enabled Plivo phone number (in E.164 format) or your registered Alphanumeric Sender ID.

Environment Variable Explanation:

  • PLIVO_AUTH_ID: Your unique Plivo account identifier. How to obtain: Log in to the Plivo Console – it's displayed prominently on the dashboard.
  • PLIVO_AUTH_TOKEN: Your secret token for API authentication. How to obtain: Log in to the Plivo Console – it's displayed below the Auth ID. Treat this like a password.
  • PLIVO_SENDER_ID: The identifier ("From" number or Alphanumeric ID) that will appear on the recipient's device. How to obtain/configure: For US/Canada, purchase an SMS-enabled number from Phone Numbers → Buy Numbers in the Plivo Console. For other regions, check Sender ID registration requirements with Plivo support or documentation.
  • PORT: The network port your Express application will listen on. 3000 is a common default for development.

Why use .env? It keeps sensitive credentials out of your source code, making your application more secure and easier to configure for different environments (development, staging, production).

<!-- GAP: Missing information about credential rotation best practices and security implications (Type: Substantive, Priority: Medium) -->

How Do You Implement SMS Sending with Plivo SDK?

3. Implementing Core Functionality (Basic Send)

<!-- DEPTH: Section lacks explanation of async/await pattern benefits and error flow (Priority: Medium) -->

Initialize the Plivo client and create a function to send a single SMS.

  1. Edit app.js: Open app.js and add the following code to set up Express, load environment variables, initialize the Plivo client, and define your API endpoints.

    javascript
    // app.js
    'use strict'; // Enforces stricter parsing and error handling
    
    const express = require('express');
    const plivo = require('plivo');
    require('dotenv').config(); // Load environment variables from .env file
    
    // Validate essential environment variables
    if (!process.env.PLIVO_AUTH_ID || !process.env.PLIVO_AUTH_TOKEN || !process.env.PLIVO_SENDER_ID) {
        console.error("Error: Plivo credentials or sender ID missing in .env file.");
        console.error("Please ensure PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_SENDER_ID are set.");
        process.exit(1); // Exit if critical configuration is missing
    }
    
    const app = express();
    app.use(express.json()); // Middleware to parse JSON request bodies
    
    const port = process.env.PORT || 3000;
    
    // Initialize Plivo client
    let client;
    try {
        client = new plivo.Client(process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN);
        console.log("Plivo client initialized successfully.");
    } catch (error) {
        console.error("Failed to initialize Plivo client:", error);
        process.exit(1); // Exit if client cannot be initialized
    }
    
    
    // --- API Endpoints ---
    
    /**
     * @route POST /send-sms
     * @description Sends a single SMS message.
     * @body {string} to - Destination phone number in E.164 format (e.g., +14155551234).
     * @body {string} text - The message content. Max 160 GSM characters or 70 Unicode characters per segment.
     */
    app.post('/send-sms', async (req, res) => {
        const { to, text } = req.body;
        const src = process.env.PLIVO_SENDER_ID;
    
        // --- Input Validation ---
        if (!to || !text) {
            console.error("Validation Error: 'to' and 'text' fields are required.");
            return res.status(400).json({ error: "'to' and 'text' fields are required." });
        }
    
        // Basic E.164 format check (starts with '+', followed by digits)
        // For production, consider more robust validation using libraries like 'joi' or 'express-validator'.
        if (!/^\+\d+$/.test(to)) {
             console.error(`Validation Error: Invalid 'to' number format: ${to}. Must be E.164 format.`);
             return res.status(400).json({ error: "Invalid 'to' number format. Must start with '+' followed by digits (E.164 format)." });
        }
        // Basic text length check (optional, Plivo handles segmentation)
        if (text.length === 0) {
             console.error(`Validation Error: 'text' field cannot be empty.`);
             return res.status(400).json({ error: "'text' field cannot be empty." });
        }
    
        console.log(`Attempting to send SMS from ${src} to ${to}`);
    
        try {
            const response = await client.messages.create(
                src, // Sender ID from .env
                to,  // Destination number from request body
                text // Message text from request body
            );
    
            console.log("Plivo API Response:", response);
            // Plivo's API usually returns a 202 Accepted on success
            // The actual delivery status comes via webhooks (if configured)
            res.status(202).json({
                message: "SMS request accepted by Plivo.",
                message_uuid: response.messageUuid // Plivo's unique ID for the message request
            });
    
        } catch (error) {
            // Log detailed error information internally
            console.error(`[Plivo API Error] Failed sending SMS to ${to}. Status: ${error.statusCode || 'N/A'}. Message: ${error.message}. Details:`, error);
    
            // Send a standardized error response to the client
            res.status(error.statusCode || 500).json({
                error: "SMS sending failed.",
                // Optionally include a reference ID for correlation in production
                // error_reference: Date.now() // Simple example reference
            });
        }
    });
    
    /**
     * @route GET /health
     * @description Health check endpoint.
     */
    app.get('/health', (req, res) => {
        // Basic health check: checks if the server is running
        // More advanced checks could verify DB connection, Plivo connectivity (with caution) etc.
        res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() });
    });
    
    
    // --- Start the server ---
    // Check if the module is run directly
    if (require.main === module) {
        app.listen(port, () => {
            console.log(`Server running on http://localhost:${port}`);
        });
    }
    
    module.exports = app; // Export for testing
<!-- GAP: Missing explanation of response.messageUuid usage for tracking and webhook correlation (Type: Substantive, Priority: Medium) --> *Code Explanation:* * `'use strict';`: Enables strict mode. * `require('dotenv').config();`: Loads variables from `.env`. Must run early. * Environment Variable Check: Ensures critical Plivo config is present. * `express()`: Creates an Express app. * `app.use(express.json())`: Middleware for parsing JSON request bodies (`req.body`). * `plivo.Client(…)`: Initializes the Plivo SDK client. Wrapped in `try…catch` for robustness. * `/send-sms` Endpoint: Handles POST requests to send messages. Includes input validation and Plivo API call within a `try…catch` block. * `/health` Endpoint: Provides a simple GET endpoint for basic health monitoring. * `app.listen(…)`: Starts the server. Conditional check `require.main === module` prevents the server from auto-starting when the file is imported for testing. * `module.exports = app;`: Exports the app instance for use in test files.

2. Run the Server:

```bash node app.js ``` You should see `Plivo client initialized successfully.` and `Server running on http://localhost:3000`. Press `Ctrl+C` to stop.

How Do You Build and Test the Plivo SMS API?

4. Building the API Layer

This section is now integrated into Section 3 where the app.js code, including the /send-sms and /health endpoints, is presented.

Testing the Endpoints:

<!-- DEPTH: Testing section lacks explanation of what success/failure responses mean and next steps (Priority: Medium) -->
  1. Start the server: node app.js

  2. Test /send-sms:

    • Open a new terminal window. Use curl or a tool like Postman.
    • Replace +1RECIPIENTNUMBER with a real phone number (verified in your Plivo sandbox if using a trial account).
    bash
    curl -X POST http://localhost:3000/send-sms \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+1RECIPIENTNUMBER",
      "text": "Hello from Node.js and Plivo!"
    }'

    Expected Success Output (HTTP 202):

    json
    {
      "message": "SMS request accepted by Plivo.",
      "message_uuid": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]
    }

    Expected Validation Error (HTTP 400): (e.g., missing "to")

    json
    {
      "error": "'to' and 'text' fields are required."
    }

    Expected Plivo Error (HTTP 500 or Plivo status): (e.g., bad credentials)

    json
    {
      "error": "SMS sending failed."
    }
  3. Test /health:

    bash
    curl http://localhost:3000/health

    Expected Output (HTTP 200):

    json
    {
      "status": "UP",
      "timestamp": "YYYY-MM-DDTHH:mm:ss.sssZ"
    }
<!-- GAP: Missing troubleshooting steps for common testing failures (Type: Substantive, Priority: High) -->

What Are the Plivo Integration Details You Need to Know?

5. Integrating with Plivo (Details)

<!-- DEPTH: Integration details lack specific examples of handling different response scenarios (Priority: Medium) -->

You've already initialized the client and used messages.create in app.js. Key points:

  • Authentication: Handled by new plivo.Client(process.env.PLIVO_AUTH_ID, process.env.PLIVO_AUTH_TOKEN). Ensure .env values are correct. Find credentials on the Plivo Console Dashboard.
  • Sender ID (src): Set via PLIVO_SENDER_ID in .env. Must be a valid Plivo number (US/Canada) or potentially a registered Alphanumeric Sender ID. Manage numbers in the Phone Numbers section of the Plivo Console.
  • API Keys/Secrets: Handled via PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN. Use .env locally and secure environment variables in production. Never commit credentials.
  • Fallback Mechanisms: The current example lacks automatic fallback. For production:
    • Consider retrying failed requests (see Section 6 concept).
    • Use multiple Plivo numbers or alternative providers for high availability (complex).
    • Monitor Plivo's status: status.plivo.com.
<!-- GAP: Missing detailed explanation of Plivo message states and lifecycle (Type: Substantive, Priority: High) -->

How Do You Implement Error Handling for Plivo SMS?

6. Implementing Error Handling and Logging

Our app.js includes basic try...catch blocks and improved console.error logging.

Note on Express 5.x: If using Express 5.x (released October 2024, requires Node.js 18+), async middleware can return rejected promises that are automatically caught by the router as errors, eliminating the need for manual try-catch blocks in many cases. This guide uses Express 4.x syntax for broader compatibility.

Retry Mechanisms (Conceptual Enhancement):

<!-- DEPTH: Retry mechanism example lacks guidance on when NOT to retry and cost implications (Priority: High) -->

Plivo requests might fail transiently. Implementing retries can improve reliability. Note: The following is a conceptual example to illustrate the pattern. It is not integrated into the main app.js code provided in Section 3. Implementing this would require modifying the /send-sms handler to use this function instead of calling client.messages.create directly.

Conceptual Example Function:

javascript
async function sendSmsWithRetry(src, to, text, retries = 3, delay = 1000) {
    try {
        // Assume 'client' is the initialized Plivo client available in this scope
        return await client.messages.create(src, to, text);
    } catch (error) {
        // Only retry on specific error types (e.g., 5xx server errors, maybe 429 rate limit)
        // Avoid retrying permanent errors like 400 Bad Request or 401 Unauthorized.
        const shouldRetry = retries > 0 && (error.statusCode >= 500 || error.statusCode === 429);

        if (shouldRetry) {
            console.warn(`Retrying SMS to ${to}. Retries left: ${retries - 1}. Delay: ${delay}ms. Error: ${error.message}`);
            await new Promise(resolve => setTimeout(resolve, delay));
            // Exponential backoff: double the delay for next retry
            return sendSmsWithRetry(src, to, text, retries - 1, delay * 2);
        } else {
            // Don't retry permanent errors or if retries exhausted
            console.error(`SMS to ${to} failed permanently or retries exhausted. Error: ${error.message}`);
            throw error; // Re-throw the error to be caught by the main handler
        }
    }
}

// To use this in the app.post('/send-sms', ...) handler, you would replace:
// const response = await client.messages.create(src, to, text);
// With:
// const response = await sendSmsWithRetry(src, to, text);

Why Retry? Handles temporary network or service issues. Exponential backoff prevents overwhelming the API during outages. Caution: Carefully choose which errors to retry.

Testing Error Scenarios:

<!-- DEPTH: Error testing scenarios lack expected outcomes and how to interpret them (Priority: Medium) -->
  • Provide invalid credentials in .env.
  • Send requests with invalid to number format (e.g., ""12345"").
  • Use an invalid/non-existent Plivo src number in .env.
  • Send to an unverified number using a trial account.
  • Temporarily disconnect internet to simulate network errors.
  • Send requests rapidly to test potential rate limits.

What Security Features Should You Add to Your SMS API?

7. Adding Security Features

Protecting your API is crucial.

<!-- DEPTH: Security section lacks threat modeling and real-world attack scenarios (Priority: High) -->
  1. Input Validation and Sanitization:

    • Our app.js includes basic checks for to (presence, format) and text (presence).
    • Enhancement: For production, use dedicated validation libraries like joi or express-validator to define schemas for req.body, check data types, enforce length limits, and potentially sanitize inputs more rigorously. Example mention added in app.js comments.
    • Check text length if specific limits are desired beyond Plivo's segmentation.
  2. Authentication/Authorization for Your API (Conceptual Enhancement):

    • The /send-sms endpoint in our base app.js is currently open. This is insecure for production.

    • Recommendation: Implement API Key Authentication (or other methods like JWT/OAuth).

    • Conceptual Example: The following shows how you could add API key middleware. This is not integrated into the main app.js example but demonstrates the principle.

      • Add a secret key to your .env: INTERNAL_API_KEY=your_secret_key_here
      • Conceptual Middleware Function:
      javascript
      // ---- Conceptual API Key Middleware (Not applied in base app.js) ----
      const API_KEY = process.env.INTERNAL_API_KEY;
      
      function authenticateApiKey(req, res, next) {
          if (!API_KEY) {
              console.error("INTERNAL_API_KEY not set. Cannot enforce API key authentication.");
              // Fail closed: If key isn't configured, deny access.
              return res.status(500).json({ error: 'Server configuration error' });
          }
      
          const providedKey = req.headers['x-api-key'];
          if (!providedKey || providedKey !== API_KEY) {
              console.warn(`Authentication failed. Invalid or missing API key.`);
              // Use 403 Forbidden if the key is missing/wrong, 401 often implies possibility of authenticating
              return res.status(403).json({ error: 'Forbidden: Invalid API Key' });
          }
          next(); // Key is valid, proceed
      }
      
      // ---- How to Apply (Example - Do not add this directly to base app.js unless implementing) ----
      // To protect the /send-sms route, you would add this line *before* the route definition:
      // app.use('/send-sms', authenticateApiKey);
      // Or apply it globally (carefully):
      // app.use(authenticateApiKey); // Applies to all routes defined after this line
      // ---- End Conceptual Example ----
      • Clients would need to include the header X-API-Key: your_secret_key_here in their requests.
<!-- GAP: Missing discussion of CORS configuration for browser-based clients (Type: Substantive, Priority: Medium) -->
  1. Rate Limiting (Conceptual Enhancement):

    • Prevent abuse and control costs.
    • Plivo Account Rate Limits (Official Specifications):
      • Default SMS Outbound MPS: 5 messages per second (MPS) at signup
      • Default MMS Outbound MPS: 0.25 messages per second at signup
      • API Concurrency Limit: 100 simultaneous requests (default)
      • Message Queueing: Messages exceeding MPS limits are queued, not dropped. Each Plivo source number maintains a dedicated queue.
      • Increasing Capacity: Distribute traffic across multiple Plivo numbers or contact support for higher MPS (additional fees apply)
      • Check Your Limits: View your allocated MPS in the Plivo Console under Account -> SMS Overview
    • Recommendation: Use libraries like express-rate-limit.
    • Conceptual Example: This shows how to set up rate limiting. It is not integrated into the main app.js example.
    bash
    npm install express-rate-limit
    javascript
    // ---- Conceptual Rate Limiting Setup (Not applied in base app.js) ----
    // Place near the top of app.js
    const rateLimit = require('express-rate-limit');
    
    const smsLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 100, // Limit each IP to 100 requests per `windowMs`
        message: { error: 'Too many SMS requests created from this IP, please try again after 15 minutes' },
        standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
        legacyHeaders: false, // Disable the `X-RateLimit-*` headers
    });
    
    // ---- How to Apply (Example - Do not add this directly to base app.js unless implementing) ----
    // Apply the limiter to the SMS route:
    // app.use('/send-sms', smsLimiter);
    // ---- End Conceptual Example ----

    Why Rate Limit? Protects against DoS attacks and cost overruns.

  2. HTTPS: Always use HTTPS in production (typically handled by load balancers, reverse proxies like Nginx, or hosting platforms) to encrypt data in transit.

How Do You Handle Special Cases in SMS Messaging?

8. Handling Special Cases (Briefly)

<!-- DEPTH: Special cases section superficial - lacks concrete examples and cost implications (Priority: High) -->
  • Character Encoding (GSM vs. Unicode): Plivo auto-detects. GSM-7 (standard text) = up to 160 chars/segment. Unicode (emojis, special chars) = up to 70 chars/segment. Unicode segments cost the same but fit less text. See Plivo's Encoding Guide.
  • Long Messages: Plivo handles multipart messages automatically. Each part is billed separately.
  • Internationalization: Use E.164 format (+countrycode...). Sender ID rules vary greatly by country (check Plivo docs/regulations).
  • Time Zones: API uses server time (often UTC). Plivo logs are typically UTC. Delivery depends on carriers.
<!-- GAP: Missing information about DND (Do Not Disturb) registries and compliance requirements (Type: Critical, Priority: High) -->

What Performance Optimizations Should You Consider?

9. Performance Optimizations (Considerations)

<!-- DEPTH: Performance section lacks benchmarking guidelines and specific metrics to monitor (Priority: Medium) -->
  • Asynchronous Operations: Node.js + async/await is efficient.

  • Connection Pooling (SDK): Plivo SDK likely handles this.

  • Caching: Not typically applicable for sending unique SMS.

  • Load Testing: Use tools (k6, artillery, ab) to test /send-sms under load. Monitor server resources (CPU, memory) and response times. Example ab command:

    bash
    # Create payload.json: {""to"": ""+1..."", ""text"": ""Load test""}
    # Send 100 requests, 10 concurrently
    ab -n 100 -c 10 -p payload.json -T application/json http://localhost:3000/send-sms
<!-- EXPAND: Could benefit from discussion of horizontal scaling and queue-based architectures (Priority: Medium) -->

How Do You Monitor Your Plivo SMS Application?

10. Monitoring and Observability (Considerations)

<!-- DEPTH: Monitoring section lacks specific alerting thresholds and incident response procedures (Priority: Medium) -->
  • Health Checks: The /health endpoint (added in Section 3) provides a basic check. Monitoring services can use it.
  • Logging: Use structured logging (e.g., Winston, Pino) and centralize logs (Datadog, Splunk, ELK) in production. Include correlation IDs.
  • Error Tracking: Services like Sentry or Datadog APM capture and alert on errors.
  • Metrics: Monitor request rate, error rate, latency (API endpoint, Plivo API), Node.js process metrics.
  • Plivo Dashboard: Check Plivo Console Messages Logs for delivery status and costs.
  • Plivo Webhooks (Status Callbacks): Configure a Message Request URL in Plivo for real-time status updates (requires building an endpoint to receive POSTs from Plivo).
<!-- GAP: Missing detailed webhook implementation example with signature verification (Type: Substantive, Priority: High) -->

What Are Common Plivo SMS Issues and Solutions?

11. Troubleshooting and Caveats

<!-- DEPTH: Troubleshooting section good but lacks step-by-step diagnostic procedures (Priority: Medium) -->
  • Common Errors & Solutions:
    • 401 Unauthorized from Plivo: Incorrect PLIVO_AUTH_ID or PLIVO_AUTH_TOKEN in .env.
    • 400 Bad Request (Invalid dst): Ensure to is valid E.164 format (+1...).
    • 400 Bad Request (Invalid src): Ensure PLIVO_SENDER_ID is a valid, SMS-enabled Plivo number or registered ID.
    • 402 Payment Required: Insufficient Plivo credits.
    • Message Failed/Undelivered: Invalid destination, number blocked, carrier issues. Check Plivo logs. Trial accounts limited to verified numbers.
    • SMS Not Received (Plivo shows 'Delivered'): Device signal, number correct?, blocked sender?, carrier filtering?
    • ECONNREFUSED/Network Errors: Server can't reach Plivo API. Check connectivity, firewalls, Plivo status (status.plivo.com).
  • Platform-Specific Limitations:
    • Trial Accounts: Send only to verified sandbox numbers. Messages prefixed. Limited credits.
    • Sender ID Restrictions: Country-dependent (US/Canada require Plivo numbers). Check Plivo guidelines.
  • Version Compatibility: Use Node.js 18+ (Node.js 22 LTS recommended as of January 2025). Node.js 18.x reaches end-of-life April 30, 2025. Keep plivo SDK updated (npm update plivo). Check github.com/plivo/plivo-node for releases.
  • Rate Limits: Be aware of Plivo account limits (default: 5 MPS for SMS, 0.25 MPS for MMS, 100 concurrent API requests). Implement app-level rate limiting (Section 7 concept) and potential retries (Section 6 concept).
  • Encoding & Character Limits: GSM (160) vs. Unicode (70) impacts segment count/cost.
<!-- EXPAND: Could benefit from flowchart for systematic troubleshooting approach (Priority: Low) -->

How Do You Deploy a Plivo SMS Application to Production?

12. Deployment and CI/CD

<!-- DEPTH: Deployment section lacks platform-specific configuration examples (Priority: High) -->
  • Deployment Environments: Heroku, AWS (EC2, Fargate, Lambda), DigitalOcean, etc.
  • Key Principles:
    • Environment Variables: Use platform's mechanism for PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_SENDER_ID, PORT, INTERNAL_API_KEY (if used), etc. NEVER hardcode.
    • NODE_ENV=production: Set this env var for performance.
    • Process Manager: Use pm2, systemd, Docker, or platform's manager to keep the app running. (pm2 start app.js --name plivo-sms)
    • Build Step: Include if using TypeScript, etc.
  • CI/CD Pipeline (Conceptual):
    1. Trigger (e.g., push to main).
    2. Checkout code.
    3. Install dependencies (npm ci).
    4. Lint/Test (npm test). Fail pipeline on errors.
    5. Build (if needed).
    6. Deploy to platform.
    7. Restart application.
  • Rollback: Have a plan to revert to a previous working version.
<!-- GAP: Missing discussion of blue-green deployment or canary releases for zero-downtime (Type: Enhancement, Priority: Low) -->

How Do You Verify and Test Your Plivo Integration?

13. Verification and Testing

  • Manual Verification Checklist:
    • Deploy to staging.
    • Set environment variables correctly.
    • Send test request via curl/Postman to /send-sms.
    • Verify 202 Accepted response and message_uuid.
    • Check Plivo Console Logs for message status (Queued -> Sent -> Delivered).
    • Confirm SMS received on test phone.
    • Test /health endpoint -> verify 200 OK and status UP.
    • Test invalid input (to/text missing/invalid format) -> verify 400 Bad Request.
    • (If implemented) Test invalid/missing API Key -> verify 403 Forbidden.
    • (If implemented) Test rate limiting -> verify 429 Too Many Requests.
    • Check application logs for errors.
<!-- DEPTH: Testing section has good automated tests but lacks integration test examples (Priority: Medium) -->
  • Automated Testing (Examples - using Jest):

    • Install Jest: npm install --save-dev jest supertest
    • Create a test file (e.g., app.test.js):
    javascript
    // app.test.js
    const request = require('supertest');
    const app = require('./app'); // Import your express app
    
    // Mock the Plivo client to avoid actual API calls/costs during tests
    // Mock implementation simulates a successful API call by default
    const mockCreate = jest.fn().mockResolvedValue({
        message: [""SMS request queued for delivery""],
        messageUuid: [""mock-uuid-"" + Date.now()],
        apiId: ""mock-api-id""
    });
    jest.mock('plivo', () => ({
        Client: jest.fn().mockImplementation(() => ({
            messages: {
                create: mockCreate // Use the mock function
            }
        }))
    }));
    
    // Load .env for consistency, but tests might override or rely on mocks
    require('dotenv').config();
    
    describe('SMS API Endpoints', () => {
    
        // Clear mocks before each test to ensure isolation
        beforeEach(() => {
            mockCreate.mockClear();
            // Reset to default success behavior if needed after failure tests
             mockCreate.mockResolvedValue({
                message: [""SMS request queued for delivery""],
                messageUuid: [""mock-uuid-"" + Date.now()],
                apiId: ""mock-api-id""
            });
        });
    
        // --- Test Suite for POST /send-sms ---
        describe('POST /send-sms', () => {
            const validPayload = {
                to: '+15551234567',
                text: 'Test message'
            };
    
            it('should return 400 Bad Request if ""to"" is missing', async () => {
                const res = await request(app)
                    .post('/send-sms')
                    .send({ text: 'Missing to field' }); // Missing 'to'
                expect(res.statusCode).toEqual(400);
                expect(res.body).toHaveProperty('error', ""'to' and 'text' fields are required."");
                expect(mockCreate).not.toHaveBeenCalled(); // Plivo should not be called
            });
    
             it('should return 400 Bad Request if ""text"" is missing', async () => {
                const res = await request(app)
                    .post('/send-sms')
                    .send({ to: '+15551234567' }); // Missing 'text'
                expect(res.statusCode).toEqual(400);
                expect(res.body).toHaveProperty('error', ""'to' and 'text' fields are required."");
                 expect(mockCreate).not.toHaveBeenCalled();
            });
    
             it('should return 400 Bad Request for invalid ""to"" format', async () => {
                const res = await request(app)
                    .post('/send-sms')
                    .send({ to: '1234567890', text: 'Invalid number format' }); // Invalid 'to'
                expect(res.statusCode).toEqual(400);
                expect(res.body).toHaveProperty('error', expect.stringContaining(""Invalid 'to' number format""));
                 expect(mockCreate).not.toHaveBeenCalled();
            });
    
            it('should return 202 Accepted and message_uuid on valid request', async () => {
                const res = await request(app)
                    .post('/send-sms')
                    .send(validPayload); // Valid payload
                expect(res.statusCode).toEqual(202);
                expect(res.body).toHaveProperty('message', 'SMS request accepted by Plivo.');
                expect(res.body).toHaveProperty('message_uuid');
                expect(mockCreate).toHaveBeenCalledTimes(1);
                expect(mockCreate).toHaveBeenCalledWith(
                    process.env.PLIVO_SENDER_ID, // Check if called with correct args
                    validPayload.to,
                    validPayload.text
                );
            });
    
            it('should return 500 if Plivo API call fails', async () => {
                // Simulate Plivo SDK throwing an error
                const plivoError = new Error(""Plivo simulated error"");
                plivoError.statusCode = 503; // Example Plivo error status
                mockCreate.mockRejectedValueOnce(plivoError); // Make the mock throw an error
    
                const res = await request(app)
                    .post('/send-sms')
                    .send(validPayload);
    
                expect(res.statusCode).toEqual(503); // Should reflect Plivo's error code if available
                expect(res.body).toHaveProperty('error', 'SMS sending failed.');
                expect(mockCreate).toHaveBeenCalledTimes(1); // Ensure it was called
            });
        });
    
        // --- Test Suite for GET /health ---
        describe('GET /health', () => {
            it('should return 200 OK and status UP', async () => {
                const res = await request(app).get('/health');
                expect(res.statusCode).toEqual(200);
                expect(res.body).toHaveProperty('status', 'UP');
                expect(res.body).toHaveProperty('timestamp');
            });
        });
    });
<!-- GAP: Missing contract testing examples for API consumers (Type: Enhancement, Priority: Low) -->

Frequently Asked Questions About Plivo SMS with Node.js

What Node.js version is required for Plivo integration?

You need Node.js 18 or higher for modern Plivo implementations. As of January 2025, Node.js 22 is the current LTS version recommended for production applications. Node.js 18.x reaches end-of-life on April 30, 2025, so plan to upgrade to Node.js 20 or 22 for continued support and security updates.

What are the Plivo SMS rate limits?

Plivo assigns default rate limits at signup: 5 messages per second (MPS) for SMS and 0.25 MPS for MMS. The API supports up to 100 simultaneous requests by default. Messages exceeding these limits are queued, not dropped. You can increase capacity by distributing traffic across multiple Plivo numbers or contacting support for higher MPS allocations (additional fees apply).

How do you secure a Plivo SMS API endpoint?

Implement multiple security layers: use API key authentication via custom headers (e.g., X-API-Key), add rate limiting with express-rate-limit to prevent abuse, validate all input data using libraries like joi or express-validator, always use HTTPS in production, and store credentials in environment variables never in code. For production, consider implementing JWT tokens or OAuth 2.0 for more robust authentication.

What is E.164 phone number format for Plivo?

E.164 format is the international phone number standard required by Plivo: it starts with + followed by the country code and subscriber number with no spaces or special characters. Examples: +14155551234 (US), +442071234567 (UK), +91981234567 (India). All phone numbers in Plivo API calls must use this format for both source and destination numbers.

Does Plivo support sending SMS internationally?

Yes, Plivo supports international SMS to 190+ countries, but sender ID requirements vary significantly by country. US and Canada require Plivo phone numbers purchased through the console. Other countries may accept Alphanumeric Sender IDs with registration. Check Plivo's country-specific documentation for sender ID requirements, pricing, and any regulatory restrictions before sending to international destinations.

How do you handle SMS delivery failures with Plivo?

Implement robust error handling: catch Plivo API errors in try-catch blocks, log detailed error information including status codes, implement retry logic with exponential backoff for transient failures (5xx errors, 429 rate limits), configure webhook endpoints to receive delivery status callbacks, store message status in a database for tracking, and monitor common errors like 401 (invalid credentials), 402 (insufficient credits), and 400 (invalid parameters).

What is the difference between Express 4.x and Express 5.x for Plivo?

Express 5.x (released October 2024) requires Node.js 18+ and includes built-in async error handling where rejected promises are automatically caught by the router, eliminating many manual try-catch blocks. Express 4.x requires explicit error handling in async routes. Both versions work with Plivo SDK, but Express 5.x provides cleaner async/await syntax and improved security through updated dependencies.

How much does it cost to send SMS with Plivo?

Plivo SMS pricing varies by destination country and typically ranges from $0.0035 to $0.10 per message segment. US SMS costs approximately $0.0079 per segment. Messages over 160 GSM characters or 70 Unicode characters are split into multiple segments, each billed separately. MMS messages cost more than SMS. Check your Plivo Console dashboard for real-time pricing for specific countries and monitor your usage to control costs.

<!-- DEPTH: FAQ section good but could benefit from more advanced topics (Priority: Low) -->

Can you send SMS from a trial Plivo account?

Yes, but with limitations: trial accounts can only send SMS to phone numbers verified in your Plivo Console (Phone Numbers → Sandbox Numbers). Messages are prefixed with a trial account notice. You have limited free credits. To send to any number, upgrade to a paid account by adding funds to your Plivo account. Trial accounts are perfect for development and testing before production deployment.

How do you implement webhook callbacks for Plivo SMS delivery status?

Configure a webhook URL in your Plivo Console (Messaging → Applications) pointing to a publicly accessible endpoint in your application (e.g., /plivo/status). Create a route that accepts POST requests from Plivo containing delivery receipt data. Validate webhook signatures using HMAC SHA1 with your auth token to ensure requests come from Plivo. Parse the status (delivered, failed, undelivered) and update your database records accordingly. Use services like ngrok for local development testing.

<!-- GAP: Missing complete webhook implementation code example (Type: Substantive, Priority: High) -->

Frequently Asked Questions

How to send SMS with Node.js and Express?

Use the Plivo Node.js SDK and Express.js to create an API endpoint that handles sending SMS messages. This setup allows you to integrate SMS functionality directly into your Node.js applications. The provided `app.js` example demonstrates the implementation with basic input validation and error handling.

What is Plivo used for in Node.js SMS?

Plivo is a cloud communications platform that provides APIs for sending SMS messages, making calls, and other communication services. Its Node.js SDK simplifies interaction with the Plivo API, allowing developers to easily integrate communication features into their applications.

Why use dotenv for Plivo credentials?

Dotenv securely manages environment variables, storing sensitive information like Plivo API keys outside your source code. This enhances security by preventing accidental exposure of credentials in version control.

How to set up a Plivo SMS project in Node.js?

First, install Node.js and npm, create a project directory (`mkdir plivo-sms-sender && cd $_`), then initialize your project (`npm init -y`). Install Express, Plivo, and dotenv (`npm install express plivo dotenv`).

How to get Plivo Auth ID and Auth Token?

Your Plivo Auth ID and Auth Token are found on your Plivo Console dashboard after you log in to your Plivo account. Treat your Auth Token like a password, keeping it confidential.

What is PLIVO_SENDER_ID, and how do I set it?

PLIVO_SENDER_ID is the 'From' number or Alphanumeric Sender ID that recipients see. For US/Canada, it must be a Plivo phone number. Obtain one via the Plivo Console (Phone Numbers -> Buy Numbers). For other countries, consult Plivo documentation for Sender ID registration.

Why are Node.js and Express good for sending SMS?

Node.js excels at building scalable, non-blocking servers, making it ideal for handling asynchronous communication like sending SMS. Express.js, a lightweight Node.js framework, simplifies building robust API endpoints for sending messages.

How to structure a Node.js Express Plivo project?

The article recommends creating an `app.js` file for your main application logic, a `.env` file for environment variables, and a `.gitignore` file to exclude `node_modules` and `.env` from version control.

How to test my Plivo SMS API endpoint?

Start your server with `node app.js`, and then use tools like `curl` or Postman to send POST requests to `http://localhost:3000/send-sms`. Include your destination number and message text in JSON format in the request body.

What does a 202 Accepted response from Plivo mean?

A 202 response indicates that Plivo has accepted your SMS request for processing but doesn't guarantee immediate delivery. Actual delivery status is confirmed later via webhooks (if configured) or by checking Plivo message logs.

How to handle Plivo SMS API errors in Node.js?

Use `try...catch` blocks around your Plivo API calls to handle potential errors. The example code provides basic error handling and logging to console. Robust error handling should log detailed information, include correlation IDs, and potentially implement retries.

When should I implement SMS retry mechanisms with Plivo?

Retry mechanisms improve reliability by resending messages if transient errors occur (e.g., server-side errors or rate limiting). Implement retries with exponential backoff and avoid retrying on permanent errors like 400 Bad Request or 401 Unauthorized.

How can I improve the security of my Plivo SMS application?

Crucial steps include robust input validation (using libraries like `joi`), implementing API key authentication for your endpoints, using rate limiting to prevent abuse, and ensuring HTTPS is used in production.

What are some common Plivo SMS API error codes?

Common errors include `401 Unauthorized` (bad credentials), `400 Bad Request` (invalid input), `402 Payment Required`, and message delivery failures. The troubleshooting section in the article provides further guidance.

How to monitor Plivo SMS performance and status?

Monitor your server resources (CPU, memory), response times, and error rates. Use the Plivo console to check message logs and costs. Utilize Plivo webhooks for real-time delivery updates, implement health checks, and centralized logging.