Vonage (formerly Nexmo) is a CPaaS alternative to Twilio offering SMS, voice calls, and 2FA — often cheaper for international SMS. In Bolt.new, use the @vonage/server-sdk in a Next.js API route to send messages. Outbound SMS works in Bolt's WebContainer preview; inbound webhooks for receiving messages require deployment to Netlify or Bolt Cloud first.
Vonage as a Twilio Alternative: SMS, Voice, and 2FA for Bolt Apps
Vonage (formerly Nexmo) is one of the major CPaaS platforms alongside Twilio and Sinch. It covers SMS, voice, video, 2FA, and number verification. For international SMS especially — Europe, Southeast Asia, Latin America — Vonage pricing is often more competitive than Twilio.
The @vonage/server-sdk is the modern unified npm package for TypeScript/JavaScript. It communicates over HTTPS, making it fully compatible with Bolt's WebContainer. Outbound SMS and voice calls work in the Bolt preview. Inbound webhooks require deployment — Bolt's browser-based runtime cannot receive incoming HTTP calls.
Vonage's Messages API v1 is the current standard for sending SMS (replacing the older SMS API). It uses JWT authentication for the most secure implementation, though Basic Auth with API key/secret is simpler to set up and sufficient for most applications.
Integration method
Vonage integrates via the @vonage/server-sdk npm package running inside a Next.js API route. Outbound SMS and voice calls require your Vonage API key and secret stored in server-side environment variables. The SDK communicates over HTTPS so it works in Bolt's WebContainer during development. Inbound webhooks for receiving messages or delivery receipts require a publicly accessible URL — test those only after deploying to Netlify or Bolt Cloud.
Prerequisites
- A Bolt.new account with a Next.js project created
- A Vonage API account at dashboard.nexmo.com (free trial includes test credits)
- A Vonage virtual phone number (required as sender for SMS in most countries)
- Your Vonage API Key and API Secret from the dashboard
- Understanding that inbound webhook testing requires deployment to a live URL
Step-by-step guide
Set Up Your Vonage Account and Get a Virtual Number
Set Up Your Vonage Account and Get a Virtual Number
Sign up at dashboard.nexmo.com for a free trial account. Vonage provides €2 in free credits on signup, which is enough to send approximately 20-30 test SMS messages depending on destination country. The dashboard shows your API Key and API Secret on the home page — copy both immediately. For sending SMS, you need a virtual phone number as the 'from' sender in most countries. Navigate to Numbers → Buy Numbers in the dashboard, choose your target country (United States numbers start around €0.90/month), and complete the purchase. The number appears in Your Numbers and should be in E.164 format (e.g., +14155550100). For the Messages API (the recommended modern API), you also need to set your inbound and status webhook URLs in the Vonage dashboard. These are used when messages are received or delivery receipts arrive. You can use placeholder URLs now and update them after deployment. Go to API Settings → Default SMS setting and switch from 'SMS API' to 'Messages API' — this affects which SDK methods work correctly.
Pro tip: Vonage's sandbox lets you test SMS to verified numbers without purchasing a virtual number. Go to dashboard → Sandbox to set up test phone numbers that receive messages for free during development.
Expected result: You have your API Key, API Secret, and a virtual phone number from the Vonage dashboard. The Messages API is set as your default SMS API.
Install the Vonage SDK and Configure Environment Variables
Install the Vonage SDK and Configure Environment Variables
The @vonage/server-sdk is the modern unified SDK for all Vonage APIs. It replaces the older nexmo npm package and provides TypeScript types for all API responses. Install it in your Bolt.new project along with its peer dependencies. The SDK supports both Basic Auth (API key + secret) and JWT authentication. For simplicity, Basic Auth is sufficient for sending SMS and most use cases. JWT is required for Voice API calls and provides better security for production apps. Start with Basic Auth and upgrade if you need Voice API functionality. Store all Vonage credentials in your .env file with no NEXT_PUBLIC_ prefix — they are server-side only secrets that must never be included in client bundles. The Vonage API secret especially should never appear in browser-accessible code.
Install the @vonage/server-sdk npm package. Create a lib/vonage.ts file that initializes a Vonage client using VONAGE_API_KEY and VONAGE_API_SECRET environment variables and exports it as a singleton. Use TypeScript.
Paste this in Bolt.new chat
1# .env file additions2# Vonage credentials — server-side only, never NEXT_PUBLIC_3VONAGE_API_KEY=your_api_key_here4VONAGE_API_SECRET=your_api_secret_here5VONAGE_FROM_NUMBER=+141555501006# For Voice API (optional — needed for making/receiving calls)7# VONAGE_APPLICATION_ID=your_application_id8# VONAGE_PRIVATE_KEY_PATH=./vonage_private_key.pemPro tip: Keep your API Secret out of any logs or error messages. Vonage API secrets have full account access including purchasing numbers and making calls — treat them with the same care as a password.
Expected result: The @vonage/server-sdk package is installed. lib/vonage.ts initializes the client without TypeScript errors. Environment variables are configured in .env.
Create the SMS Sending API Route
Create the SMS Sending API Route
Build the core API route that sends SMS messages using the Vonage Messages API. The Messages API v1 is the modern endpoint that supports SMS, MMS, WhatsApp, Viber, Facebook Messenger, and RCS from a single consistent interface — unlike the legacy SMS API which only handles basic SMS. The API route should validate the input (phone number in E.164 format, non-empty message text), call the Vonage Messages API via the SDK, and return the message UUID on success. Vonage returns a unique message_uuid for each sent message, which you can use to track delivery status via webhooks later. Phone numbers must be in E.164 format (international format with + prefix, e.g., +14155550123 for a US number). A common source of send failures is locally formatted numbers without the country code — validate and normalize numbers before sending.
Create a Next.js API route at /api/send-sms (POST) using the Vonage Messages API. It should accept 'to' (E.164 phone number), 'text' (message content), and optional 'from' (defaults to VONAGE_FROM_NUMBER env var). Validate that 'to' matches E.164 format. Return the message UUID on success. Handle Vonage API errors and return appropriate HTTP status codes.
Paste this in Bolt.new chat
1// app/api/send-sms/route.ts2import { NextResponse } from 'next/server';3import { Vonage } from '@vonage/server-sdk';4import { Auth } from '@vonage/auth';56const vonage = new Vonage(7 new Auth({8 apiKey: process.env.VONAGE_API_KEY ?? '',9 apiSecret: process.env.VONAGE_API_SECRET ?? '',10 })11);1213const E164_REGEX = /^\+[1-9]\d{6,14}$/;1415export async function POST(request: Request) {16 try {17 const { to, text, from } = await request.json();1819 if (!to || !E164_REGEX.test(to)) {20 return NextResponse.json(21 { error: 'Invalid phone number. Use E.164 format (e.g., +14155550123)' },22 { status: 400 }23 );24 }25 if (!text || text.trim().length === 0) {26 return NextResponse.json({ error: 'Message text is required' }, { status: 400 });27 }28 if (text.length > 1600) {29 return NextResponse.json({ error: 'Message exceeds 1600 character limit' }, { status: 400 });30 }3132 const fromNumber = from ?? process.env.VONAGE_FROM_NUMBER;33 if (!fromNumber) {34 return NextResponse.json({ error: 'No sender number configured' }, { status: 500 });35 }3637 const response = await vonage.messages.send({38 message_type: 'text',39 to,40 from: fromNumber,41 channel: 'sms',42 text: text.trim(),43 });4445 return NextResponse.json({46 success: true,47 message_uuid: response.message_uuid,48 });49 } catch (error: unknown) {50 console.error('Vonage SMS error:', error);51 const vonageError = error as { response?: { data?: { title?: string; detail?: string } } };52 const detail = vonageError.response?.data?.detail ?? 'Failed to send SMS';53 return NextResponse.json({ error: detail }, { status: 500 });54 }55}Pro tip: SMS messages longer than 160 characters (or 70 characters if they contain Unicode) are automatically split into multiple parts and charged per part. Long messages are concatenated on the recipient's phone but billed separately.
Expected result: The /api/send-sms endpoint accepts POST requests and sends SMS via Vonage. Test with a curl or Postman request using a verified phone number during development.
Add Inbound Webhook Handling (Deployment Required)
Add Inbound Webhook Handling (Deployment Required)
To receive incoming SMS messages or delivery receipts, you need webhook endpoints that Vonage can call when events occur. Vonage sends a POST request to your registered webhook URL with the message content or delivery status. During development in Bolt's WebContainer, these webhooks cannot be tested — Bolt's browser-based runtime cannot receive incoming HTTP connections from external servers like Vonage. This is a fundamental WebContainer limitation: the runtime virtualizes networking within the browser but cannot expose ports to the public internet. Build the webhook handler routes now, but register the webhook URLs in the Vonage dashboard only after deploying. After deployment, go to Vonage Dashboard → API Settings → Default SMS Setting → Messages webhooks, and update the Inbound URL and Status URL to your deployed endpoint URLs. For local development testing before deploying, you can use tools like ngrok to expose a local port — but that requires running Next.js outside Bolt. The practical approach is to deploy early and test webhooks on the deployed URL.
Create two Next.js API routes for Vonage webhooks: 1) /api/webhooks/vonage-inbound (POST) that parses an incoming SMS webhook from Vonage and logs the from number, to number, message text, and timestamp. Return HTTP 200 immediately. 2) /api/webhooks/vonage-status (POST) that handles delivery receipt webhooks and logs the message_uuid and status. Both should return 200 quickly since Vonage retries if it doesn't receive a response within 10 seconds.
Paste this in Bolt.new chat
1// app/api/webhooks/vonage-inbound/route.ts2import { NextResponse } from 'next/server';34export async function POST(request: Request) {5 try {6 const payload = await request.json();7 // Vonage Messages API inbound SMS payload shape8 const {9 from, // sender phone number10 to, // your Vonage virtual number11 text, // message content12 timestamp, // ISO timestamp13 message_uuid,14 } = payload;1516 console.log('Inbound SMS:', { from, to, text, timestamp, message_uuid });1718 // Process the message here:19 // - Save to database20 // - Trigger auto-reply21 // - Route to support ticket2223 // IMPORTANT: Always return 200 quickly24 // Vonage retries delivery if no 200 within 10 seconds25 return NextResponse.json({ status: 'received' }, { status: 200 });26 } catch (error) {27 console.error('Inbound webhook error:', error);28 // Still return 200 to prevent Vonage from retrying29 return NextResponse.json({ status: 'error' }, { status: 200 });30 }31}Pro tip: Always return HTTP 200 from webhook handlers immediately, even if your processing fails. If Vonage doesn't receive a 200 within 10 seconds, it retries the webhook, which can cause duplicate processing. Handle errors asynchronously.
Expected result: Webhook routes are created. After deploying and registering the URLs in the Vonage dashboard, incoming SMS messages appear in your server logs.
Common use cases
International SMS Notifications for SaaS Apps
Send order confirmations, shipping alerts, or account notifications to users in international markets where SMS delivery rates are high and email open rates are low. Vonage's international routing can be cheaper than Twilio for European and Asian phone numbers.
Add Vonage SMS notifications to my Next.js app. Create an API route at /api/send-sms that accepts a 'to' phone number (E.164 format), a 'message' text, and sends it using the Vonage Messages API with @vonage/server-sdk. Store VONAGE_API_KEY, VONAGE_API_SECRET, and VONAGE_FROM_NUMBER in environment variables. Return the message UUID on success.
Copy this prompt to try it in Bolt.new
Two-Factor Authentication via SMS OTP
Implement SMS-based 2FA using Vonage Verify API, which handles OTP generation, delivery, retries, and verification in a single API with built-in fraud protection. This is faster to build than rolling your own OTP logic and handles carrier-level delivery optimization automatically.
Implement two-factor authentication using the Vonage Verify API. Create two API routes: /api/auth/send-otp that takes a phone number and starts a Vonage Verify v2 workflow, and /api/auth/verify-otp that takes the request_id and the code entered by the user and verifies it. Show appropriate error messages for incorrect codes and expired verification requests.
Copy this prompt to try it in Bolt.new
Outbound Voice Calls for Appointment Reminders
Make automated voice calls to remind customers of upcoming appointments, using text-to-speech to read the appointment details. Vonage's Voice API accepts NCCO (Nexmo Call Control Objects) — JSON arrays defining what the call should do — enabling complex call flows without recording audio files.
Build an appointment reminder system using the Vonage Voice API. Create an API route that makes an outbound call to a phone number, uses text-to-speech to say the appointment details, and returns the call UUID. The NCCO should say 'This is a reminder for your appointment tomorrow at [time]. Press 1 to confirm or 2 to cancel.' Handle the keypress webhook to update the appointment status.
Copy this prompt to try it in Bolt.new
Troubleshooting
'Non-Whitelisted Destination' error when sending SMS
Cause: Vonage trial accounts can only send SMS to verified phone numbers (numbers you've explicitly added to a whitelist in the dashboard). Attempting to send to unverified numbers fails with this error.
Solution: Go to Vonage Dashboard → Settings → Test Numbers and add the phone number you want to test with. Alternatively, upgrade to a paid account to send to any number.
Messages API returns 'Authentication failed' despite correct API key and secret
Cause: The Messages API requires a different authentication method than the legacy SMS API. If you have a Vonage Application with JWT auth configured, the Messages API expects JWT, not Basic Auth credentials.
Solution: For Basic Auth, ensure you have NOT configured a Vonage Application ID in your SDK initialization. Use only apiKey and apiSecret. For JWT auth (required for Voice API), use the applicationId and privateKey fields instead.
1// Basic Auth only (for SMS):2const vonage = new Vonage(new Auth({3 apiKey: process.env.VONAGE_API_KEY ?? '',4 apiSecret: process.env.VONAGE_API_SECRET ?? '',5}));Inbound webhooks not being received on deployed app
Cause: The webhook URL is not registered in the Vonage dashboard, or the URL was registered with http:// instead of https://, or the webhook URL points to the Bolt preview instead of the deployed URL.
Solution: Go to Vonage Dashboard → API Settings → Default SMS setting (or your Application settings for Voice). Update both the Inbound URL and Status URL to use your exact deployed HTTPS URL. Verify the routes return 200 by calling them manually first.
'The request body is invalid' when calling the Messages API
Cause: The Messages API v1 requires a specific JSON shape with message_type, channel, to, from, and the content field matching the message_type. Mixing up field names from the legacy SMS API causes this error.
Solution: Ensure your message object includes all required fields: message_type: 'text', channel: 'sms', to, from, and text. The text field name is specific to SMS — other channels use different content field names.
1await vonage.messages.send({2 message_type: 'text', // required3 channel: 'sms', // required4 to: '+14155550123', // E.164 format5 from: '+14155550100', // your Vonage number6 text: 'Your message', // the content7});Best practices
- Store VONAGE_API_KEY and VONAGE_API_SECRET in server-side environment variables only — never in NEXT_PUBLIC_ prefixed variables or client-side code
- Validate phone numbers in E.164 format before sending — locally formatted numbers without country codes are a common source of delivery failures
- Return HTTP 200 from webhook handlers immediately, then process asynchronously — Vonage retries webhooks that don't receive 200 within 10 seconds
- Test outbound SMS in Bolt's WebContainer preview (it works), but register and test inbound webhooks only after deploying to a live URL
- Use Vonage's Sandbox environment for development testing — it supports test numbers without purchasing credits
- For production apps, implement idempotency by storing message_uuid in your database to detect and ignore duplicate webhook deliveries
- Compare Vonage vs Twilio pricing per country for your target markets — Vonage is often 30-50% cheaper for European and Asian numbers
Alternatives
Twilio has a larger ecosystem, more detailed documentation, and better US pricing, making it the default choice for US-focused apps and developers who value extensive community support.
Sinch specializes in high-volume messaging with carrier-direct connections and competitive bulk SMS pricing for enterprises sending millions of messages per month.
Plivo offers similar SMS and voice APIs with pricing that's often lower than both Twilio and Vonage, making it worth comparing for cost-sensitive high-volume use cases.
Frequently asked questions
How do I connect Bolt.new to Vonage (Nexmo)?
Install @vonage/server-sdk, add your VONAGE_API_KEY and VONAGE_API_SECRET to your .env file (server-side only), and create a Next.js API route that initializes the Vonage client and calls vonage.messages.send(). Outbound SMS works immediately in Bolt's WebContainer preview. For inbound messages and delivery receipts, deploy to Netlify or Bolt Cloud and register your webhook URLs in the Vonage dashboard.
Is Vonage cheaper than Twilio for international SMS?
It depends on the destination country. Vonage is frequently cheaper than Twilio for European Union countries, Southeast Asia, and Latin America. Twilio is sometimes cheaper for US and Canadian numbers. Always compare current pricing on both dashboards for your specific target countries before committing — prices change frequently.
Can I receive inbound SMS in Bolt's WebContainer preview?
No. Receiving inbound SMS requires Vonage to POST to a webhook URL on your server. Bolt's WebContainer runs inside a browser and cannot receive incoming HTTP connections from external services. You must deploy to Netlify or Bolt Cloud, register the deployed HTTPS URL in the Vonage dashboard, and test inbound messaging on the live site.
What's the difference between the Vonage SMS API and Messages API?
The SMS API is the legacy endpoint — simpler but limited to SMS only. The Messages API v1 is the modern endpoint supporting SMS, MMS, WhatsApp, Facebook Messenger, Viber, and RCS from a single consistent interface. Use the Messages API for new integrations. The @vonage/server-sdk's vonage.messages.send() method uses the Messages API. Ensure your Vonage dashboard is set to 'Messages API' (not 'SMS API') in the default settings.
Does Bolt.new work with Vonage Voice API for phone calls?
Yes. The Voice API uses JWT authentication instead of Basic Auth, requiring a Vonage Application with a private key. Create a Vonage Application in the dashboard, download the private key, and initialize the SDK with applicationId and privateKey. Making outbound calls works in Bolt's WebContainer preview, but receiving inbound calls requires a registered answer URL on a deployed server.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation