To integrate Replit with Vonage (formerly Nexmo), create a Vonage API account, store your API key and secret in Replit Secrets (lock icon π), and use the Vonage Messages API or server SDKs from Python or Node.js to send SMS, MMS, and WhatsApp messages. Use Autoscale deployment for webhook-based two-way messaging apps.
Why Connect Replit to Vonage (Nexmo)?
Vonage provides one of the most developer-friendly programmable messaging APIs in the industry. Originally known as Nexmo before being acquired by Vonage (now part of Ericsson), the platform combines simple SMS APIs with advanced omnichannel messaging capabilities β SMS, MMS, WhatsApp Business, Facebook Messenger, Viber, and voice β all through a single unified Messages API. This makes it particularly compelling for applications that need to reach users through multiple channels from a single Replit backend.
Common integration patterns include sending OTP (one-time password) verification codes using the Vonage Verify API, building two-way SMS chatbots where users text a virtual number to interact with your app, sending transactional notifications (order confirmations, appointment reminders, shipping updates), and handling WhatsApp Business messages for customer support or marketing.
Replit's Secrets system (lock icon π in the sidebar) keeps your Vonage API key and secret secure. The API secret is equivalent to a password for your Vonage account β it grants the ability to send messages and incur charges. Store VONAGE_API_KEY and VONAGE_API_SECRET in Replit Secrets, and access them with os.environ['VONAGE_API_KEY'] in Python or process.env.VONAGE_API_KEY in Node.js. For the Messages API, you also need a private key for JWT signing β store this as VONAGE_PRIVATE_KEY in Secrets.
Integration method
You connect Replit to Vonage by creating a Vonage API account, obtaining your API key and API secret from the dashboard, and storing them in Replit Secrets. Your server-side Python or Node.js code calls the Vonage Messages API or SMS API using the key and secret for authentication. For advanced features like WhatsApp and the Messages API, you also create a Vonage application and use JWT authentication. Inbound messages and delivery receipts arrive via webhooks to your deployed Replit app.
Prerequisites
- A Replit account with a Python or Node.js project created
- A Vonage API account (free trial available with test credits)
- A verified phone number for testing in your Vonage account
- Basic familiarity with REST APIs and webhook endpoints
- Node.js 18+ or Python 3.10+ (both available on Replit by default)
Step-by-step guide
Create a Vonage Account and Find Your API Credentials
Create a Vonage Account and Find Your API Credentials
Go to developer.vonage.com and click 'Sign Up'. After completing registration, you will land on the Vonage API Dashboard. Your API key and API secret are displayed prominently on the dashboard home page β they look like '12345678' (API key) and 'AbCdEfGhIjKlMnOp' (API secret). Copy both values immediately. Your Vonage trial account comes with a small amount of free test credits (typically β¬2 EUR) that you can use to send test SMS messages to the phone numbers you verify in your account. On a trial account, you can only send SMS to phone numbers you have explicitly verified. For the basic SMS API, your API key and secret are all you need. For the Messages API (WhatsApp, Facebook Messenger, and other channels), you also need to create a Vonage Application: 1. In the Vonage Dashboard, go to 'Applications' in the left sidebar. 2. Click 'Create a new application'. 3. Give it a name and toggle 'Messages' capability to On. 4. Set a Messages inbound URL and status URL β for development use a placeholder like https://example.com/inbound; you will update this after deployment. 5. Click 'Generate public and private key' β this downloads a private.key file. Store this key content in Replit Secrets. Note your Application ID from the Applications page β it is a UUID like 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.
Pro tip: On the Vonage trial account, you can only send SMS to verified phone numbers. Go to Numbers > Test Numbers in the dashboard to add and verify your test phone number. This restriction is lifted when you add billing.
Expected result: You have your Vonage API key, API secret, Application ID, and private key content copied and ready to store in Replit Secrets.
Store Vonage Credentials in Replit Secrets
Store Vonage Credentials in Replit Secrets
Open your Replit project and click the lock icon π in the left sidebar to open the Secrets pane. Add the following secrets: - Key: VONAGE_API_KEY β Value: your 8-digit API key - Key: VONAGE_API_SECRET β Value: your API secret - Key: VONAGE_APPLICATION_ID β Value: your Application UUID (for Messages API) - Key: VONAGE_PRIVATE_KEY β Value: the full contents of your private.key file (including the -----BEGIN PRIVATE KEY----- header and footer) - Key: VONAGE_BRAND_NUMBER β Value: your Vonage virtual number for sending (e.g., +15551234567) For the VONAGE_PRIVATE_KEY secret, paste the entire multi-line private key content as a single secret value. Replit's Secrets support multi-line values β they are stored and injected exactly as entered. In Python, access the private key with os.environ['VONAGE_PRIVATE_KEY']. The Vonage Python SDK accepts the key as a string or a file path. In Node.js, use process.env.VONAGE_PRIVATE_KEY. When writing the key to a temporary file for SDK initialization, use Python's tempfile module or write to /tmp/ on Replit.
Pro tip: Store your Vonage virtual number (from_number) as VONAGE_BRAND_NUMBER in Secrets. This is the number your recipients see as the sender. You can find it in the Vonage Dashboard under Numbers > Your Numbers.
Expected result: Five Secrets appear in the Replit Secrets pane: VONAGE_API_KEY, VONAGE_API_SECRET, VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY, and VONAGE_BRAND_NUMBER.
Send SMS Messages with Python
Send SMS Messages with Python
The Vonage SMS API is the simplest way to send text messages. Install the Vonage Python SDK with pip install vonage or use direct HTTP requests. The SDK simplifies authentication and response handling. For SMS, Vonage uses Basic Auth via the API key and secret β no JWT required. The SMS API endpoint is https://rest.nexmo.com/sms/json. Phone numbers must be in E.164 format (international format with + prefix, e.g., +14155552671). The Vonage Verify API is also demonstrated below β it is the recommended approach for 2FA because Vonage handles OTP generation, delivery, retry logic (first SMS, then voice call if undelivered), and code expiry. You never store or compare OTP codes in your own database.
1import os2import vonage3from vonage.errors import ClientError45# Initialize Vonage client with API key and secret6client = vonage.Client(7 key=os.environ["VONAGE_API_KEY"],8 secret=os.environ["VONAGE_API_SECRET"]9)1011sms = vonage.Sms(client)1213def send_sms(to_number: str, message: str, from_number: str = None) -> dict:14 """15 Send an SMS message.16 to_number: E.164 format, e.g., '+14155552671'17 Returns response dict with status and message_id.18 """19 sender = from_number or os.environ.get("VONAGE_BRAND_NUMBER", "Vonage")20 response = sms.send_message({21 "from": sender,22 "to": to_number.replace('+', ''), # Vonage SMS API omits the +23 "text": message,24 "type": "unicode" # Supports emojis and non-ASCII characters25 })26 if response["messages"][0]["status"] == "0":27 return {28 "success": True,29 "message_id": response["messages"][0]["message-id"],30 "remaining_balance": response["messages"][0].get("remaining-balance")31 }32 else:33 error = response["messages"][0]["error-text"]34 raise ValueError(f"SMS failed: {error} (status {response['messages'][0]['status']})")3536# --- Vonage Verify API for 2FA ---3738verify = vonage.Verify(client)3940def start_verification(phone_number: str, brand: str = "MyApp") -> str:41 """42 Start 2FA verification. Returns request_id to use in check_verification.43 phone_number: E.164 format without +, e.g., '14155552671'44 """45 response = verify.start_verification({46 "number": phone_number,47 "brand": brand,48 "code_length": "6",49 "lg": "en-us"50 })51 if response["status"] == "0":52 return response["request_id"]53 raise ValueError(f"Verification failed: {response.get('error_text')}")5455def check_verification(request_id: str, code: str) -> bool:56 """57 Check a verification code entered by the user.58 Returns True if code is correct, raises on failure.59 """60 response = verify.check({61 "request_id": request_id,62 "code": code63 })64 if response["status"] == "0":65 return True66 # Status 16 = already verified, 17 = too many attempts67 error_msg = response.get("error_text", "Verification failed")68 raise ValueError(f"Code check failed: {error_msg} (status {response['status']})")6970def cancel_verification(request_id: str) -> bool:71 """Cancel an ongoing verification request."""72 response = verify.cancel(request_id)73 return response.get("status") == "0"7475# Example usage76if __name__ == "__main__":77 # Test SMS send (replace with a verified number on trial accounts)78 result = send_sms("+14155550100", "Hello from Replit + Vonage!")79 print(f"SMS sent: {result['message_id']}")80 print(f"Balance remaining: {result.get('remaining_balance', 'N/A')}")Pro tip: Use type: 'unicode' for SMS messages that contain emojis or non-Latin characters. Unicode messages are limited to 70 characters per SMS segment (vs 160 for standard ASCII), so they incur higher costs for long messages.
Expected result: Running the script sends a test SMS to your verified phone number and prints the Vonage message ID and remaining account balance.
Send WhatsApp Messages with the Messages API (Node.js)
Send WhatsApp Messages with the Messages API (Node.js)
The Vonage Messages API supports WhatsApp Business messages, and it uses JWT (JSON Web Token) authentication rather than API key/secret. The JWT is generated by signing a payload with your Application ID and private key. The Vonage Node.js SDK handles JWT generation automatically when you provide the Application ID and private key. Install the SDK with npm install @vonage/server-sdk. For the Messages API, you need a WhatsApp Business number linked to your Vonage application β this requires a Meta Business account and WhatsApp Business approval. During development and testing, Vonage provides a sandbox where you can send test WhatsApp messages to pre-registered test numbers. The Express server below handles both outbound WhatsApp message sending and receiving inbound messages via webhook. The private key is read from the VONAGE_PRIVATE_KEY environment Secret and written to a temporary file for the SDK to read.
1const express = require('express');2const { Vonage } = require('@vonage/server-sdk');3const fs = require('fs');4const path = require('path');5const os = require('os');67const app = express();8app.use(express.json());910// Write private key from Replit Secret to a temp file for the SDK11const privateKeyContent = process.env.VONAGE_PRIVATE_KEY;12const privateKeyPath = path.join(os.tmpdir(), 'vonage_private.key');13fs.writeFileSync(privateKeyPath, privateKeyContent);1415const vonage = new Vonage({16 apiKey: process.env.VONAGE_API_KEY,17 apiSecret: process.env.VONAGE_API_SECRET,18 applicationId: process.env.VONAGE_APPLICATION_ID,19 privateKey: privateKeyPath20});2122const FROM_NUMBER = process.env.VONAGE_BRAND_NUMBER; // Your Vonage virtual number2324// Send an SMS via the basic SMS API25app.post('/send-sms', async (req, res) => {26 const { to, text } = req.body;27 if (!to || !text) return res.status(400).json({ error: 'to and text are required' });2829 try {30 const response = await vonage.sms.send({31 from: FROM_NUMBER,32 to: to.replace('+', ''),33 text34 });35 const msg = response.messages[0];36 if (msg.status === '0') {37 res.json({ success: true, messageId: msg['message-id'] });38 } else {39 res.status(400).json({ error: msg['error-text'] });40 }41 } catch (err) {42 console.error('SMS error:', err);43 res.status(500).json({ error: err.message });44 }45});4647// Send a WhatsApp message via the Messages API48app.post('/send-whatsapp', async (req, res) => {49 const { to, text } = req.body;50 if (!to || !text) return res.status(400).json({ error: 'to and text are required' });5152 try {53 const response = await vonage.messages.send({54 message_type: 'text',55 to: to.replace('+', ''),56 from: FROM_NUMBER,57 channel: 'whatsapp',58 text59 });60 res.json({ success: true, messageUuid: response.message_uuid });61 } catch (err) {62 console.error('WhatsApp error:', err.response?.data);63 res.status(500).json({ error: err.message });64 }65});6667// Webhook: receive inbound SMS/WhatsApp messages68app.post('/webhooks/inbound', (req, res) => {69 const { from, to, text, channel, message_uuid } = req.body;70 console.log(`Inbound ${channel || 'SMS'} from ${from} to ${to}: ${text}`);71 // Process message: save to database, trigger chatbot, route to agent, etc.72 res.status(200).json({ received: true });73});7475// Webhook: delivery receipt status updates76app.post('/webhooks/status', (req, res) => {77 const { to, status, message_uuid } = req.body;78 console.log(`Delivery status for ${message_uuid}: ${status}`);79 // Update message status in database80 res.status(200).json({ received: true });81});8283app.listen(3000, '0.0.0.0', () => {84 console.log('Vonage integration server running on port 3000');85});Pro tip: WhatsApp Business messages in production require a pre-approved template (called a Message Template) for outbound messages to users who have not messaged you first in the past 24 hours. During the WhatsApp sandbox testing period, you can send free-form messages without templates.
Expected result: The server starts on port 3000. A POST to /send-sms with a verified phone number and text body sends an SMS and returns the Vonage message ID.
Configure Webhooks and Deploy
Configure Webhooks and Deploy
Vonage needs to reach your Replit app to deliver inbound messages and delivery receipt callbacks. These webhooks require a publicly accessible URL β meaning your app must be deployed, not just running in development mode. Deploy your Replit app by clicking 'Deploy' in the toolbar. Choose Autoscale deployment for messaging apps where traffic varies throughout the day. Your app gets a stable URL at https://your-app.replit.app. Once deployed, update your Vonage Application's webhook URLs: 1. Go to Vonage Dashboard > Applications > click your application. 2. In the Messages capability section, set the Inbound URL to: https://your-app.replit.app/webhooks/inbound 3. Set the Status URL to: https://your-app.replit.app/webhooks/status 4. Click 'Save changes'. For the SMS API (non-Messages API), go to Vonage Dashboard > Account > Settings > API Settings and set the Inbound SMS webhook URL to: https://your-app.replit.app/webhooks/inbound Vonage sends delivery receipts via POST requests and expects your endpoints to respond with 200 OK within 3 seconds. If your endpoint times out or returns a non-200 status, Vonage retries the webhook up to 5 times with exponential backoff.
1from flask import Flask, request, jsonify2import os34app = Flask(__name__)56@app.route('/webhooks/inbound', methods=['POST', 'GET'])7def inbound_sms():8 """Receive inbound SMS messages from Vonage."""9 # Vonage can send via POST (JSON or form) or GET (query params)10 if request.method == 'POST':11 data = request.json or request.form12 else:13 data = request.args1415 from_number = data.get('msisdn') or data.get('from')16 to_number = data.get('to')17 message_text = data.get('text') or data.get('message', {}).get('content', {}).get('text', '')18 message_id = data.get('messageId') or data.get('message_uuid')1920 print(f"Inbound SMS from {from_number} to {to_number}: {message_text}")21 print(f"Message ID: {message_id}")2223 # Process: save to database, trigger autoresponder, etc.24 # Example: echo back25 # send_sms(from_number, f'You said: {message_text}')2627 return jsonify({'received': True}), 2002829@app.route('/webhooks/status', methods=['POST'])30def delivery_receipt():31 """Receive delivery status updates for sent messages."""32 data = request.json or request.form33 message_id = data.get('messageId') or data.get('message_uuid')34 status = data.get('status')35 error_code = data.get('err-code', '0')3637 print(f"Delivery receipt: {message_id} β status: {status}, error: {error_code}")38 # Update message status in database3940 return jsonify({'received': True}), 2004142if __name__ == '__main__':43 app.run(host='0.0.0.0', port=3000)Pro tip: Vonage's inbound SMS webhook can arrive as either a GET or POST request depending on your account settings. Accept both methods in your endpoint to handle either configuration β use request.args for GET parameters and request.json or request.form for POST.
Expected result: After deployment and webhook URL configuration, inbound SMS messages sent to your Vonage number appear in your app's console output with the sender's number and message text.
Common use cases
Two-Factor Authentication (2FA) with SMS OTP
Add SMS-based two-factor authentication to your Replit app using the Vonage Verify API. When a user logs in, your server requests a verification code that Vonage sends to their phone number. The user enters the code in your app, and you verify it against Vonage's Verify API β no need to manage OTP generation or expiry logic yourself.
Build a Flask app with POST /verify/start that takes a phone number and triggers a Vonage Verify request, and POST /verify/confirm that takes the request_id and code and validates it against the Vonage Verify API, using VONAGE_API_KEY and VONAGE_API_SECRET from Replit Secrets.
Copy this prompt to try it in Replit
Transactional SMS Notifications
Send automated SMS notifications to users when important events occur in your app β order confirmations, appointment reminders, shipping updates, or account alerts. The Vonage SMS API sends messages within seconds and provides delivery receipts so you can confirm messages were received.
Create an Express endpoint POST /notify that accepts a phone number and message text, sends an SMS via the Vonage SMS API using credentials from Replit Secrets, stores the message ID and status in a PostgreSQL database, and returns a confirmation with the Vonage message ID.
Copy this prompt to try it in Replit
WhatsApp Business Messaging Integration
Build a customer support or notification system that sends and receives WhatsApp messages using the Vonage Messages API. Users can message your WhatsApp Business number, and your Replit app processes the conversation, routes to a support agent, or responds automatically using a chatbot workflow.
Build a Flask server that receives inbound WhatsApp messages via a Vonage webhook at /webhooks/inbound, parses the sender number and message text, stores the conversation in a database, and sends an auto-reply via the Vonage Messages API using a WhatsApp Business number.
Copy this prompt to try it in Replit
Troubleshooting
SMS send returns status '5' with error 'Internal Error'
Cause: Status code 5 from the Vonage SMS API indicates an internal routing error or an issue with the destination number. On trial accounts, this often means you are trying to send to a non-verified phone number.
Solution: On Vonage trial accounts, you can only send to phone numbers you have verified in the dashboard under Numbers > Test Numbers. Add and verify your test number. Status 5 on paid accounts usually indicates a routing issue β retry the request, as it often succeeds on a second attempt.
1# Python: handle all non-zero status codes2if response['messages'][0]['status'] != '0':3 status_code = response['messages'][0]['status']4 error_text = response['messages'][0].get('error-text', 'Unknown error')5 # Status codes: 1=throttled, 2=missing params, 3=invalid params,6 # 4=invalid credentials, 5=internal error, 6=unroutable, 7=blocked7 print(f"SMS failed (status {status_code}): {error_text}")Messages API returns 401 β 'Invalid token' for WhatsApp sends
Cause: The JWT token for Messages API authentication is invalid, expired, or was generated with the wrong Application ID or private key. JWTs for Vonage expire after a short period (typically 15 minutes by default).
Solution: Verify that VONAGE_APPLICATION_ID matches the UUID of the application linked to your Messages capability. Ensure VONAGE_PRIVATE_KEY contains the complete private key content including the header and footer lines. The Vonage SDK generates fresh JWTs per request automatically if credentials are configured correctly.
1# Python: verify private key is correctly loaded2private_key = os.environ['VONAGE_PRIVATE_KEY']3if not private_key.startswith('-----BEGIN'):4 raise ValueError('Private key must include -----BEGIN PRIVATE KEY----- header')5print(f'Private key loaded: {len(private_key)} characters')Inbound webhooks are never received despite messages being sent to the Vonage number
Cause: The webhook URL in the Vonage Dashboard points to the development Replit URL (which goes offline when the browser tab is closed) or was set before the app was deployed.
Solution: Deploy your Replit app first to get a stable URL (https://your-app.replit.app), then update the webhook URLs in Vonage Dashboard > Applications > your app. For SMS webhooks, also update the SMS inbound URL in Account > Settings > API Settings. Test by sending a text message to your Vonage number.
Phone number format causes 400 error β 'Invalid to address'
Cause: Vonage requires phone numbers in E.164 format without the leading + sign for the SMS API. Numbers formatted with spaces, dashes, parentheses, or the + prefix cause validation errors.
Solution: Strip all non-numeric characters except the country code from phone numbers before sending. Use a helper function to normalize numbers to E.164 format (digits only, starting with country code).
1import re23def normalize_phone(phone: str) -> str:4 """Convert any phone format to Vonage-compatible E.164 without +."""5 # Remove all non-digit characters except leading +6 digits = re.sub(r'[^\d]', '', phone)7 # Remove leading + if present (Vonage SMS API doesn't use it)8 return digits.lstrip('+')910# Examples:11# '+1 (415) 555-0100' -> '14155550100'12# '+44 7911 123456' -> '447911123456'Best practices
- Always store VONAGE_API_KEY, VONAGE_API_SECRET, and VONAGE_PRIVATE_KEY in Replit Secrets (lock icon π) β never in source code or Git history.
- Normalize phone numbers to E.164 format (country code + digits, no + or spaces) before sending β Vonage rejects inconsistently formatted numbers.
- Use the Vonage Verify API for 2FA instead of implementing your own OTP system β it handles code generation, SMS delivery, voice fallback, and expiry automatically.
- For WhatsApp messages to users who have not messaged you in the past 24 hours, use approved WhatsApp Message Templates to comply with Meta's commerce policies.
- Implement idempotency in your inbound message webhook handler β Vonage may deliver the same message multiple times if your endpoint returns non-200 or times out.
- Deploy with Autoscale for messaging apps where traffic peaks during business hours; use Reserved VM only if you process time-critical inbound messages that cannot tolerate cold-start delays.
- Monitor your Vonage account balance via the API and set up low-balance alerts in the dashboard β SMS messages will silently fail when credits are exhausted on prepaid accounts.
- Use the type: 'unicode' parameter for SMS messages with emojis or non-ASCII characters, and be aware that Unicode messages have shorter per-segment limits (70 chars vs 160).
Alternatives
Twilio has a larger developer ecosystem, more native integrations, and broader feature set including programmable voice and video, making it a better choice for complex unified communications platforms rather than high-volume SMS alone.
Bandwidth owns its own carrier network which provides lower per-message costs at high volume, making it a better choice for enterprise deployments with millions of monthly messages where unit economics are critical.
SendGrid specializes in email delivery rather than SMS and messaging, making it a better fit when your primary communication channel is transactional email rather than text messages.
Frequently asked questions
How do I store my Vonage API key in Replit?
Click the lock icon π in the left sidebar of your Replit project to open the Secrets pane. Add VONAGE_API_KEY with your 8-digit API key and VONAGE_API_SECRET with your API secret β both found on the Vonage Dashboard home page. For the Messages API, also add VONAGE_APPLICATION_ID and VONAGE_PRIVATE_KEY (the full private key file contents). Access them in Python with os.environ['VONAGE_API_KEY'] and in Node.js with process.env.VONAGE_API_KEY.
Can I use Vonage with Replit on a free trial account?
Yes. Vonage trial accounts include free test credits (approximately β¬2 EUR) that allow you to send SMS messages to phone numbers you verify in the dashboard. The API access is the same on trial and paid accounts β the only restriction is that trial accounts can only send to verified phone numbers. Upgrade to a paid account to send to any number without verification.
What is the difference between the Vonage SMS API and the Messages API?
The SMS API (at rest.nexmo.com/sms/json) handles SMS messages only and uses simple API key/secret authentication. The Messages API (at api.nexmo.com/v1/messages) is newer, supports multiple channels (SMS, WhatsApp, Facebook Messenger, Viber), and uses JWT authentication with an Application ID and private key. Use the SMS API for straightforward SMS sending and the Messages API when you need multi-channel support.
How do I receive inbound SMS messages in my Replit app?
Deploy your Replit app to get a stable URL (https://your-app.replit.app), then go to Vonage Dashboard > Account > Settings > API Settings and set the 'Inbound SMS' webhook URL to your deployed endpoint (e.g., https://your-app.replit.app/webhooks/inbound). Vonage will POST JSON payloads to this URL whenever someone texts your Vonage number. Your endpoint must respond with 200 OK within 3 seconds.
Does Vonage support WhatsApp Business messaging from Replit?
Yes. The Vonage Messages API supports WhatsApp Business messages. You need a WhatsApp Business account linked to a Meta Business account, which must go through Meta's approval process. Once approved, link your WhatsApp number to a Vonage Application. Vonage provides a WhatsApp sandbox for testing before production approval. Use JWT authentication (Application ID + private key) for Messages API calls.
How do I send an SMS to multiple recipients from Replit?
The Vonage SMS API sends one message per API call β there is no native batch send endpoint. To send to multiple recipients, loop through your list and call the send function for each number, with a small delay between calls to avoid hitting rate limits. For campaigns with thousands of recipients, consider using Vonage's Dispatch API or batching requests with a queue to manage throughput.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation