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.
Integration method
The plivo npm package communicates with Plivo's REST API over HTTPS and works in Bolt's WebContainer for outbound operations. All SMS sending, call initiation, and status lookups run through Next.js API routes with Auth ID and Auth Token from environment variables. Incoming SMS webhooks and inbound voice calls require Plivo to POST to a public URL — deploy to Netlify or Bolt Cloud before testing inbound flows.
Prerequisites
- 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
Get Plivo Credentials and a Phone Number
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.
1# Plivo credentials locations in console.plivo.com:2# Auth ID: console.plivo.com/dashboard/ → shown as 'AUTH ID' (starts with 'MA')3# Auth Token: console.plivo.com/dashboard/ → click eye icon to reveal4# Phone Number: console.plivo.com/phone-numbers/ → your purchased numbers56# API Base URL: https://api.plivo.com/v1/Account/{AUTH_ID}/Pro tip: Plivo trial accounts can send SMS to any destination without verification restrictions, unlike Twilio. This makes it easier to test your integration immediately after signup.
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.
Install Plivo SDK and Set Up Environment Variables
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.
Install the plivo npm package. Create a .env file with PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN, and PLIVO_PHONE_NUMBER (my Plivo number in +1XXXXXXXXXX format). Create a lib/plivo.ts file that initializes the Plivo client with PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN and exports the client instance. Include a check that throws a clear error if credentials are missing.
Paste this in Bolt.new chat
1// lib/plivo.ts2import plivo from 'plivo';34const authId = process.env.PLIVO_AUTH_ID;5const authToken = process.env.PLIVO_AUTH_TOKEN;67if (!authId || !authToken) {8 throw new Error('PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN must be set in environment variables');9}1011export const plivoClient = new plivo.Client(authId, authToken);12export const PLIVO_PHONE_NUMBER = process.env.PLIVO_PHONE_NUMBER!;1314// .env content:15// PLIVO_AUTH_ID=MAxxxxxxxxxxxxxxxxxxxxxxxx16// PLIVO_AUTH_TOKEN=your_auth_token_here17// PLIVO_PHONE_NUMBER=+15551234567Pro tip: The Plivo SDK accepts phone numbers with or without the + prefix. However, for consistency and to avoid ambiguity, always use E.164 format (+[country code][number]) in your application code.
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.
Create the SMS Sending API Route
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.
Create a Next.js API route at /api/plivo/send-sms (POST) that accepts 'to' (E.164 phone number) and 'message' (string) in the request body. Validate that 'to' starts with '+' and message is not empty. Use the plivoClient from lib/plivo.ts to call messages.create({ src: PLIVO_PHONE_NUMBER, dst: to, text: message }). Return the message UUID on success or a descriptive error message on failure. Handle the 202 Accepted response from Plivo correctly.
Paste this in Bolt.new chat
1// app/api/plivo/send-sms/route.ts2import { NextResponse } from 'next/server';3import { plivoClient, PLIVO_PHONE_NUMBER } from '@/lib/plivo';45const E164_REGEX = /^\+[1-9]\d{1,14}$/;67export async function POST(request: Request) {8 try {9 const { to, message } = await request.json();1011 if (!to || !E164_REGEX.test(to)) {12 return NextResponse.json(13 { error: 'Invalid phone number. Use E.164 format: +14155552671' },14 { status: 400 }15 );16 }1718 if (!message || message.trim().length === 0) {19 return NextResponse.json(20 { error: 'Message cannot be empty' },21 { status: 400 }22 );23 }2425 const response = await plivoClient.messages.create(26 PLIVO_PHONE_NUMBER, // src27 to, // dst28 message // text29 );3031 return NextResponse.json({32 success: true,33 messageUuid: response.message_uuid[0],34 apiId: response.api_id,35 });36 } catch (error: unknown) {37 const e = error as { message: string; statusCode?: number };38 return NextResponse.json(39 { error: e.message },40 { status: e.statusCode || 500 }41 );42 }43}Pro tip: Plivo charges per SMS segment (160 characters). Messages over 160 characters are split into 153-character segments (the remaining 7 characters are used for multi-part concatenation headers). Keep notification messages under 160 characters to minimize costs.
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.
Add Voice Call Functionality
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.
Create two Plivo voice call API routes. First, /api/plivo/make-call (POST) accepts 'to' phone number and 'message' text, uses plivoClient.calls.create() with from PLIVO_PHONE_NUMBER, to the recipient, and answer_url pointing to /api/plivo/call-answer on my deployed URL (NEXT_PUBLIC_APP_URL from env). Second, /api/plivo/call-answer (GET) returns a Plivo XML response using plivo.Response() with a Speak element that reads the message passed as a URL parameter. Handle the GET request from Plivo when the call is answered.
Paste this in Bolt.new chat
1// app/api/plivo/call-answer/route.ts2import { NextResponse } from 'next/server';3import plivo from 'plivo';45export async function GET(request: Request) {6 const { searchParams } = new URL(request.url);7 const message = searchParams.get('message') || 'Hello, this is an automated message.';89 const response = new plivo.Response();10 response.addSpeak(message, {11 voice: 'WOMAN',12 language: 'en-US',13 loop: '1',14 });15 response.addHangup();1617 return new Response(response.toXML(), {18 headers: { 'Content-Type': 'text/xml' },19 });20}2122// app/api/plivo/make-call/route.ts23import { plivoClient, PLIVO_PHONE_NUMBER } from '@/lib/plivo';2425export async function POST(request: Request) {26 try {27 const { to, message } = await request.json();28 const appUrl = process.env.NEXT_PUBLIC_APP_URL;29 const answerUrl = `${appUrl}/api/plivo/call-answer?message=${encodeURIComponent(message)}`;3031 const callResponse = await plivoClient.calls.create(32 PLIVO_PHONE_NUMBER,33 to,34 answerUrl,35 { answerMethod: 'GET' }36 );3738 return NextResponse.json({ success: true, requestUuid: callResponse.request_uuid });39 } catch (error: unknown) {40 const e = error as { message: string };41 return NextResponse.json({ error: e.message }, { status: 500 });42 }43}Pro tip: Plivo's Speak element supports multiple voice options: WOMAN, MAN, and various neural voices. For a more natural sound, use Plivo's SSML support in the Speak element to control pauses, emphasis, and pronunciation.
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.
Deploy and Configure Inbound SMS and Voice Webhooks
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.
Create an inbound SMS webhook handler at /api/plivo/incoming-sms (POST). Parse Plivo's form-encoded webhook: From (sender number), To (your Plivo number), Text (message content), MessageUUID. Save the message to a Supabase messages table with sender, content, and timestamp. Return a Plivo XML auto-reply using plivo.Response() with a Speak or Message element: 'Thanks for your message! We will get back to you soon.' Parse the request body as form data since Plivo sends application/x-www-form-urlencoded.
Paste this in Bolt.new chat
1// app/api/plivo/incoming-sms/route.ts2import { NextResponse } from 'next/server';3import plivo from 'plivo';45export async function POST(request: Request) {6 try {7 const formData = await request.formData();8 const from = formData.get('From') as string;9 const text = formData.get('Text') as string;10 const messageUuid = formData.get('MessageUUID') as string;1112 console.log(`Inbound SMS from ${from}: ${text} (UUID: ${messageUuid})`);13 // Save to database here...1415 // Respond with Plivo XML16 const response = new plivo.Response();17 response.addMessage(18 'Thanks for your message! We will get back to you soon.',19 { src: process.env.PLIVO_PHONE_NUMBER!, dst: from }20 );2122 return new Response(response.toXML(), {23 headers: { 'Content-Type': 'text/xml' },24 });25 } catch (error: unknown) {26 const e = error as { message: string };27 console.error('Plivo webhook error:', e.message);28 return NextResponse.json({ error: e.message }, { status: 500 });29 }30}Pro tip: Plivo sends webhooks as application/x-www-form-urlencoded, not JSON. Use request.formData() to parse the body — attempting JSON.parse will fail with a parse error.
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.
Common 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.
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.
Copy this prompt to try it in Bolt.new
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.
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.
Copy this prompt to try it in Bolt.new
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.
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.
Copy this prompt to try it in Bolt.new
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.
1// Add to lib/plivo.ts for clear error messages:2console.log('Plivo Auth ID:', process.env.PLIVO_AUTH_ID?.substring(0, 5) + '...');3// 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.
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
Alternatives
Twilio has larger market share, more developer resources, and the Twilio Verify service for managed OTP, but costs 40-60% more per message than Plivo for high-volume use cases.
Vonage offers competitive international SMS pricing with a similar API pattern, and includes additional channels like WhatsApp Business API and in-app messaging through the Vonage Conversation API.
Sinch adds RCS (Rich Communication Services) for Android rich messaging beyond standard SMS and has strong presence in European markets where Plivo has less coverage.
Bandwidth operates its own carrier network rather than reselling, offering lower latency and direct carrier relationships for high-volume enterprise messaging requirements.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation