code examples

Sent logo
Sent TeamMar 8, 2026 / code examples / Plivo

How to Integrate WhatsApp Business API with Next.js Using Plivo (2025 Guide)

Step-by-step tutorial: Integrate Plivo's WhatsApp Business API into Next.js. Send/receive messages, handle webhooks, and build production-ready WhatsApp messaging in your Node.js app.

WhatsApp Integration with Plivo & Next.js: Complete Guide

This comprehensive guide walks you through integrating Plivo's WhatsApp Business API into your Next.js application. You'll learn how to build a production-ready solution for sending and receiving WhatsApp messages, handling webhooks securely, and managing message templates effectively.

We'll cover everything from initial project setup to webhook configuration, enabling you to leverage WhatsApp for customer communication, notifications, or interactive services directly within your Next.js app. By the end, you'll have a functional WhatsApp messaging application ready for production use cases.

Project Overview and Goals

What We're Building:

A Next.js application with API routes that can:

  1. Send WhatsApp Messages: Trigger outgoing messages (text, media, templates, interactive) via Plivo's API through a secure backend endpoint.
  2. Receive WhatsApp Messages: Handle incoming messages and status updates from Plivo via a secure webhook endpoint.

Problem Solved:

This integration enables direct, programmatic communication with users on WhatsApp, bypassing the need for manual messaging or less integrated solutions. It's ideal for applications requiring timely notifications, customer support interactions, or automated conversational flows over WhatsApp.

Technologies Used:

  • Next.js: A React framework providing server-side rendering, API routes, and a streamlined developer experience (v15.x as of 2025, with App Router requiring React 19). Chosen for its integrated backend capabilities (API routes) and popularity in modern web development.
  • Node.js: The underlying runtime for Next.js and the Plivo SDK.
  • Plivo Node.js SDK: Simplifies interaction with the Plivo REST API for sending messages and handling communication logic.
  • Plivo WhatsApp Business API: The service providing the connection to the WhatsApp network.
  • (Optional) ngrok: For exposing local development servers to the internet to test webhooks.

System Architecture:

mermaid
graph LR
    A[User/Client App] -- HTTP Request --> B(Next.js Frontend);
    B -- API Call --> C{Next.js API Route (/api/send-whatsapp)};
    C -- Plivo SDK --> D[Plivo API];
    D -- WhatsApp Network --> E[End User WhatsApp];

    E -- Sends Message --> D;
    D -- Webhook POST --> F{Next.js API Route (/api/plivo-webhook)};
    F -- Process/Log --> G[(Optional) Database/Logging Service];
    F -- 200 OK --> D;

    style C fill:#f9f,stroke:#333,stroke-width:2px
    style F fill:#f9f,stroke:#333,stroke-width:2px

Prerequisites:

  • Node.js (v20 "Iron" or v22 "Jod" recommended – current LTS versions as of 2025. Production applications should use Active LTS or Maintenance LTS releases)
  • npm or yarn package manager
  • A Plivo account with Auth ID and Auth Token.
  • A WhatsApp Business Account (WABA) and a Plivo-approved WhatsApp Sender number configured in your Plivo console.
  • Basic understanding of Next.js, APIs, and JavaScript.
  • (Optional) ngrok installed for local webhook testing.

Final Outcome:

A Next.js application with two API endpoints: one to trigger outgoing WhatsApp messages and another to receive incoming messages/statuses from Plivo. The setup will use environment variables for security and include basic error handling.


1. Setting Up Your Next.js WhatsApp Project

