# How to Integrate Bolt.new with Plivo

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

To integrate Plivo with Bolt.new, install the plivo npm package and create Next.js API routes using your Auth ID and Auth Token from the Plivo console. Plivo is 40-60% cheaper than Twilio with an identical SDK pattern. Outbound SMS and voice calls work in Bolt's WebContainer preview. Incoming message webhooks and call control require a deployed URL — deploy to Netlify or Bolt Cloud first.

## Cost-Effective SMS and Voice with Plivo in Bolt.new

Plivo is a Communications Platform as a Service (CPaaS) that offers SMS, MMS, and voice APIs nearly identical in functionality to Twilio but at 40-60% lower cost. If you are building a notification system, two-factor authentication flow, or automated calling application and Twilio's pricing is a concern, Plivo is the most straightforward drop-in alternative — the API patterns, SDK usage, and even some method signatures are similar.

Plivo's npm package (`plivo`) is a pure JavaScript library that communicates with Plivo's REST API over HTTPS. This makes it fully compatible with Bolt's WebContainer runtime, which blocks raw TCP connections but allows all HTTPS traffic. The SDK installs cleanly, initializes with your Auth ID and Auth Token, and provides methods for sending messages, initiating calls, and managing phone numbers. You can use it exactly as you would the Twilio SDK, with slightly different method signatures.

The Plivo integration follows the same pattern as any REST API integration in Bolt: create a Next.js API route, initialize the Plivo client with server-side environment variables, call the Plivo API, and return the result to your frontend. Outbound operations (sending SMS, initiating calls) work in the Bolt preview. Inbound operations — receiving SMS when someone texts your Plivo number, answering inbound calls — require Plivo to send HTTP callbacks to your server, which means you need a deployed URL first.

## Before you start

- A Plivo account (free trial includes $0.10 credit for testing)
- A Plivo phone number purchased or trial number from the Plivo console
- Your Plivo Auth ID and Auth Token from the Plivo console dashboard
- A Bolt.new project using Next.js for server-side API routes
- For voice calls: a deployed URL from Netlify or Bolt Cloud (for the answer_url parameter)

## Step-by-step guide

### 1. Get Plivo Credentials and a Phone Number

Your Plivo integration requires three things from the Plivo console: your Auth ID, your Auth Token, and a Plivo phone number. Log into console.plivo.com. On the main dashboard, you will see your Auth ID (a string starting with 'MA') and your Auth Token immediately. The Auth Token is shown masked by default — click the eye icon to reveal it. Copy both.

To get a phone number: in the left sidebar, go to Phone Numbers → Buy Numbers. Filter by the country and select 'SMS' capability (and 'Voice' if you need voice calls). Choose a number and click Buy. Plivo trial accounts start with $0.10 credit which is enough to send several test messages. Trial accounts can send SMS to any number, unlike Twilio which restricts trial sends to verified numbers — this makes initial testing easier with Plivo.

Plivo's API uses HTTPS Basic Authentication internally: the Auth ID is the username and Auth Token is the password. The plivo npm package handles this for you — you just pass them to the constructor. Never use them in client-side code or add them to any variable with NEXT_PUBLIC_ prefix.

```
# Plivo credentials locations in console.plivo.com:
# Auth ID: console.plivo.com/dashboard/ → shown as 'AUTH ID' (starts with 'MA')
# Auth Token: console.plivo.com/dashboard/ → click eye icon to reveal
# Phone Number: console.plivo.com/phone-numbers/ → your purchased numbers

# API Base URL: https://api.plivo.com/v1/Account/{AUTH_ID}/
```

**Expected result:** You have your Plivo Auth ID (MA...), Auth Token, and a Plivo phone number in E.164 format (+1XXXXXXXXXX or international equivalent) ready for configuration.

### 2. Install Plivo SDK and Set Up Environment Variables

The official Plivo Node.js SDK is the `plivo` npm package. It is pure JavaScript with no native binary dependencies, making it fully compatible with Bolt's WebContainer runtime. The package communicates with Plivo's REST API over HTTPS and installs without any compilation step.

Configure environment variables in your .env file. Plivo credentials are server-side only — they should never be exposed to the client. Using PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN as names (no NEXT_PUBLIC_ prefix) ensures they stay server-side in Next.js API routes.

Create a lib/plivo.ts utility that initializes the Plivo client and exports it. The plivo package constructor takes Auth ID and Auth Token as parameters. Initialize the client once and export the instance — avoid re-initializing on every API call. TypeScript types are included in the plivo package from version 4.x onwards.

```
// lib/plivo.ts
import plivo from 'plivo';

const authId = process.env.PLIVO_AUTH_ID;
const authToken = process.env.PLIVO_AUTH_TOKEN;

if (!authId || !authToken) {
  throw new Error('PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN must be set in environment variables');
}

export const plivoClient = new plivo.Client(authId, authToken);
export const PLIVO_PHONE_NUMBER = process.env.PLIVO_PHONE_NUMBER!;

// .env content:
// PLIVO_AUTH_ID=MAxxxxxxxxxxxxxxxxxxxxxxxx
// PLIVO_AUTH_TOKEN=your_auth_token_here
// PLIVO_PHONE_NUMBER=+15551234567
```

**Expected result:** The plivo package is installed. lib/plivo.ts exports an initialized Plivo client. PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_PHONE_NUMBER are set in .env.

### 3. Create the SMS Sending API Route

The SMS API route is the core of the Plivo integration. It runs server-side in the Next.js API routes directory, has access to the Plivo credentials from environment variables, and communicates with Plivo's API over HTTPS. The route accepts a destination phone number and message, validates them, and sends the SMS using the plivo client.

Plivo's message create method signature is slightly different from Twilio's: it takes an object with src (source number), dst (destination number), and text fields. The src field is your Plivo phone number; dst is the recipient in E.164 format. For sending to multiple recipients in one API call, Plivo supports comma-separated numbers in the dst field (e.g., '+14155552671<+14155552672') — a feature Twilio doesn't offer on its standard API.

The response from Plivo's message API includes a message_uuid array (array because of multi-recipient support) and an api_id for request tracking. Store the message_uuid if you want to check delivery status later. Plivo returns HTTP 202 Accepted (not 200 OK) for successful message sends — this is important to note for error handling logic.

```
// app/api/plivo/send-sms/route.ts
import { NextResponse } from 'next/server';
import { plivoClient, PLIVO_PHONE_NUMBER } from '@/lib/plivo';

const E164_REGEX = /^\+[1-9]\d{1,14}$/;

export async function POST(request: Request) {
  try {
    const { to, message } = await request.json();

    if (!to || !E164_REGEX.test(to)) {
      return NextResponse.json(
        { error: 'Invalid phone number. Use E.164 format: +14155552671' },
        { status: 400 }
      );
    }

    if (!message || message.trim().length === 0) {
      return NextResponse.json(
        { error: 'Message cannot be empty' },
        { status: 400 }
      );
    }

    const response = await plivoClient.messages.create(
      PLIVO_PHONE_NUMBER, // src
      to,                 // dst
      message             // text
    );

    return NextResponse.json({
      success: true,
      messageUuid: response.message_uuid[0],
      apiId: response.api_id,
    });
  } catch (error: unknown) {
    const e = error as { message: string; statusCode?: number };
    return NextResponse.json(
      { error: e.message },
      { status: e.statusCode || 500 }
    );
  }
}
```

**Expected result:** POST to /api/plivo/send-sms with a valid phone number and message sends an actual SMS via Plivo. The response includes a messageUuid. This works in the Bolt preview without deployment.

### 4. Add Voice Call Functionality

Plivo's voice call API initiates outbound calls using the calls.create() method. Unlike SMS, voice calls require a publicly accessible URL (the answer_url) that Plivo fetches when the call is answered to determine what to do — read a message, collect keypad input, transfer, or hang up. This answer_url is controlled via Plivo's XML markup (similar to Twilio's TwiML).

The answer_url limitation means voice calls that play dynamic messages require a deployed endpoint. However, you can specify any public URL as the answer_url — including a static URL hosted elsewhere if you just need to play a fixed message. For development, the recommended pattern is to create the call-handling XML endpoint in Bolt, deploy it quickly to Netlify, and test the full voice flow on the deployed app.

Plivo's XML response format uses plivo.Response() to create the response object. Add elements like Speak (text-to-speech), Play (pre-recorded audio), GetDigits (collect DTMF input), Dial (transfer to a number), or Hangup. The Speak element supports text-to-speech with language and voice options — useful for multilingual applications without pre-recording audio files.

```
// app/api/plivo/call-answer/route.ts
import { NextResponse } from 'next/server';
import plivo from 'plivo';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const message = searchParams.get('message') || 'Hello, this is an automated message.';

  const response = new plivo.Response();
  response.addSpeak(message, {
    voice: 'WOMAN',
    language: 'en-US',
    loop: '1',
  });
  response.addHangup();

  return new Response(response.toXML(), {
    headers: { 'Content-Type': 'text/xml' },
  });
}

// app/api/plivo/make-call/route.ts
import { plivoClient, PLIVO_PHONE_NUMBER } from '@/lib/plivo';

export async function POST(request: Request) {
  try {
    const { to, message } = await request.json();
    const appUrl = process.env.NEXT_PUBLIC_APP_URL;
    const answerUrl = `${appUrl}/api/plivo/call-answer?message=${encodeURIComponent(message)}`;

    const callResponse = await plivoClient.calls.create(
      PLIVO_PHONE_NUMBER,
      to,
      answerUrl,
      { answerMethod: 'GET' }
    );

    return NextResponse.json({ success: true, requestUuid: callResponse.request_uuid });
  } catch (error: unknown) {
    const e = error as { message: string };
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** After deploying to Netlify and setting NEXT_PUBLIC_APP_URL to your deployed URL, POST to /api/plivo/make-call initiates a real outbound voice call that plays the specified message when answered.

### 5. Deploy and Configure Inbound SMS and Voice Webhooks

Outbound SMS and call initiation work during Bolt development. To receive inbound SMS (texts sent to your Plivo number) or handle inbound voice calls, Plivo must send HTTP requests to your server. Since Bolt's WebContainer has no public URL, you must deploy before setting up inbound webhooks.

Deploy your Bolt project to Netlify via Settings → Applications → Netlify. After the deploy completes, add your environment variables in Netlify's dashboard (Site Settings → Environment Variables): PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_PHONE_NUMBER. Redeploy after adding env vars.

Configure inbound SMS in the Plivo console: go to Phone Numbers → Your Number → click the number → in the 'Message' section, set the 'Message URL' to your deployed webhook endpoint (https://your-app.netlify.app/api/plivo/incoming-sms) and method to POST. For inbound calls, set the 'Answer URL' and 'Fallback URL' in the 'Voice' section. Create the incoming-sms handler route that parses Plivo's webhook format — Plivo sends form data with fields: From, To, Text, MessageUUID — and responds with a Plivo XML response for auto-replies.

```
// app/api/plivo/incoming-sms/route.ts
import { NextResponse } from 'next/server';
import plivo from 'plivo';

export async function POST(request: Request) {
  try {
    const formData = await request.formData();
    const from = formData.get('From') as string;
    const text = formData.get('Text') as string;
    const messageUuid = formData.get('MessageUUID') as string;

    console.log(`Inbound SMS from ${from}: ${text} (UUID: ${messageUuid})`);
    // Save to database here...

    // Respond with Plivo XML
    const response = new plivo.Response();
    response.addMessage(
      'Thanks for your message! We will get back to you soon.',
      { src: process.env.PLIVO_PHONE_NUMBER!, dst: from }
    );

    return new Response(response.toXML(), {
      headers: { 'Content-Type': 'text/xml' },
    });
  } catch (error: unknown) {
    const e = error as { message: string };
    console.error('Plivo webhook error:', e.message);
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** After deploying and configuring the Plivo console webhook URLs, SMS messages sent to your Plivo number trigger the webhook handler. The sender receives an automated reply, and the message is logged or stored.

## Best practices

- Store PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN only as server-side environment variables — these credentials control your entire Plivo account including billing
- Always use E.164 format for phone numbers (+[country code][number]) to ensure consistent handling and avoid Plivo's format validation errors
- Keep SMS messages under 160 characters when possible — Plivo charges per 153-character segment for multi-part messages, which can triple costs unexpectedly for long notifications
- Return responses quickly from voice call answer_url endpoints — Plivo has a 5-second timeout for fetching call instructions; slow database queries can cause calls to fail
- Implement message deduplication for inbound SMS by storing the MessageUUID — Plivo may deliver the same webhook more than once during network issues
- Use Plivo's messaging logs (console.plivo.com → Messaging → Logs) to debug delivery issues before assuming there's a code problem
- Set up fallback URLs in Plivo for voice calls — if your primary answer_url fails, Plivo calls the fallback URL, preventing all voice calls from dropping silently

## Use cases

### SMS Notification System for App Events

Send SMS notifications to users when important events happen in your app: order confirmations, appointment reminders, security alerts, or subscription renewals. Plivo's competitive pricing makes it cost-effective for high-volume notification systems. The API route accepts a recipient number and message, validates the input, and sends via Plivo.

Prompt example:

```
Create an SMS notification system using Plivo. Install the plivo npm package. Create an API route at /api/plivo/send-sms that accepts 'to' (phone in E.164 format) and 'message' (string, max 160 chars) in the request body. Initialize plivo with PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN from env. Use client.messages.create() to send the SMS from PLIVO_PHONE_NUMBER. Return the message UUID on success. Add a notification preferences page where users can enter their phone number and choose which events trigger SMS alerts.
```

### Phone Number Verification (OTP) Flow

Implement two-factor authentication or phone number verification by sending a one-time password via SMS. Your app generates a 6-digit code, stores it server-side with an expiry, sends it via Plivo, and verifies it when the user submits the code. More affordable than Twilio Verify for high-volume verification at Plivo's lower per-message rate.

Prompt example:

```
Build phone number verification with Plivo OTP. Create two API routes: /api/auth/phone/send-otp (POST, accepts phoneNumber, generates a 6-digit code, stores it in Redis/database with 10-minute expiry, sends via Plivo SMS) and /api/auth/phone/verify-otp (POST, accepts phoneNumber and code, checks against stored code, returns {verified: true} on match). Add a two-step UI: phone input with 'Send Code' button, then code input with 'Verify' and 'Resend' options. Use PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, PLIVO_PHONE_NUMBER from env.
```

### Automated Outbound Voice Calls

Initiate automated phone calls to users using Plivo's voice API with XML call control. Use cases include appointment reminders, fraud alerts that require confirmation, or automated survey calls. Plivo's voice API uses XML (similar to Twilio's TwiML) to control call flow — read a message, collect input, branch based on key presses.

Prompt example:

```
Create an automated voice call system using Plivo. Create an API route at /api/plivo/make-call that accepts 'to' (phone number) and 'message' (text to speak). Initialize plivo with PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN. Use client.calls.create() to initiate an outbound call from PLIVO_PHONE_NUMBER to the recipient. Set answer_url to a deployed endpoint /api/plivo/call-xml that returns a Plivo XML response using plivo.Response() with a Speak element for the message. Create the call-xml endpoint. Note: the answer_url must be a deployed URL that Plivo can reach.
```

## Troubleshooting

### API throws 'Invalid Auth' or 401 Unauthorized error

Cause: PLIVO_AUTH_ID or PLIVO_AUTH_TOKEN is incorrect. The Auth ID starts with 'MA' and is 24 characters; the Auth Token is 40 characters. Mixing them up causes authentication failures.

Solution: Go to console.plivo.com dashboard and copy both credentials fresh. Ensure Auth ID goes in PLIVO_AUTH_ID and Auth Token goes in PLIVO_AUTH_TOKEN — they are different! No quotes needed in .env. Restart the Bolt dev server after editing .env.

```
// Add to lib/plivo.ts for clear error messages:
console.log('Plivo Auth ID:', process.env.PLIVO_AUTH_ID?.substring(0, 5) + '...');
// Should output: MA... (first 5 chars). If undefined, env var is not loading.
```

### SMS sends successfully but never arrives at destination

Cause: The destination number may be invalid, the carrier may be filtering the message, or the sender number is not provisioned for SMS in that country.

Solution: Verify the destination number is in E.164 format. Check your Plivo account's messaging logs (console.plivo.com → Messaging → Logs) to see if the message was delivered or rejected by the carrier. Some carriers block messages from shared sender IDs — use a dedicated Plivo number. Also ensure your Plivo account is funded for international messages if sending outside the US.

### Voice call answer_url returns error or call hangs up immediately

Cause: Plivo fetches the answer_url when the call connects. If the URL is not publicly accessible (e.g., localhost or Bolt preview URL), Plivo cannot fetch the call instructions and hangs up.

Solution: The answer_url must be a publicly accessible URL. Deploy your app to Netlify first, then use your deployed URL as the answer_url. During development, you cannot test voice call flow in the Bolt preview — deploy to Netlify (30-60 seconds) to test each iteration of your call flow.

### Inbound webhooks not received during Bolt development

Cause: This is the expected WebContainer limitation. Plivo webhooks are inbound HTTP requests from Plivo's servers to your application. Bolt's WebContainer has no public URL for Plivo to reach.

Solution: Deploy to Netlify or Bolt Cloud. Configure your Plivo phone number's Message URL and Answer URL in the Plivo console to point to your deployed endpoints. Test inbound functionality on the deployed app, not in the Bolt preview.

## Frequently asked questions

### Does the Plivo npm package work in Bolt's WebContainer?

Yes. The plivo npm package is pure JavaScript and communicates with Plivo's REST API over HTTPS. It installs and runs in Bolt's WebContainer without issues. Outbound operations — sending SMS, initiating voice calls — work in the Bolt preview. Inbound operations — receiving SMS, answering voice calls — require Plivo to call a webhook URL, which needs a publicly accessible deployed server.

### How much cheaper is Plivo than Twilio?

Plivo's US SMS pricing is approximately $0.0035 per outbound message versus Twilio's $0.0079, making Plivo roughly 55% cheaper for domestic SMS. International pricing varies by country but Plivo is consistently 30-60% less expensive. For voice calls, Plivo is approximately $0.013/minute outbound vs Twilio's $0.014/minute. Phone number rental costs are similar ($1/month). The savings compound significantly at high volumes.

### Can I migrate from Twilio to Plivo without changing much code?

The migration requires code changes since the SDKs have different method signatures, but the patterns are similar. The main differences: Plivo uses Auth ID/Auth Token instead of Account SID/Auth Token; the message method uses src/dst/text instead of from/to/body; response XML uses plivo.Response() instead of twilio.twiml; and the phone number format requirements are the same (E.164). Plan for a few hours of code changes and testing rather than a direct drop-in replacement.

### Does Plivo have a sandbox or test environment?

Plivo does not have a separate sandbox environment like Twilio's test credentials. However, Plivo's free trial account includes $0.10 credit which is sufficient for testing. Use your real credentials during development — each test SMS costs a fraction of a cent. Plivo's console shows all sent messages in the Messaging Logs section for debugging, including delivery status and any error codes.

### Can I receive inbound SMS in Bolt.new without deploying?

No. Receiving inbound SMS requires Plivo to send an HTTP request to your webhook endpoint when someone texts your Plivo number. Bolt's WebContainer has no public URL — it runs inside a browser tab that external services cannot reach. Deploy to Netlify or Bolt Cloud first, configure your Plivo number's Message URL, and test inbound SMS on the deployed app. For outbound SMS testing, the Bolt preview works immediately.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/plivo
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/plivo
