code examples
code examples
How to Send SMS with Plivo in Next.js Using NextAuth (2025 Guide)
Learn how to send SMS messages using Plivo API in Next.js 15 with NextAuth authentication. Step-by-step tutorial covering protected API routes, session management, error handling, and production best practices.
Send SMS with Plivo in Next.js using NextAuth: Complete Guide
Build a Next.js application with NextAuth authentication to send SMS messages via the Plivo API. Learn how to implement authenticated API routes, protect SMS endpoints with session management, and build production-ready SMS features using Next.js 15 App Router or Pages Router.
By the end of this guide, you'll have a functional Next.js application with NextAuth-protected API endpoints that send SMS messages programmatically, plus the knowledge to integrate this capability into larger applications.
Project Overview and Goals
Goal: Build a Next.js application with NextAuth authentication that accepts a destination phone number and message text, then uses the Plivo API to send an SMS message through a protected API route.
Problem Solved: Enables authenticated applications to programmatically send SMS notifications, alerts, verification codes, or other messages with proper session management and security controls.
Technologies Used:
- Next.js 15: A React framework for building full-stack web applications with built-in routing, API routes, and server-side rendering capabilities. Latest stable version is 15.5 (released January 2025) which includes App Router with React Server Components and Turbopack builds in beta.
- NextAuth.js (v4): Complete open-source authentication solution for Next.js applications. Supports OAuth, JWT, database sessions, and is designed for serverless environments. Version 4.24.x is compatible with Next.js 15 with some considerations for App Router usage.
- Plivo Node.js SDK (v4.x): Official library provided by Plivo to interact with their communication APIs from Node.js applications. Latest stable version as of January 2025 is available via
npm install plivo. Supports SMS, voice calls, and WhatsApp messaging (Plivo Node.js SDK). - Plivo API: The third-party service used for actual SMS delivery.
System Architecture:
+-------------+ +------------------------+ +-------------+ +-----------------+
| Client |------>| Next.js API Route |------>| Plivo SDK |------>| Plivo API/Infra |
| (Browser/ | | (Protected by NextAuth)| | (Node Module)| | (Sends SMS) |
| React) | +------------------------+ +-------------+ +-----------------+
+-------------+ |
| POST /api/send-sms |
| { "to": "...", |
| "text": "..." } |
| |
| [NextAuth]
| Session Check
| |
+----------------------+
Prerequisites:
- Node.js 18.17 or later: Required for Next.js 15. Download from nodejs.org. Check version with
node --version. Note: Node.js 18 reaches end-of-life on April 30, 2025; consider using Node.js 20 LTS or later for new projects. - npm, yarn, or pnpm: Package manager included with Node.js. Next.js 15 supports all three.
- A Plivo Account: Sign up at plivo.com. A trial account is sufficient to start.
- A Plivo Phone Number: You need an SMS-enabled Plivo phone number to send messages, especially to the US and Canada. Purchase one from the Plivo console (
Phone Numbers>Buy Numbers). Alternatively, for some regions, you might register an Alphanumeric Sender ID. - Plivo Auth ID and Auth Token: Found on the dashboard of your Plivo console after logging in. Store securely in environment variables.
- Basic understanding of React, Next.js, and TypeScript (optional but recommended).
- Familiarity with REST APIs and authentication concepts.
Final Outcome: A running Next.js application with NextAuth authentication and a protected /api/send-sms endpoint that successfully sends SMS messages via Plivo when called by authenticated users with valid credentials and parameters.
Related Guides: If you're also working with other frameworks, check out our guides on implementing SMS in Express.js or explore bulk SMS sending strategies for high-volume scenarios.
1. Setting Up Your Next.js Project with Plivo SDK
Initialize your Next.js project and install the necessary dependencies.
-
Create Next.js Project: Open your terminal and use
create-next-appto scaffold a new Next.js application:bashnpx create-next-app@latest plivo-sms-nextauthWhen prompted, select these options:
- TypeScript? Yes (recommended for type safety)
- ESLint? Yes
- Tailwind CSS? Optional (your preference)
src/directory? Yes (for better organization)- App Router? Yes (Next.js 15 default, uses React Server Components)
- Import alias? @/* (default)
bashcd plivo-sms-nextauth -
Install Dependencies: Install the Plivo Node.js SDK and NextAuth for authentication:
bashnpm install plivo next-authCurrent versions (as of January 2025):
plivo: Latest available via npm (GitHub: plivo/plivo-node)next-auth: ^4.24.x (v4 for Next.js 13-15, NextAuth.js docs)next: ^15.5.0 (included via create-next-app, Next.js 15.5 release)
-
Project Structure: Your Next.js project should have this structure:
plivo-sms-nextauth/ ├── src/ │ ├── app/ # App Router (Next.js 15) │ │ ├── api/ │ │ │ ├── auth/ │ │ │ │ └── [...nextauth]/ │ │ │ │ └── route.ts # NextAuth configuration │ │ │ └── send-sms/ │ │ │ └── route.ts # SMS API endpoint │ │ ├── layout.tsx # Root layout │ │ └── page.tsx # Home page │ └── lib/ │ └── auth.ts # NextAuth config options ├── .env.local # Environment variables (local) ├── .gitignore ├── next.config.js ├── package.json └── tsconfig.json -
Configure
.gitignore: Ensure.env.localis in your.gitignore(Next.js includes this by default):Code# environment variables .env*.local .env.production # next.js /.next/ /out/ # dependencies /node_modules # logs npm-debug.log* -
Configure Environment Variables (
.env.local): Create a.env.localfile in your project root. Next.js automatically loads variables from this file.- Purpose:
PLIVO_AUTH_ID: Your Plivo Account Auth ID for API authentication (found at https://console.plivo.com/dashboard/)PLIVO_AUTH_TOKEN: Your Plivo Account Auth Token for API authenticationPLIVO_SENDER_ID: The Plivo phone number (E.164 format:+14155551212) or approved Alphanumeric Sender IDNEXTAUTH_SECRET: Random string for encrypting JWT tokens. Generate with:openssl rand -base64 32NEXTAUTH_URL: Your application URL (http://localhost:3000 for development)
- How to Obtain Plivo Credentials:
- Log in to your Plivo Console
- Your
Auth IDandAuth Tokenare displayed on the dashboard - For
PLIVO_SENDER_ID: Navigate toPhone Numbers→Buy Numbersto purchase an SMS-enabled number. Use E.164 format (e.g., +14155551212) - For Alphanumeric Sender IDs (availability varies by country), check Plivo's documentation or support for registration requirements
- Format:
Code# Plivo Credentials - KEEP THESE SECRET PLIVO_AUTH_ID=your_auth_id_here PLIVO_AUTH_TOKEN=your_auth_token_here PLIVO_SENDER_ID=+14155551212 # NextAuth Configuration NEXTAUTH_SECRET=your_generated_secret_here NEXTAUTH_URL=http://localhost:3000Important: Never commit
.env.localto version control. Use environment variables in your hosting provider (Vercel, AWS, etc.) for production. - Purpose:
2. Configuring NextAuth Authentication for SMS Endpoints
Set up NextAuth to handle authentication across your application.
-
Create NextAuth Configuration (
src/lib/auth.ts):typescript// src/lib/auth.ts import { NextAuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; // Define your NextAuth configuration export const authOptions: NextAuthOptions = { providers: [ CredentialsProvider({ name: "Credentials", credentials: { username: { label: "Username", type: "text" }, password: { label: "Password", type: "password" } }, async authorize(credentials) { // Replace this with your actual authentication logic // This is a simple example - DO NOT use in production if (credentials?.username === "demo" && credentials?.password === "demo123") { return { id: "1", name: "Demo User", email: "demo@example.com" }; } return null; } }) ], session: { strategy: "jwt" }, pages: { signIn: "/auth/signin", }, callbacks: { async jwt({ token, user }) { if (user) { token.id = user.id; } return token; }, async session({ session, token }) { if (session.user) { session.user.id = token.id as string; } return session; } }, secret: process.env.NEXTAUTH_SECRET, };Note: The credentials provider shown here demonstrates the concept. For production applications, integrate with a proper authentication provider (OAuth, database-backed authentication, etc.). See NextAuth providers documentation for options.
-
Create NextAuth API Route Handler (
src/app/api/auth/[...nextauth]/route.ts):typescript// src/app/api/auth/[...nextauth]/route.ts import NextAuth from "next-auth"; import { authOptions } from "@/lib/auth"; const handler = NextAuth(authOptions); export { handler as GET, handler as POST };This route handles all NextAuth requests including sign in, sign out, and callback operations at
/api/auth/*endpoints.
3. Building the Protected Plivo SMS API Route
Create the protected API endpoint that sends SMS messages via Plivo.
Create SMS API Route (src/app/api/send-sms/route.ts):
// src/app/api/send-sms/route.ts
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import plivo from "plivo";
// Initialize Plivo client
// Credentials are loaded from environment variables
const plivoClient = new plivo.Client(
process.env.PLIVO_AUTH_ID!,
process.env.PLIVO_AUTH_TOKEN!
);
// E.164 phone number validation regex
// Format: + followed by 1-15 digits (country code + subscriber number)
// Source: https://www.twilio.com/docs/glossary/what-e164
const E164_REGEX = /^\+[1-9]\d{1,14}$/;
export async function POST(request: NextRequest) {
try {
// 1. Check authentication
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json(
{ error: "Unauthorized. Sign in to send SMS." },
{ status: 401 }
);
}
// 2. Parse and validate request body
const body = await request.json();
const { to, text } = body;
// Validate required fields
if (!to || !text) {
return NextResponse.json(
{ error: "Missing required fields: 'to' and 'text'." },
{ status: 400 }
);
}
// Validate phone number format (E.164)
if (!E164_REGEX.test(to)) {
return NextResponse.json(
{
error: "Invalid 'to' phone number format. Use E.164 format (e.g., +12025551234).",
format: "E.164 format: +[country code][subscriber number]"
},
{ status: 400 }
);
}
// Validate message text
if (typeof text !== "string" || text.trim().length === 0) {
return NextResponse.json(
{ error: "Invalid 'text' field. Must be a non-empty string." },
{ status: 400 }
);
}
if (text.length > 1600) {
return NextResponse.json(
{ error: "Message text exceeds maximum length of 1600 characters." },
{ status: 400 }
);
}
// 3. Verify Plivo credentials are configured
if (!process.env.PLIVO_AUTH_ID || !process.env.PLIVO_AUTH_TOKEN || !process.env.PLIVO_SENDER_ID) {
console.error("Missing Plivo environment variables");
return NextResponse.json(
{ error: "SMS service configuration error. Contact support." },
{ status: 500 }
);
}
// 4. Send SMS via Plivo
console.log(`Sending SMS to ${to} from user ${session.user?.email}`);
const response = await plivoClient.messages.create(
process.env.PLIVO_SENDER_ID!, // src: Sender ID or Plivo number
to, // dst: Destination number in E.164 format
text // text: The content of the SMS message
);
console.log("Plivo API Response:", response);
// 5. Return success response
return NextResponse.json({
success: true,
message: "SMS sent successfully.",
details: {
messageUuid: response.messageUuid,
apiId: response.apiId
}
}, { status: 200 });
} catch (error: any) {
// Log detailed error server-side
console.error("Error sending SMS via Plivo:", {
error: error.message,
stack: error.stack,
response: error.response?.data
});
// Return generic error to client (don't expose internal details)
return NextResponse.json(
{
error: "Failed to send SMS. Try again later.",
// Include error code if available for debugging
...(error.response?.status && { statusCode: error.response.status })
},
{ status: 500 }
);
}
}Explanation:
- Authentication Check: Uses
getServerSession()from NextAuth to verify authentication before processing requests. Returns 401 Unauthorized if no valid session exists. - Input Validation:
- Validates required fields (
to,text) - Validates phone number format using E.164 standard regex:
^\+[1-9]\d{1,14}$(ITU E.164 standard) - Validates message text is non-empty and under 1600 characters
- Validates required fields (
- Plivo Client Initialization: Creates a Plivo client instance with credentials from environment variables
- SMS Sending: Calls
plivoClient.messages.create()with sender ID, destination number, and message text - Error Handling: Catches and logs errors server-side, returns generic error messages to client to avoid exposing internal details
4. Creating a React Frontend for SMS Sending
Create a basic UI to test your SMS sending functionality.
Update Home Page (src/app/page.tsx):
// src/app/page.tsx
"use client";
import { useSession, signIn, signOut } from "next-auth/react";
import { useState } from "react";
export default function Home() {
const { data: session, status } = useSession();
const [to, setTo] = useState("");
const [text, setText] = useState("");
const [loading, setLoading] = useState(false);
const [result, setResult] = useState<{ success?: boolean; message?: string; error?: string } | null>(null);
const handleSendSMS = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setResult(null);
try {
const response = await fetch("/api/send-sms", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ to, text }),
});
const data = await response.json();
if (response.ok) {
setResult({ success: true, message: data.message });
setTo("");
setText("");
} else {
setResult({ success: false, error: data.error });
}
} catch (error) {
setResult({ success: false, error: "Network error. Try again." });
} finally {
setLoading(false);
}
};
if (status === "loading") {
return (
<main className="flex min-h-screen items-center justify-center">
<p>Loading…</p>
</main>
);
}
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<div className="w-full max-w-md space-y-8">
<div className="text-center">
<h1 className="text-3xl font-bold">Plivo SMS Sender</h1>
<p className="mt-2 text-gray-600">Next.js + NextAuth + Plivo</p>
</div>
{!session ? (
<div className="space-y-4 text-center">
<p>Sign in to send SMS messages.</p>
<button
onClick={() => signIn()}
className="rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
>
Sign In
</button>
</div>
) : (
<div className="space-y-6">
<div className="rounded-lg border p-4">
<p className="text-sm text-gray-600">Signed in as</p>
<p className="font-medium">{session.user?.email}</p>
<button
onClick={() => signOut()}
className="mt-2 text-sm text-red-600 hover:text-red-700"
>
Sign Out
</button>
</div>
<form onSubmit={handleSendSMS} className="space-y-4">
<div>
<label htmlFor="to" className="block text-sm font-medium text-gray-700">
Recipient Phone Number (E.164 format)
</label>
<input
type="tel"
id="to"
value={to}
onChange={(e) => setTo(e.target.value)}
placeholder="+14155551234"
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2"
required
/>
<p className="mt-1 text-xs text-gray-500">
Format: +[country code][number]
</p>
</div>
<div>
<label htmlFor="text" className="block text-sm font-medium text-gray-700">
Message Text
</label>
<textarea
id="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Enter your message here"
rows={4}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2"
required
/>
<p className="mt-1 text-xs text-gray-500">
{text.length}/1600 characters
</p>
</div>
<button
type="submit"
disabled={loading}
className="w-full rounded-md bg-green-600 px-4 py-2 text-white hover:bg-green-700 disabled:bg-gray-400"
>
{loading ? "Sending…" : "Send SMS"}
</button>
</form>
{result && (
<div
className={`rounded-md p-4 ${
result.success
? "bg-green-50 text-green-800"
: "bg-red-50 text-red-800"
}`}
>
{result.message || result.error}
</div>
)}
</div>
)}
</div>
</main>
);
}Add Session Provider to Root Layout (src/app/layout.tsx):
// src/app/layout.tsx
import "./globals.css";
import { SessionProvider } from "@/components/SessionProvider";
export const metadata = {
title: "Plivo SMS Sender - Next.js",
description: "Send SMS with Plivo, Next.js, and NextAuth",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
);
}Create Session Provider Component (src/components/SessionProvider.tsx):
// src/components/SessionProvider.tsx
"use client";
import { SessionProvider as NextAuthSessionProvider } from "next-auth/react";
export function SessionProvider({ children }: { children: React.ReactNode }) {
return <NextAuthSessionProvider>{children}</NextAuthSessionProvider>;
}5. Testing Your Plivo SMS Integration
-
Start the Development Server:
bashnpm run devThe application will be available at
http://localhost:3000 -
Test Authentication:
- Click "Sign In" on the home page
- Use the demo credentials (username:
demo, password:demo123) - You should be redirected back to the home page, now showing the SMS form
-
Send a Test SMS:
- Enter a valid phone number in E.164 format (e.g.,
+14155551234) - Note: For Plivo trial accounts, this number must be verified in your Plivo Console under
Phone Numbers>Sandbox Numbers - Enter your message text
- Click "Send SMS"
- Check for success message and verify SMS delivery on the destination device
- Enter a valid phone number in E.164 format (e.g.,
Expected API Responses:
-
Success (200 OK):
json{ "success": true, "message": "SMS sent successfully.", "details": { "messageUuid": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "apiId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } -
Unauthorized (401):
json{ "error": "Unauthorized. Sign in to send SMS." } -
Validation Error (400 Bad Request):
json{ "error": "Invalid 'to' phone number format. Use E.164 format (e.g., +12025551234).", "format": "E.164 format: +[country code][subscriber number]" } -
Server Error (500 Internal Server Error):
json{ "error": "Failed to send SMS. Try again later." }
6. Plivo Account Setup and Configuration
Plivo Credentials and Setup:
- Auth ID & Auth Token: Required for all API authentication. Found at Plivo Console Dashboard.
- Sender ID Configuration:
- US/Canada: Must use a Plivo phone number (long code or Toll-Free) enabled for SMS. Purchase from
Phone Numbers>Buy Numbersin the Plivo Console. For US-based applications, you may also need 10DLC registration for application-to-person messaging. - Other Regions: Can use a Plivo number or pre-approved Alphanumeric Sender ID (e.g., "MyApp"). Regulations vary by country (Plivo SMS pricing page has country-specific details).
- Format: Always use E.164 format for phone numbers:
+[country code][subscriber number](e.g.,+14155551212).
- US/Canada: Must use a Plivo phone number (long code or Toll-Free) enabled for SMS. Purchase from
Trial Account Limitations:
- Plivo trial accounts can only send SMS to verified phone numbers
- Add verified numbers in Plivo Console:
Phone Numbers>Sandbox Numbers - Trial accounts include limited credits (typically $10 USD)
- Messages may include a trial account prefix
- Upgrade to a paid account to remove restrictions and send to any number globally
SMS Pricing:
According to Plivo's US SMS pricing (as of January 2025):
- Long Codes: Starting at $0.0070/SMS (outbound)
- Toll-Free: $0.0072/SMS (outbound)
- Receiving SMS: $0.0055/SMS (inbound)
Pricing varies by destination country. Check Plivo's pricing page for international rates.
7. Implementing Error Handling and Retry Logic
Enhanced Error Handling:
Implement more robust error handling for production applications:
-
Specific Error Types: Plivo SDK errors include status codes and error messages. Handle common cases:
401 Unauthorized: Invalid credentials400 Bad Request: Invalid destination number or parameters402 Payment Required: Insufficient account balance429 Too Many Requests: Rate limiting5xx Server Errors: Plivo service issues
-
Structured Logging: Use a logging library like
winstonorpinofor production:typescript// Example enhanced logging import pino from 'pino'; const logger = pino({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', }); logger.info({ action: 'send_sms', destination: to, userId: session.user?.id, timestamp: new Date().toISOString() }, 'SMS send attempt'); -
Error Tracking: Integrate services like Sentry or Datadog for production error monitoring.
Retry Logic:
Implement exponential backoff for transient failures:
async function sendSmsWithRetry(
client: plivo.Client,
src: string,
dst: string,
text: string,
maxRetries: number = 3
) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await client.messages.create(src, dst, text);
return { success: true, data: response };
} catch (error: any) {
lastError = error;
const statusCode = error.response?.status;
// Only retry on server errors (5xx) or rate limits (429)
const isRetryable = statusCode >= 500 || statusCode === 429;
if (!isRetryable || attempt === maxRetries) {
throw error;
}
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt - 1) * 1000;
console.log(`Retry attempt ${attempt}/${maxRetries} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}Note: Do not retry authentication failures (401), invalid parameters (400), or insufficient funds (402) – these require manual intervention.
8. Securing Your SMS API with Rate Limiting
Rate Limiting:
Protect your API from abuse using rate limiting. Install @upstash/ratelimit for serverless-friendly rate limiting:
npm install @upstash/ratelimit @upstash/redis// src/lib/ratelimit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
// Create rate limiter (requires Upstash Redis)
export const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "15 m"), // 10 requests per 15 minutes
analytics: true,
});Then in your API route:
// In send-sms route.ts, before authentication check
const identifier = request.ip ?? "anonymous";
const { success, limit, reset, remaining } = await ratelimit.limit(identifier);
if (!success) {
return NextResponse.json(
{ error: "Too many requests. Try again later." },
{ status: 429 }
);
}Input Sanitization:
Always validate and sanitize inputs, even for SMS text:
- Use validation libraries like
zodfor type-safe validation - Limit message length (160 chars GSM-7, 70 chars Unicode per segment)
- Strip dangerous characters if storing in databases
- Consider content filtering for compliance
Secrets Management:
For production deployments:
- Vercel: Use Environment Variables in project settings
- AWS: Use AWS Secrets Manager
- Google Cloud: Use Secret Manager
- Azure: Use Key Vault
Never commit .env.local or hardcode credentials in source code.
9. SMS Best Practices: Character Encoding and Compliance
Character Encoding:
- GSM 03.38 (7-bit): Standard SMS character set, 160 characters per segment. Includes basic Latin characters, numbers, and common symbols.
- Unicode (UCS-2): Required for emojis, non-Latin scripts (Arabic, Chinese, etc.), and special characters. Reduces capacity to 70 characters per segment.
- Automatic Handling: Plivo automatically detects encoding and segments messages appropriately. See Plivo's encoding guide for details.
Multipart Messages (Concatenation):
- Messages exceeding single segment limits are automatically split
- GSM: 153 chars per segment (7 chars reserved for concatenation headers)
- Unicode: 67 chars per segment
- Recipients' devices reassemble segments automatically
- Billing: Each segment is billed separately
E.164 Phone Number Format:
The international standard for phone numbers (ITU E.164):
- Format:
+[country code][subscriber number] - Length: Maximum 15 digits (including country code)
- Examples:
- US:
+14155551234(country code 1) - UK:
+442071234567(country code 44) - Brazil:
+551155256325(country code 55)
- US:
- Validation Regex:
^\+[1-9]\d{1,14}$(starts with +, followed by 1-15 digits, no leading zero)
Sender ID Regulations (Country-Specific):
- India: Requires registered Alphanumeric Sender IDs. Unregistered numbers may be replaced with generic codes like
VM-PLVS. Contact Plivo support for registration. - United States: Requires 10DLC registration for application-to-person (A2P) messaging. See Plivo's 10DLC guide.
- European Union: Sender ID regulations vary by country. Some require pre-registration.
- Other Countries: Regulations vary widely. Always consult Plivo's country-specific documentation before sending.
Opt-Out Handling:
In the US, TCPA regulations require honoring opt-out requests (e.g., "STOP" replies):
- Implement webhook handlers to receive incoming SMS (see our guide on two-way SMS messaging)
- Maintain an opt-out list in your database
- Check opt-out status before sending messages
- See Plivo's compliance guide for requirements
10. Optimizing SMS Performance for Production
Connection Management:
- The Plivo Node.js SDK handles HTTP connection pooling automatically
- Reuse client instances (as shown in our implementation) rather than creating new clients per request
- Next.js API routes are stateless, but module-level client initialization provides reuse within the same serverless function instance
Bulk SMS Sending:
For sending to multiple recipients:
// Plivo's bulk method - most efficient
const destinations = ['+14155551212', '+14155551213'];
const destinationString = destinations.join('<');
await plivoClient.messages.create(
process.env.PLIVO_SENDER_ID!,
destinationString,
"Bulk message text"
);Parallel Processing:
For different messages to different recipients:
const messages = [
{ to: '+14155551212', text: 'Message 1' },
{ to: '+14155551213', text: 'Message 2' }
];
const results = await Promise.all(
messages.map(msg =>
plivoClient.messages.create(
process.env.PLIVO_SENDER_ID!,
msg.to,
msg.text
)
)
);Monitoring:
- Track API response times using Next.js middleware
- Monitor error rates and delivery success
- Use Plivo Console analytics for message status tracking
- Set up alerts for unusual activity or failures
11. Troubleshooting Common Plivo SMS Issues
Common Issues:
- 401 Unauthorized: Invalid
PLIVO_AUTH_IDorPLIVO_AUTH_TOKEN. Verify credentials in Plivo Console and.env.local. - Trial Account Restrictions: "Cannot send SMS to unverified number" – Add destination numbers to
Phone Numbers>Sandbox Numbersin Plivo Console. - 402 Payment Required: Insufficient account balance. Add funds or upgrade from trial account.
- 400 Bad Request - Invalid dst: Phone number not in E.164 format. Ensure format is
+[country code][number]. - 400 Bad Request - Invalid src: Sender ID not owned or approved. Verify
PLIVO_SENDER_IDis a purchased Plivo number or registered Alphanumeric ID. - 429 Too Many Requests: Rate limiting. Implement backoff or reduce send frequency.
- Network Errors:
ECONNREFUSED,ETIMEDOUT– Check network connectivity toapi.plivo.com. Verify firewalls allow outbound HTTPS. - NextAuth Session Issues: Ensure
NEXTAUTH_SECRETis set andNEXTAUTH_URLmatches your application URL.
Debugging Tips:
- Check Server Logs: Run
npm run devand watch console output for detailed error messages - Plivo Console: Check
Logssection in Plivo Console for API call history and errors - Test Authentication: Use
/api/auth/sessionendpoint to verify NextAuth is working - Verify Environment Variables: Add console.log to verify env vars are loaded (remove before production)
- Test Phone Number Format: Use online E.164 validators to verify phone number format
Production Considerations:
- Enable HTTPS (required for NextAuth callbacks)
- Set up proper error monitoring (Sentry, Datadog)
- Implement proper logging with log aggregation
- Configure rate limiting on both API and Plivo levels
- Set up delivery status webhooks for tracking
- Implement message queuing for high-volume scenarios
- Regular security audits of authentication flow
Frequently Asked Questions (FAQ)
How do I send SMS with Plivo in Next.js 15?
Install the Plivo Node.js SDK (npm install plivo) and create an API route at app/api/send-sms/route.ts. Initialize the Plivo client with your Auth ID and Token from environment variables, then call client.messages.create() with the sender number, destination number (E.164 format), and message text. For Next.js 15 App Router, use NextRequest and NextResponse for request/response handling.
What is the difference between Next.js API routes and NextAuth protected routes?
Next.js API routes are serverless functions that handle HTTP requests at /api/* endpoints. NextAuth protected routes add authentication middleware that checks for valid user sessions before allowing access to the API endpoint. Use getServerSession() from next-auth in Next.js 13-15 App Router to verify authentication status before processing SMS requests.
How much does it cost to send SMS with Plivo?
Plivo charges per SMS segment based on the destination country. US SMS costs approximately $0.0070 per message for long codes and $0.0072 for toll-free numbers (January 2025 pricing). Messages exceeding 160 characters (GSM-7) or 70 characters (Unicode) split into multiple segments, with each segment billed separately. Check Plivo's official pricing page for current rates and volume discounts.
What is E.164 phone number format and why does Plivo require it?
E.164 is the international standard for phone numbers, formatted as + followed by country code and subscriber number (e.g., +14155551212 for US, +442071234567 for UK). Plivo requires E.164 format to ensure accurate message routing across global telecommunications networks. The format allows up to 15 digits total and excludes spaces, dashes, or parentheses. Always validate phone numbers with the regex ^\+[1-9]\d{1,14}$ before calling the Plivo API.
How do I protect my Plivo SMS endpoint with NextAuth?
Install NextAuth (npm install next-auth) and configure authentication providers in app/api/auth/[...nextauth]/route.ts. In your SMS API route, import getServerSession from next-auth, call it with your auth options, and return a 401 Unauthorized response if no session exists. This ensures only authenticated users can trigger SMS sending. See NextAuth documentation for App Router integration details.
Can I use Plivo with Next.js App Router and Server Components?
Yes, Plivo works seamlessly with Next.js 15 App Router and Server Components. Create API routes in the app/api directory as Route Handlers, which run on the server side. Server Components cannot directly call the Plivo API (they don't handle POST requests), so use Server Actions or API routes to send SMS messages from your Next.js application. The Plivo Node.js SDK is fully compatible with Next.js serverless functions.
What are the Plivo trial account limitations for SMS?
Plivo trial accounts can only send SMS to verified phone numbers that you manually add in the Plivo Console under Phone Numbers > Sandbox Numbers. Trial accounts have limited credits (typically $10) and may display a trial message prefix on outgoing SMS. Upgrade to a paid account to remove these restrictions and send to any phone number globally.
How do I handle SMS delivery failures in Next.js?
Implement error handling with try-catch blocks around client.messages.create() calls and check for specific Plivo error codes (401 for authentication, 400 for invalid numbers, 402 for insufficient funds). Log errors to monitoring services like Sentry, return appropriate HTTP status codes to clients, and optionally implement retry logic with exponential backoff for transient failures (429 rate limits, 5xx server errors). Do not retry authentication failures or invalid parameters.
What is the best way to store Plivo credentials in Next.js?
Store Plivo credentials in .env.local for development (automatically ignored by Git) and use environment variables in your hosting provider for production. Never hardcode Auth ID or Auth Token in source code. Access credentials server-side only using process.env.PLIVO_AUTH_ID and process.env.PLIVO_AUTH_TOKEN. For enhanced security, use secret management services like Vercel Environment Variables, AWS Secrets Manager, or HashiCorp Vault.
How do I send SMS verification codes with Plivo and NextAuth?
Create a Next.js API route that generates a random 6-digit code, stores it in your database (or cache like Redis) with a 10-minute expiration, and sends it via Plivo to the user's phone number. When the user submits the code, verify it matches the stored value and hasn't expired. Integrate this with NextAuth's credentials provider to enable SMS-based two-factor authentication (2FA). See NextAuth credentials provider documentation for implementation details.
Related Resources: Next.js SMS Integration & Authentication
Next.js & API Development:
- Next.js 15 Documentation – Official Next.js guide
- Next.js 15.5 Release Notes – Latest features and updates (January 2025)
- Next.js API Routes Best Practices – API development patterns
- Next.js Environment Variables Guide – Secure credential management
NextAuth Authentication:
- NextAuth.js Documentation – Complete authentication setup
- NextAuth with App Router – Next.js 13+ integration guide
- NextAuth Session Management – Client and server sessions
- NextAuth Credentials Provider – Custom authentication flows
Plivo SMS Integration:
- Plivo Node.js SDK GitHub – Source code and examples
- Plivo SMS API Documentation – Complete API reference
- Plivo Node.js SDK Documentation – Official SDK guide
- Plivo SMS Pricing Calculator – Cost estimation tool
- Plivo US SMS Pricing – Detailed US pricing ($0.0070/SMS)
Standards & Specifications:
- E.164 Phone Number Format – International numbering standard
- ITU-T E.164 Recommendation – Official specification
Security & Best Practices:
- OWASP API Security Top 10 – API security guidelines
- Plivo SMS Compliance Guide – Regulatory compliance
Master Next.js SMS integration with Plivo and NextAuth to build production-ready authenticated messaging features. Implement proper error handling, session management, and security best practices for reliable SMS delivery in your applications.
Primary Source Citations:
- Plivo Node.js SDK: https://github.com/plivo/plivo-node (Accessed January 2025)
- Plivo SMS Pricing (US): https://www.plivo.com/sms/pricing/us ($0.0070/SMS, January 2025)
- NextAuth.js: https://next-auth.js.org/ (v4.24.x, January 2025)
- Next.js 15.5: https://nextjs.org/blog/next-15-5 (Released January 2025)
- E.164 Standard: https://www.twilio.com/docs/glossary/what-e164 (ITU-T Recommendation)
- Node.js Release Schedule: https://github.com/nodejs/release#release-schedule (Node.js 18 EOL: April 30, 2025)
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 involves initializing the Plivo client with your credentials, then using the `client.messages.create()` method to send messages via the Plivo API. Ensure you have the necessary dependencies installed (`npm install express plivo dotenv`).
What is the Plivo Node.js SDK?
The Plivo Node.js SDK is a library that simplifies interaction with the Plivo communications API from your Node.js applications. It provides methods for sending SMS messages, making calls, and other communication functions, abstracting away the low-level API details.
Why does Plivo require E.164 format?
Plivo uses the E.164 format (e.g., +14155551212) for phone numbers to ensure global compatibility and accurate routing of messages. This standardized format includes the country code and full phone number, eliminating ambiguity.
When should I use Alphanumeric Sender ID?
Use an Alphanumeric Sender ID (e.g., "MyApp") as your message sender when allowed by the destination country's regulations. Check Plivo's documentation as rules vary by country. In some regions, it's an alternative to using a phone number.
Can I send bulk SMS messages with Plivo?
Yes, Plivo allows sending bulk SMS messages. You can use their API's built-in method by joining multiple destination numbers with '<' or send messages in parallel using `Promise.all` for greater control, although the former is generally more network-efficient.
How to set up a Plivo SMS project in Node.js?
Initialize a Node.js project, install Express, Plivo, and dotenv, then set up environment variables for your Plivo credentials. You'll also need to create a Plivo account, buy a Plivo number if sending to certain regions, and obtain your API keys.
What is the purpose of dotenv in a Plivo project?
Dotenv loads environment variables from a `.env` file into `process.env`, allowing you to store sensitive Plivo API credentials (Auth ID, Auth Token) securely, separate from your codebase. This crucial for security and should never be committed to version control.
Why does my Plivo SMS fail with a 400 error?
A 400 error often means an invalid request. Verify your 'to' number is in E.164 format and both 'to' and 'text' fields are correctly supplied in your API call. Also, make sure your 'from' number (Sender ID) is valid.
When should I implement SMS retry mechanisms?
Implement retry mechanisms when you encounter transient network errors (timeouts) or specific Plivo errors indicating temporary issues (like rate limiting). Use exponential backoff to wait progressively longer between retries.
How to handle Plivo SMS errors effectively?
Catch Plivo errors in a `try...catch` block and implement logging with enough context to understand the issue. Do not expose internal error details to the client; instead, log those server-side and return a generic error message to the client. Consider using dedicated logging libraries like Winston or Pino and integrate with error tracking services.
How to improve Plivo SMS sending performance?
Optimize performance when sending many messages by utilizing Plivo's bulk send feature (joining numbers with '<') or making parallel API calls using `Promise.all`. Ensure Node.js resource limits are appropriate for your workload.
What is the role of a database schema in a Plivo SMS application?
A database schema allows storing SMS logs, including recipient, sender, message, status, and any errors, creating an audit trail. This enables tracking message history, managing opt-outs, and analyzing delivery success rates.
How can I add security to my Plivo SMS integration?
Secure your setup by validating inputs, implementing rate limiting to prevent abuse, protecting your API endpoints with authentication or API keys, and storing Plivo credentials securely using environment variables or a secrets manager. Always use HTTPS in production.
How to troubleshoot Plivo SMS delivery issues?
Check common problems: invalid credentials, trial account limits, insufficient funds, incorrect 'to' and 'from' formats, rate limits, or network connectivity. Plivo's console logs can provide valuable information. Ensure your sender ID complies with destination country rules.
What is GSM encoding in SMS, and how does it affect messages?
GSM encoding is the standard 7-bit character set for SMS, allowing 160 characters per segment. Using characters outside GSM (like emojis or some non-Latin scripts) requires Unicode, reducing the segment length to 70 characters. Plivo handles this but be aware of potential cost implications for longer Unicode messages.