Let's initialize a new Next.js project and install the necessary dependencies for WhatsApp messaging.

  1. Create a Next.js App: Open your terminal and run the following command. Choose your preferred settings when prompted (we'll use TypeScript: No, ESLint: Yes, Tailwind CSS: No, src/ directory: No, App Router: Yes, customize import alias: No for this guide).

    bash
    npx create-next-app@latest plivo-nextjs-whatsapp
  2. Navigate to Project Directory:

    bash
    cd plivo-nextjs-whatsapp
  3. Install Plivo Node.js SDK: Add the Plivo SDK to your project dependencies.

    bash
    npm install plivo
  4. Set Up Environment Variables: Create a file named .env.local in the root of your project. This file is gitignored by default in Next.js and is the secure place for your credentials. Add your Plivo Auth ID and Auth Token.

    • Find your Auth ID and Auth Token on the Plivo Console dashboard homepage.
    plaintext
    # .env.local
    
    # Plivo Credentials
    PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID
    PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN
    
    # Your Plivo WhatsApp Sender Number (in E.164 format, e.g., +14155552671)
    PLIVO_WHATSAPP_SENDER_NUMBER=+1XXXXXXXXXX
    
    # Optional: A secret token if implementing additional custom webhook checks
    # PLIVO_WEBHOOK_SECRET=your_strong_random_secret_here
    • PLIVO_AUTH_ID / PLIVO_AUTH_TOKEN: Used by the SDK to authenticate API requests to Plivo. Obtainable from the Plivo Console dashboard. This is also used for validating incoming webhooks.
    • PLIVO_WHATSAPP_SENDER_NUMBER: Your Plivo-provisioned WhatsApp number used as the src for outgoing messages. Find this under Messaging -> WhatsApp Senders in the Plivo Console.
    • PLIVO_WEBHOOK_SECRET: (Commented out by default) A secret you define. Plivo doesn't use this directly for its standard signature validation. You might use it if implementing additional custom verification logic beyond Plivo's signature check. Note: Plivo's primary webhook security relies on signature validation using your PLIVO_AUTH_TOKEN, which is covered later.
  5. Project Structure: Your basic structure (using App Router) will look something like this:

    text
    plivo-nextjs-whatsapp/
    ├── app/
    │   ├── api/              # API routes live here
    │   │   ├── send-whatsapp/
    │   │   │   └── route.js
    │   │   └── plivo-webhook/
    │   │       └── route.js
    │   ├── layout.js
    │   └── page.js
    ├── node_modules/
    ├── public/
    ├── .env.local          # Your secret credentials
    ├── .gitignore
    ├── next.config.mjs
    ├── package.json
    └── README.md

    We place our backend logic within the app/api/ directory, following Next.js conventions for API routes.


2. Building the WhatsApp Messaging API Endpoint

We'll create an API route that accepts a request (containing destination number and message content) and uses the Plivo SDK to send a WhatsApp message.

  1. Create the Send API Route: Create the file app/api/send-whatsapp/route.js.

  2. Implement the Sending Logic: Add the following code to app/api/send-whatsapp/route.js.

    javascript
    // app/api/send-whatsapp/route.js
    import { NextResponse } from 'next/server';
    import plivo from 'plivo';
    
    // Ensure environment variables are loaded (Next.js does this automatically)
    const authId = process.env.PLIVO_AUTH_ID;
    const authToken = process.env.PLIVO_AUTH_TOKEN;
    const plivoWhatsappNumber = process.env.PLIVO_WHATSAPP_SENDER_NUMBER;
    
    // Validate essential environment variables
    if (!authId || !authToken || !plivoWhatsappNumber) {
      console.error("Missing Plivo credentials or sender number in environment variables.");
      // In a real app, you might throw an error or handle this differently
      // For now, we'll log and prevent client initialization
    }
    
    let client;
    try {
      // Initialize Plivo client only if credentials are present
      if (authId && authToken) {
          client = new plivo.Client(authId, authToken);
      } else {
          // Throw error if credentials are not available during initialization
          throw new Error("Plivo client cannot be initialized due to missing credentials.");
      }
    } catch (error) {
        console.error("Failed to initialize Plivo client:", error);
        // Handle client initialization failure globally if needed
        // Depending on setup, this might prevent the API route from working correctly
    }
    
    export async function POST(request) {
      if (!client) {
          console.error("Plivo client is not initialized. Check server logs for initialization errors.");
          return NextResponse.json(
              { success: false, error: "Server configuration error: Plivo client not available." },
              { status: 500 }
          );
      }
    
      let requestBody;
      try {
          requestBody = await request.json();
      } catch (error) {
          return NextResponse.json({ success: false, error: "Invalid JSON body" }, { status: 400 });
      }
    
      const { to, message, type = 'text', // Default to text message
              media_urls, template, interactive, location } = requestBody;
    
      // --- Input Validation ---
      if (!to || !to.startsWith('+') || to.length < 10) { // Basic E.164 check
          return NextResponse.json({ success: false, error: "Invalid 'to' phone number format. Use E.164 (e.g., +14155552671)." }, { status: 400 });
      }
    
      if (type === 'text' && !message) {
          return NextResponse.json({ success: false, error: "Missing 'message' field for text message." }, { status: 400 });
      }
      if (type === 'media' && (!media_urls || !Array.isArray(media_urls) || media_urls.length === 0)) {
          return NextResponse.json({ success: false, error: "Missing or invalid 'media_urls' array for media message." }, { status: 400 });
      }
       if (type === 'template' && !template) {
          return NextResponse.json({ success: false, error: "Missing 'template' object for template message." }, { status: 400 });
      }
       if (type === 'interactive' && !interactive) {
          return NextResponse.json({ success: false, error: "Missing 'interactive' object for interactive message." }, { status: 400 });
      }
      if (type === 'location' && !location) {
          return NextResponse.json({ success: false, error: "Missing 'location' object for location message." }, { status: 400 });
      }
      // Add more validation as needed based on message types (e.g., structure of template/interactive objects)
    
      // --- WhatsApp Message Type Requirements ---
      // IMPORTANT: Template messages require prior approval from WhatsApp/Meta through Plivo Console.
      // - Templates are used to INITIATE conversations with users (24-hour window starts upon delivery).
      // - Non-template messages (text, media, interactive, location) can only be sent WITHIN an active
      //   24-hour conversation window (started by either a user message or a delivered template).
      // - Attempting to send non-template messages outside this window will result in API errors.
      // Consult Plivo's WhatsApp Business API documentation for conversation window rules.
    
    
      // --- Construct Plivo Payload ---
      const payload = {
        src: plivoWhatsappNumber,
        dst: to,
        type: 'whatsapp', // Specify WhatsApp channel
        // Optional: Add a URL for delivery status callbacks for *this specific message*
        // url: 'https://yourdomain.com/api/plivo-webhook', // Overrides Plivo Application setting if provided
        // method: 'POST',
      };
    
      // Add type-specific parameters
      switch(type) {
          case 'text':
              payload.text = message;
              break;
          case 'media':
              payload.media_urls = media_urls;
              // Optionally add text caption if needed
              if (message) payload.text = message;
              break;
          case 'template':
              payload.template = template; // Pass the template object directly
              break;
           case 'interactive':
              payload.interactive = interactive; // Pass the interactive object directly
              break;
           case 'location':
              payload.location = location; // Pass the location object directly
              break;
          default:
              return NextResponse.json({ success: false, error: `Unsupported message type: ${type}` }, { status: 400 });
      }
    
    
      // --- Send Message via Plivo ---
      try {
        const response = await client.messages.create(payload);
    
        console.log("Plivo API Response:", response);
    
        // Success
        return NextResponse.json({
          success: true,
          message_uuid: response.messageUuid?.[0], // Plivo returns uuids in an array
          api_id: response.apiId,
          message: response.message, // Confirmation message from Plivo
        });
    
      } catch (error) {
        console.error("Plivo API Error:", error);
    
        // Extract more specific error details if available
        const errorMessage = error.message || "Failed to send message via Plivo.";
        const errorStatus = error.statusCode || 500; // Plivo errors often have a statusCode
    
        return NextResponse.json(
          { success: false, error: errorMessage, details: error.error }, // Include Plivo's error details if present
          { status: errorStatus }
        );
      }
    }

    Explanation:

    • We import NextResponse for API responses and the plivo SDK.
    • We retrieve Plivo credentials and the sender number from environment variables. Crucially, we check if they exist before initializing the client. An error is thrown if initialization fails due to missing credentials.
    • The Plivo client is initialized using the Auth ID and Token. This happens outside the POST handler for potential reuse.
    • The POST function handles incoming requests. It first checks if the client was successfully initialized. It expects a JSON body containing to (destination number) and fields specific to the message type (e.g., message for text, media_urls for media, template object, interactive object, location object).
    • Basic input validation is performed on the to number and required fields based on type.
    • We construct the payload object for the client.messages.create method, setting src, dst, and type: 'whatsapp'.
    • A switch statement adds the correct parameters (text, media_urls, template, etc.) based on the requested type.
    • The client.messages.create(payload) function sends the request to Plivo.
    • We wrap the API call in a try...catch block to handle potential errors from the Plivo API (e.g., invalid number, insufficient funds, API downtime).
    • On success, we return the Plivo message UUID and API ID.
    • On error, we log the error and return a JSON response with the error message and appropriate status code (using Plivo's status code if available).

3. Implementing WhatsApp Webhook Handler for Incoming Messages

Plivo uses webhooks to send your application information about incoming messages or status updates for outgoing messages. We need an API route to receive these webhook POST requests.

  1. Create the Webhook API Route: Create the file app/api/plivo-webhook/route.js.

  2. Implement the Webhook Handler: Add the following code to app/api/plivo-webhook/route.js.

    javascript
    // app/api/plivo-webhook/route.js
    import { NextResponse } from 'next/server';
    import plivo from 'plivo'; // SDK might be needed for validation utility
    import { headers } from 'next/headers'; // To access request headers
    
    // Retrieve Auth Token needed for signature validation
    const authToken = process.env.PLIVO_AUTH_TOKEN;
    // Note: PLIVO_WEBHOOK_SECRET (if defined) is not used for Plivo's standard signature validation.
    
    // --- Plivo Signature Validation Function (CRITICAL FOR SECURITY) ---
    // This function is a placeholder and conceptual example.
    // You MUST replace this with the actual validation logic provided in
    // Plivo's official documentation for Node.js. Failure to implement
    // correct validation leaves your webhook endpoint vulnerable.
    function validatePlivoSignature(request, rawBody) {
        const headerList = headers(); // Get access to headers
        const signature = headerList.get('X-Plivo-Signature-V3');
        const nonce = headerList.get('X-Plivo-Signature-V3-Nonce');
        // Construct the full URL the request was sent to. In Next.js on Vercel,
        // you might need to combine headers like 'x-forwarded-proto', 'x-forwarded-host', and request.nextUrl.pathname.
        // Consult Plivo docs and test carefully. For simplicity here, we use request.url
        // but verify this matches what Plivo expects.
        const url = request.url;
    
        if (!signature || !nonce || !authToken) {
            console.warn("Webhook validation skipped: Missing 'X-Plivo-Signature-V3' header, 'X-Plivo-Signature-V3-Nonce' header, or 'PLIVO_AUTH_TOKEN'.");
            return false;
        }
    
        try {
            // >>> CRITICAL: Replace this section with Plivo's official validation method <<<
            // 1. Check Plivo's Node.js SDK documentation for a validation utility function.
            //    It might look something like:
            //    // return plivo.validateV3Signature(url, nonce, signature, authToken, rawBody); // Check exact params required by SDK!
    
            // 2. If no SDK utility exists, implement the manual steps precisely as documented by Plivo.
            //    This typically involves:
            //    - Concatenating the URL, nonce, and raw request body (order matters!).
            //    - Creating an HMAC SHA-256 hash of the concatenated string using your Auth Token as the key.
            //    - Base64 encoding the hash.
            //    - Performing a timing-safe comparison between the calculated signature and the one in the header.
    
            // Example conceptual placeholder (DO NOT USE IN PRODUCTION):
            // const crypto = require('crypto');
            // const baseString = url + nonce + rawBody; // Verify exact concatenation order from Plivo docs
            // const expectedSignature = crypto.createHmac('sha256', authToken).update(baseString).digest('base64');
            // const isValid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
            // return isValid;
    
            // --- Current Status: Validation Logic Missing ---
            console.error("CRITICAL SECURITY WARNING: Plivo signature validation logic is NOT IMPLEMENTED. Using placeholder. Replace with logic from Plivo documentation IMMEDIATELY.");
            // Defaulting to 'false' for safety. Remove this line once real validation is implemented.
            return false;
            // ------------------------------------------------
    
        } catch (error) {
            console.error("Error during (placeholder) signature validation:", error);
            return false; // Fail validation on any error during the process
        }
    }
    
    
    export async function POST(request) {
        let rawBody;
        try {
            // IMPORTANT: Need the raw request body *before* parsing for signature validation
            rawBody = await request.text();
    
            // --- Validate Signature ---
            // CRITICAL: Enable and ensure correct implementation before production deployment.
            // const isValid = validatePlivoSignature(request, rawBody);
            // if (!isValid) {
            //     console.warn("Invalid Plivo signature received. Rejecting request.");
            //     // Return 403 Forbidden for invalid signatures
            //     return new Response("Invalid signature", { status: 403 });
            // }
            // Temporary log message while validation is bypassed/placeholder:
            console.warn("Plivo signature validation is currently bypassed or using placeholder logic. IMPLEMENT PROPER VALIDATION!");
    
            // --- Parse and Process ---
            // Now parse the JSON body *after* validation (or temporary bypass)
            const data = JSON.parse(rawBody);
    
            console.log("Received Plivo Webhook Data:", JSON.stringify(data, null, 2));
    
            // Extract common fields
            const messageType = data.Type; // e.g., 'message', 'message_status'
            const fromNumber = data.From;
            const toNumber = data.To;
            const messageUuid = data.MessageUUID;
    
            // Process based on webhook type
            if (messageType === 'message') {
                // An incoming message from a user
                const text = data.Text;
                const mediaUrl = data.MediaUrl; // If it's a media message
                const mediaContentType = data.MediaContentType;
                // Check for interactive message response data (structure depends on Plivo payload)
                const buttonPayload = data.Button?.Payload; // Example: Payload from button click
                const listId = data.List?.Id;             // Example: ID of selected list item
    
                console.log(`Incoming message from ${fromNumber}: ${text || ('Media: ' + mediaUrl) || 'Interactive Response (check payload/ID)'}`);
    
                if (buttonPayload) {
                    console.log(`Button Payload: ${buttonPayload}`);
                    // Handle button response based on payload
                }
                if (listId) {
                    console.log(`List Selection ID: ${listId}`);
                    // Handle list selection based on ID
                }
    
                // Add your business logic here:
                // - Store the message in a database
                // - Trigger an automated response
                // - Route to a support system
    
            } else if (messageType === 'message_status') {
                // Status update for an outgoing message you sent
                const status = data.MessageStatus; // e.g., 'sent', 'delivered', 'read', 'failed', 'undelivered'
                console.log(`Status update for message ${messageUuid}: ${status}`);
    
                // Add your business logic here:
                // - Update message status in your database
                if (status === 'failed' || status === 'undelivered') {
                    const errorCode = data.ErrorCode;
                    console.error(`Message ${messageUuid} failed with code: ${errorCode}. Error details: ${data.ErrorReason || 'N/A'}`);
                    // Handle failure (e.g., notify admin, update UI)
                }
            } else {
                console.log(`Received unhandled webhook type: ${messageType}`);
            }
    
            // --- Acknowledge Receipt ---
            // Plivo expects a 200 OK response to confirm receipt.
            // Avoid sending any body content unless specified by Plivo docs.
             return new Response(null, { status: 200 }); // Empty body, 200 status
    
        } catch (error) {
            if (error instanceof SyntaxError && rawBody !== undefined) {
                 // Error parsing the rawBody as JSON
                 console.error("Failed to parse webhook JSON body:", error);
                 return NextResponse.json({ success: false, error: "Invalid JSON received" }, { status: 400 });
            } else {
                // General processing error
                console.error("Error processing Plivo webhook:", error);
                // Avoid sending detailed internal errors back in the response for security.
                // Return 500 to indicate a server-side issue. Plivo might retry.
                return NextResponse.json({ success: false, error: "Internal server error processing webhook" }, { status: 500 });
            }
        }
    }

    Explanation:

    • Signature Validation Function (validatePlivoSignature):
      • This is the most critical security component for your webhook. It verifies that incoming requests originate from Plivo.
      • Placeholder Warning: The provided function is a placeholder only. It demonstrates the concept but lacks the actual validation logic.
      • Action Required: You must consult the official Plivo documentation for Node.js and replace the placeholder section with Plivo's recommended signature validation method. This might involve using a utility function from the Plivo Node.js SDK (if available) or manually implementing the HMAC-SHA256 validation steps using the request URL, nonce header (X-Plivo-Signature-V3-Nonce), signature header (X-Plivo-Signature-V3), your Plivo Auth Token, and the raw request body.
      • Do not deploy to production without implementing and testing correct signature validation. The current code defaults to failing validation (return false;) for safety if the placeholder isn't replaced.
    • Webhook Handler (POST):
      • Reads the raw request body using request.text() before JSON parsing, as the raw body is essential for signature validation.
      • Validation Call (Commented Out): The call to validatePlivoSignature is commented out initially. You must uncomment and integrate it once the validation function is correctly implemented. If validation fails, a 403 Forbidden response should be returned.
      • Parsing: After successful validation (or during testing with the bypass), the raw body is parsed into a JSON object using JSON.parse().
      • Processing: The code logs the incoming data, checks the Type field (message or message_status), and extracts relevant information. It includes examples for handling text, media, and basic interactive message responses (button clicks, list selections).
      • Business Logic: Placeholder comments indicate where your application-specific logic (database interaction, automated replies, etc.) should be added.
      • Acknowledgement: A 200 OK response with an empty body is returned to Plivo upon successful receipt and basic processing. Returning non-2xx status codes may cause Plivo to retry the webhook delivery.
    • Error Handling: Catches JSON parsing errors and other processing errors, logging them and returning appropriate HTTP status codes (400 for bad JSON, 500 for internal errors).

4. Configuring Plivo Webhooks for Your Next.js App

You need to tell Plivo where to send these webhook events. This is typically done by creating or configuring a Plivo Application.

  1. Navigate to Plivo Console: Go to Messaging -> Applications.
  2. Create or Edit an Application:
    • Click "Add New Application".
    • Give it a descriptive name (e.g., "Next.js WhatsApp App").
    • Message URL: This is the crucial part. Enter the publicly accessible HTTPS URL for your webhook handler:
      • Local Development: Start ngrok (ngrok http 3000) and copy the https forwarding URL. Your Message URL will be https://<your-ngrok-subdomain>.ngrok.io/api/plivo-webhook.
      • Production (e.g., Vercel): Use your deployed application's URL: https://<your-vercel-app-name>.vercel.app/api/plivo-webhook.
    • Method: Set to POST.
    • (Optional) Configure Answer URL and other settings if you plan to use Plivo for voice calls with this application as well.
    • Click "Create Application" or "Update Application".
  3. Associate Sender with Application:
    • Go to Messaging -> WhatsApp Senders.
    • Find your WhatsApp sender number and click "Edit".
    • In the "Application" dropdown, select the Plivo Application you just created or updated.
    • Click "Update Sender". This ensures incoming messages and status updates for this sender are routed to your webhook URL.

5. Error Handling, Logging, and Retry Strategies

Robust applications need solid error handling.

  • API Route Errors: The API routes already include try...catch blocks.

    • Log errors using console.error. In production, integrate a dedicated logging service (e.g., Sentry, Logtail, Datadog) for better aggregation, searching, and analysis.
    • Return meaningful JSON error responses with appropriate HTTP status codes (4xx for client errors like invalid input, 5xx for server errors like failed Plivo client initialization or downstream issues). Avoid leaking sensitive internal details in error responses sent to the client.
  • Plivo API Errors: The catch block in /api/send-whatsapp/route.js specifically handles errors from client.messages.create(). Log the error.message and potentially error.error (which might contain Plivo-specific error details) for debugging. Consider the error.statusCode to understand the error type (e.g., 400 bad request, 401 auth error, 5xx server error).

  • Webhook Errors: The webhook handler logs processing errors. Ensure signature validation failures are logged clearly, including why validation failed (e.g., missing header, calculation mismatch). If your internal processing logic fails after receiving the webhook (e.g., database write error), consider logging the error but still returning a 200 OK to Plivo to prevent unnecessary retries if the issue is internal and not Plivo's fault (assuming you can handle the failure asynchronously or it's non-critical). If the webhook should be retried because the failure was temporary (e.g., temporary DB unavailability), return a 5xx error code.

  • Retries:

    • Outgoing Messages: For transient errors from the Plivo API (e.g., network issues, temporary Plivo downtime resulting in 5xx errors), consider implementing a retry mechanism in the /api/send-whatsapp route. Use libraries like async-retry for exponential backoff.

      bash
      npm install async-retry
      javascript
      // Example integration within /api/send-whatsapp/route.js POST function
      import retry from 'async-retry';
      // ... inside the POST function, replace the direct client.messages.create call ...
      
      try {
          const response = await retry(
              async (bail, attemptNumber) => {
                  console.log(`Attempt ${attemptNumber} to send message via Plivo...`);
                  try {
                      const apiResponse = await client.messages.create(payload);
                      console.log("Plivo API call successful.");
                      return apiResponse; // Success, return result
                  } catch (error) {
                      console.warn(`Plivo API call attempt ${attemptNumber} failed:`, error.message);
                      // Don't retry on client errors (4xx) - indicates a problem with the request itself.
                      if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
                          console.error(`Bailing on Plivo API call due to client error ${error.statusCode}.`);
                          bail(error); // bail stops retrying and throws the error captured here
                          return; // Explicitly return after bailing
                      }
                      // For other errors (e.g., 5xx, network errors, no statusCode), throw to trigger retry.
                      throw error;
                  }
              },
              {
                  retries: 3, // Number of retries (e.g., 3 retries = 4 total attempts)
                  factor: 2, // Exponential backoff factor
                  minTimeout: 1000, // Initial delay in ms (1 second)
                  maxTimeout: 10000, // Maximum delay in ms (10 seconds)
                  onRetry: (error, attempt) => console.warn(`Retrying Plivo API call (Attempt ${attempt}). Error: ${error.message}`)
              }
          );
      
          // Success after retries (or on first attempt)
          console.log("Successfully sent message after retries (if any). Plivo Response:", response);
          return NextResponse.json({
              success: true,
              message_uuid: response.messageUuid?.[0],
              api_id: response.apiId,
              message: response.message,
          });
      
      } catch (error) {
          // Error after all retries failed, or if bail() was called due to a 4xx error.
          console.error("Plivo API Error after all retries or non-retryable error:", error);
          const errorMessage = error.message || "Failed to send message via Plivo after retries.";
          const errorStatus = error.statusCode || 500;
          return NextResponse.json(
              { success: false, error: errorMessage, details: error.error },
              { status: errorStatus }
          );
      }
    • Incoming Webhooks: Plivo automatically handles retries for webhooks if your endpoint doesn't return 200 OK within a certain timeout. Ensure your endpoint is reliable and responds quickly. If processing might take longer than Plivo's timeout, acknowledge the webhook immediately (200 OK) and process the data asynchronously.


Looking to expand your messaging capabilities? Check out these related guides:

For more information about Plivo's WhatsApp Business API, visit the official Plivo WhatsApp documentation.