code examples
code examples
Vonage WhatsApp API Node.js Integration: Complete Express.js Tutorial [2025]
Learn how to integrate WhatsApp and SMS messaging in Node.js with Vonage Messages API. Step-by-step Express.js tutorial with webhook handlers, JWT authentication, and production deployment guides for building scalable messaging applications.
This comprehensive guide teaches you how to build a production-ready WhatsApp and SMS integration using Node.js, Express.js, and the Vonage Messages API. Whether you're implementing customer notifications, two-factor authentication, or automated messaging workflows, this tutorial covers everything you need.
You'll learn how to send and receive WhatsApp messages with Node.js, implement secure webhook integration with Express.js, handle JWT authentication for API security, manage delivery status callbacks, and deploy your messaging application to production. Perfect for developers building customer communication platforms, chatbots, notification systems, or multi-channel messaging services.
Project Overview and Goals
What You'll Build:
You'll build an Express.js web application that:
- Sends outbound SMS messages via API calls or internal functions
- Sends outbound WhatsApp messages using the Vonage WhatsApp Sandbox for testing
- Receives inbound SMS messages via webhooks configured in the Vonage API Dashboard
- Receives inbound WhatsApp messages via the same webhook, handling replies
- Receives message status updates via a separate webhook for delivery confirmations
Problem You'll Solve:
This application provides a foundational backend service for integrating programmable SMS and WhatsApp messaging into larger systems, enabling customer communication, notifications, two-factor authentication triggers, or basic chatbots.
Technologies Used:
-
Node.js: A JavaScript runtime environment for building server-side applications. Chosen for its asynchronous nature, large ecosystem (npm), and popularity.
-
Express.js: A minimal and flexible Node.js web application framework. Provides robust features for web and mobile applications, ideal for creating webhook handlers and potentially a simple API.
-
Vonage Messages API: A unified API for sending and receiving messages across multiple channels (SMS, MMS, WhatsApp, Facebook Messenger, Viber). Chosen for its multi-channel support and developer-friendly SDKs.
-
Vonage Node.js SDK (
@vonage/server-sdk,@vonage/messages,@vonage/jwt): Official libraries simplifying interaction with the Vonage APIs and handling security features like JWT verification. -
dotenv: A zero-dependency module that loads environment variables from a.envfile intoprocess.env. Essential for managing credentials securely. -
ngrok: A tool to expose local servers to the internet. Crucial for testing Vonage webhooks during development.Note: For production deployments,
ngrokis replaced by a publicly accessible server with a static IP or domain name.
System Architecture:
+-------------+ +------------------------+ +----------------+ +-----------------+
| Your Client | ----> | Node.js/Express App | ----> | Vonage API | ----> | SMS/WhatsApp |
| (Optional) | | (localhost or Server) | <---- | (Messages API) | <---- | User's Phone |
+-------------+ +---------+--------------+ +----------------+ +-----------------+
| ^
| | (Webhooks: Inbound Message / Status Update)
v |
+---------+--------------+
| ngrok (Development) |
+------------------------+Prerequisites:
- Node.js: Version 18 or higher installed. (Download Node.js)
- npm: Node Package Manager (comes with Node.js).
- Vonage API Account: Free signup available. (Sign up for Vonage)
- Vonage API Key and Secret: Found on your Vonage API Dashboard homepage.
- A Vonage Phone Number: Capable of sending SMS. You can purchase one in the Vonage API Dashboard under "Numbers".
ngrok: Installed and authenticated with a free account. (Download ngrok)- (Optional) WhatsApp Account: Needed on your phone to test WhatsApp integration with the Sandbox.
1. Set Up Your Node.js WhatsApp Project
Let's create the project directory, initialize Node.js, and install the necessary dependencies.
-
Create Project Directory:
Open your terminal or command prompt and create a new directory for your project, then navigate into it.
bashmkdir vonage-messaging-app cd vonage-messaging-app -
Initialize Node.js Project:
Initialize a new Node.js project using npm. The
-yflag accepts the default settings.bashnpm init -yThis creates a
package.jsonfile, which tracks your project's dependencies and other metadata. -
Install Dependencies:
Install Express, the Vonage SDK components, and
dotenv.bashnpm install express @vonage/server-sdk @vonage/messages @vonage/jwt dotenvexpress: Web framework.@vonage/server-sdk: Core Vonage SDK.@vonage/messages: Specific SDK for the Messages API.@vonage/jwt: For verifying webhook signatures.dotenv: For loading environment variables.
-
Project Structure:
Your basic project structure should look like this:
textvonage-messaging-app/ ├── node_modules/ ├── package.json ├── package-lock.json ├── .env # Will be created in step 6 ├── .gitignore # Will be created in step 7 ├── server.js # Will be created in step 8 └── private.key # Will be downloaded from Vonage -
Configure Vonage Account Settings:
Before writing code, ensure your Vonage account is set up correctly:
-
Set Default SMS API:
- Go to your Vonage API Dashboard.
- Navigate to Settings in the left-hand menu.
- Under API keys > SMS settings, ensure "Which API do you want to use by default for sending SMS?" is set to Messages API.
- Click Save changes. This ensures the correct webhook format is used.
Why? Vonage has older SMS APIs. Setting Messages as default ensures consistency.
-
-
Create
.envFile for Credentials:Create a file named
.envin the root of your project directory. This file will store your sensitive credentials and configuration. Never commit this file to version control.dotenv# .env # Vonage API Credentials (Find on Vonage API Dashboard homepage) VONAGE_API_KEY=YOUR_API_KEY VONAGE_API_SECRET=YOUR_API_SECRET # Vonage Application Credentials (Generated in next step) VONAGE_APPLICATION_ID=YOUR_APPLICATION_ID VONAGE_PRIVATE_KEY=./private.key # Path to your downloaded private key file # Vonage Numbers (Find/Buy in Vonage API Dashboard -> Numbers) VONAGE_SMS_NUMBER=YOUR_VONAGE_SMS_NUMBER_IN_E164_FORMAT # E.164 format (e.g., 14155551212) VONAGE_WHATSAPP_NUMBER=YOUR_VONAGE_WHATSAPP_SANDBOX_NUMBER # e.g., 14157386102 (Sandbox Number) # Webhook Security (Find in Vonage API Dashboard -> Settings -> API secrets) VONAGE_API_SIGNATURE_SECRET=YOUR_SIGNATURE_SECRET # Your Phone Number for Testing (E.164 format) MY_PHONE_NUMBER=YOUR_PERSONAL_PHONE_NUMBER_IN_E164_FORMAT # E.164 format (e.g., 12015551212) # Server Configuration PORT=8000- Replace placeholders like
YOUR_API_KEYwith your actual values (exceptVONAGE_APPLICATION_IDandVONAGE_PRIVATE_KEY, which we'll get next). - Number Formatting: Enter phone numbers in E.164 format (country code + number, without leading
+or00).
- Replace placeholders like
-
Create
.gitignoreFile:To prevent accidentally committing sensitive information and unnecessary files, create a
.gitignorefile in the root directory:text# .gitignore node_modules .env private.key *.log -
Initialize
server.js:Create a file named
server.jsin the root directory. This will contain our application logic. Add the initial setup code:javascript// server.js require('dotenv').config(); // Load environment variables from .env file const express = require('express'); const { Vonage } = require('@vonage/server-sdk'); const { WhatsAppText } = require('@vonage/messages'); const { verifySignature } = require('@vonage/jwt'); const app = express(); app.use(express.json()); // Middleware to parse JSON request bodies app.use(express.urlencoded({ extended: true })); // Middleware to parse URL-encoded request bodies const PORT = process.env.PORT || 8000; // --- Vonage Client Initialization (Add later) --- // --- Webhook Endpoints (Add later) --- // --- Send Functions (Add later) --- // --- Start Server --- app.listen(PORT, () => { console.log(`Server is listening on port ${PORT}`); console.log(`Ensure ngrok is running and pointing to http://localhost:${PORT}`); }); // Basic check endpoint app.get('/', (req, res) => { res.status(200).send('Vonage Messaging App is running!'); });
2. Configure Vonage WhatsApp Sandbox and Application
Now, let's create a Vonage Application and set up the WhatsApp Sandbox. This application acts as a container for your settings and authentication.
-
Create a Vonage Application:
-
Navigate to Applications in your Vonage API Dashboard.
-
Click + Create a new application.
-
Enter an Application name (e.g., "Node Express Messaging App").
-
Click Generate public and private key. This will automatically download a file named
private.key. Save this file in the root of your project directory. The public key is stored securely by Vonage.Why? This key pair is used for authenticating API requests securely instead of just using the API secret for certain operations.
-
Important: Copy the Application ID generated on this page and paste it into your
.envfile for theVONAGE_APPLICATION_IDvariable. -
Scroll down to Capabilities.
-
Toggle Messages ON.
- You'll see fields for Inbound URL and Status URL. We'll fill these in the next step after starting
ngrok. Leave them blank for now or enter temporary placeholders likehttp://example.com/inbound.
- You'll see fields for Inbound URL and Status URL. We'll fill these in the next step after starting
-
Scroll down to Virtual numbers.
-
Click Link next to the Vonage phone number you purchased (the one you put in
.envasVONAGE_SMS_NUMBER). This connects incoming messages on that number to this application's webhooks. -
Click Generate new application.
-
-
Start
ngrok:Open a new terminal window/tab (keep the one for running the server separate). Run
ngrokto expose the port your Express app will listen on (defined in.env, default is 8000).bashngrok http 8000ngrokwill display output similar to this:textSession Status online Account Your Name (Plan: Free) Version x.y.z Region United States (us-cal-1) Latency -- Web Interface http://127.0.0.1:4040 Forwarding https://<YOUR_UNIQUE_ID>.ngrok-free.app -> http://localhost:8000 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00- Copy the
https://<YOUR_UNIQUE_ID>.ngrok-free.appForwarding URL. This is your public URL for testing.
- Copy the
-
Configure Webhook URLs in Vonage Application:
-
Go back to your Vonage Application settings (Vonage API Dashboard -> Click your application name).
-
Click Edit.
-
Under Capabilities > Messages:
- Set Inbound URL to:
YOUR_NGROK_FORWARDING_URL/webhooks/inbound(e.g.,https://<YOUR_UNIQUE_ID>.ngrok-free.app/webhooks/inbound) - Set Status URL to:
YOUR_NGROK_FORWARDING_URL/webhooks/status(e.g.,https://<YOUR_UNIQUE_ID>.ngrok-free.app/webhooks/status)
Why? Inbound URL receives incoming messages. Status URL receives delivery receipts/status updates for outbound messages.
- Set Inbound URL to:
-
Click Save changes.
-
-
Set Up Vonage WhatsApp Sandbox:
The Sandbox allows testing WhatsApp messaging without needing a full WhatsApp Business Account initially.
-
Navigate to Messages API Sandbox under Build & Manage in the Vonage API Dashboard.
-
Allowlist Your Number: Follow the instructions to send the specific WhatsApp message shown on the page (e.g., "Allowlist <keyword>") from your personal WhatsApp number to the Vonage Sandbox number provided (usually
+14157386102). This confirms you consent to receive test messages. The Sandbox number is what you should have in your.envfile asVONAGE_WHATSAPP_NUMBER. -
Configure Sandbox Webhooks: In the Sandbox settings page, find the Webhooks section.
- Set Inbound message URL to:
YOUR_NGROK_FORWARDING_URL/webhooks/inbound - Set Message status URL to:
YOUR_NGROK_FORWARDING_URL/webhooks/status
Why? These tell the Sandbox where to send incoming WhatsApp messages and status updates, separate from the main application settings but typically pointing to the same place in simple apps.
- Set Inbound message URL to:
-
Click Save webhooks.
-
-
Get Signature Secret:
-
Go to Settings in the Vonage API Dashboard.
-
Under API secrets, find the Signing secret associated with your main API key. Click the eye icon to reveal it.
-
Copy this secret and paste it into your
.envfile forVONAGE_API_SIGNATURE_SECRET.Why? This secret is used to create a signature (JWT) for incoming webhooks, allowing your application to verify they genuinely came from Vonage.
-
3. Send SMS and WhatsApp Messages with Vonage Messages API
Now, let's add the code to initialize the Vonage client and functions to send SMS and WhatsApp messages.
-
Initialize Vonage Client:
Add the following code to your
server.jsfile, below the initialrequireandappsetup:javascript// server.js (Add this section) // --- Vonage Client Initialization --- const vonage = new Vonage( { apiKey: process.env.VONAGE_API_KEY, apiSecret: process.env.VONAGE_API_SECRET, applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: process.env.VONAGE_PRIVATE_KEY, // Path from .env }, { // Use sandbox host for WhatsApp testing if required by SDK version or specific needs // Check Vonage Node SDK documentation for latest recommendations // Often, the SDK handles the correct endpoint automatically based on message type // apiHost: 'https://messages-sandbox.nexmo.com' // Usually only needed for older SDKs or specific sandbox interactions } );- This uses the credentials loaded from your
.envfile to create an authenticated Vonage client instance. - The
privateKeypath is read directly from the environment variable. - The
apiHostoverride is generally not needed for sending messages with recent SDK versions but is included for awareness if you encounter issues specifically with the sandbox.
- This uses the credentials loaded from your
-
Implement
sendSmsFunction:Add a function to handle sending SMS messages.
javascript// server.js (Add this section) // --- Send Functions --- /** * Sends an SMS message using the Vonage Messages API. * @param {string} to - The recipient phone number (E.164 format, e.g., 12015551212). * @param {string} text - The message content. */ async function sendSms(to, text) { console.log(`Attempting to send SMS to ${to}...`); try { const resp = await vonage.messages.send({ message_type: 'text', text: text, to: to, from: process.env.VONAGE_SMS_NUMBER, // Your Vonage SMS number from .env channel: 'sms', }); console.log(`SMS sent successfully to ${to}. Message UUID: ${resp.messageUuid}`); return resp; // Contains message_uuid } catch (err) { console.error(`Error sending SMS to ${to}:`, err?.response?.data || err.message); // Rethrow or handle specific errors as needed throw err; } }- Uses
vonage.messages.send. - Specifies
channel: 'sms'andmessage_type: 'text'. - Uses the
VONAGE_SMS_NUMBERfrom.envas the sender ID. - Includes basic logging and error handling.
- Uses
-
Implement
sendWhatsAppFunction:Add a function to handle sending WhatsApp messages.
javascript// server.js (Add this section) /** * Sends a WhatsApp message using the Vonage Messages API. * Uses the WhatsAppText class for simplicity. * @param {string} to - The recipient WhatsApp number (E.164 format, e.g., 12015551212). * @param {string} text - The message content. */ async function sendWhatsApp(to, text) { console.log(`Attempting to send WhatsApp message to ${to}...`); try { // NOTE: For Sandbox, 'from' MUST be the Sandbox number. // For production WhatsApp Business API, 'from' is your provisioned WhatsApp number. const fromNumber = process.env.VONAGE_WHATSAPP_NUMBER; const resp = await vonage.messages.send( new WhatsAppText({ text: text, to: to, from: fromNumber, }) ); console.log(`WhatsApp message sent successfully to ${to}. Message UUID: ${resp.messageUuid}`); return resp; // Contains message_uuid } catch (err) { console.error(`Error sending WhatsApp message to ${to}:`, err?.response?.data || err.message); // Log specific WhatsApp errors if available in err.response.data if (err.response?.data?.invalid_parameters) { console.error('Invalid Parameters:', err.response.data.invalid_parameters); } throw err; } }- Uses
vonage.messages.sendwith aWhatsAppTextobject from@vonage/messages. - Uses the
VONAGE_WHATSAPP_NUMBER(which is the Sandbox number in our setup) as thefromnumber. This is required for the Sandbox. - Includes similar logging and error handling.
- Uses
-
(Optional) Add Test Endpoints:
To easily trigger sending messages during development, you can add simple GET endpoints. Remember to remove or secure these properly before production.
javascript// server.js (Add this section - for testing only) // --- Test Endpoints (Development Only - Secure or Remove for Production) --- app.get('/test/sms', async (req, res) => { const to = process.env.MY_PHONE_NUMBER; // Send to your own number from .env const text = `Hello from Vonage SMS! (${new Date().toLocaleTimeString()})`; if (!to) { return res.status(400).send('MY_PHONE_NUMBER environment variable not set.'); } try { const result = await sendSms(to, text); res.status(200).json({ message: 'SMS test initiated.', result }); } catch (error) { res.status(500).json({ message: 'Failed to send SMS test.', error: error.message }); } }); app.get('/test/whatsapp', async (req, res) => { const to = process.env.MY_PHONE_NUMBER; // Send to your own allowlisted number from .env const text = `Hello from Vonage WhatsApp Sandbox! (${new Date().toLocaleTimeString()})`; if (!to) { return res.status(400).send('MY_PHONE_NUMBER environment variable not set.'); } try { // Ensure your number is allowlisted in the Sandbox first! const result = await sendWhatsApp(to, text); res.status(200).json({ message: 'WhatsApp test initiated.', result }); } catch (error) { res.status(500).json({ message: 'Failed to send WhatsApp test.', error: error.message }); } });- These endpoints read your personal phone number from
.envand call the respective send functions.
- These endpoints read your personal phone number from
4. Build Express.js Webhook Integration for Inbound Messages
Webhooks are HTTP callbacks triggered by Vonage when events occur (like receiving a message or a status update). Our Express app needs endpoints to listen for these callbacks.
-
Implement JWT Verification Middleware:
Create a middleware function to verify the signature of incoming webhooks. This ensures the request genuinely came from Vonage.
javascript// server.js (Add this section) // --- Webhook Security Middleware --- const verifyVonageSignature = (req, res, next) => { try { // Get authorization header (should be "Bearer <JWT>") const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { console.warn('Webhook received without Bearer token.'); return res.status(401).send('Unauthorized: Missing Bearer token.'); } const token = authHeader.split(' ')[1]; // Verify the JWT using the signature secret from .env const decoded = verifySignature(token, process.env.VONAGE_API_SIGNATURE_SECRET); // Optional: Check claims like api_key or application_id if needed // if (decoded.api_key !== process.env.VONAGE_API_KEY) { // console.warn('Webhook received with mismatched API key.'); // return res.status(401).send('Unauthorized: Invalid API Key'); // } console.log('Webhook JWT verified successfully.'); req.vonage_jwt = decoded; // Attach decoded payload if needed later next(); // Proceed to the next middleware/handler } catch (error) { console.error('Webhook JWT verification failed:', error.message); res.status(401).send('Unauthorized: Invalid signature.'); } };- Extracts the JWT from the
Authorization: Bearer <token>header. - Uses
verifySignaturefrom@vonage/jwtand yourVONAGE_API_SIGNATURE_SECRET. - Sends a
401 Unauthorizedresponse if verification fails. - Calls
next()to pass control to the actual route handler if verification succeeds.
- Extracts the JWT from the
-
Create Inbound Webhook Endpoint:
This endpoint (
/webhooks/inbound) will receive incoming SMS and WhatsApp messages.javascript// server.js (Add this section) // --- Webhook Endpoints --- // Endpoint to receive incoming messages (SMS & WhatsApp) // Vonage sends POST requests to this URL app.post('/webhooks/inbound', verifyVonageSignature, async (req, res) => { console.log('--- Inbound Webhook Received ---'); console.log('Body:', JSON.stringify(req.body, null, 2)); // Log the full payload const message = req.body; // Basic sanity check for expected properties if (!message.from || !message.to || !message.channel) { console.warn('Received incomplete inbound message data.'); // Still send 200 OK to prevent Vonage retries for malformed requests return res.status(200).end(); } console.log(`Received message on channel: ${message.channel}`); console.log(`From: ${message.from.number || message.from.id}`); // Number format varies by channel console.log(`To: ${message.to.number || message.to.id}`); if (message.message_type === 'text') { console.log(`Text: ${message.text}`); // --- Example: Auto-reply logic --- if (message.channel === 'whatsapp') { try { // Simple reply to incoming WhatsApp const replyText = `Thanks for your WhatsApp message: "${message.text}"`; await sendWhatsApp(message.from.id, replyText); // Reply to the sender console.log(`Sent WhatsApp auto-reply to ${message.from.id}`); } catch (error) { console.error(`Failed to send WhatsApp auto-reply to ${message.from.id}`, error); } } else if (message.channel === 'sms') { // Optional: Implement SMS auto-reply logic here // const replyText = `Thanks for your SMS: "${message.text}"`; // await sendSms(message.from.number, replyText); } // --- End Example --- } else { console.log(`Received non-text message type: ${message.message_type}`); // Handle other message types (image, audio, location, etc.) if needed } // IMPORTANT: Always respond with 200 OK quickly to acknowledge receipt. // Vonage will retry if it doesn't get a 200 response. res.status(200).end(); });- Uses the
verifyVonageSignaturemiddleware first. - Logs the entire incoming request body (
req.body). The structure varies slightly depending on the channel (SMS vs. WhatsApp) and message type. Refer to Vonage Messages API Webhook Reference for details. - Includes a simple example of sending an auto-reply via WhatsApp.
- Crucially, always sends a
200 OKresponse to Vonage to acknowledge receipt, even if processing fails later. This prevents unnecessary retries from Vonage.
- Uses the
-
Create Status Webhook Endpoint:
This endpoint (
/webhooks/status) receives updates about the delivery status of messages you sent.javascript// server.js (Add this section) // Endpoint to receive message status updates // Vonage sends POST requests to this URL app.post('/webhooks/status', verifyVonageSignature, (req, res) => { console.log('--- Status Webhook Received ---'); console.log('Body:', JSON.stringify(req.body, null, 2)); // Log the full payload const statusUpdate = req.body; // Example: Log key status information if (statusUpdate.message_uuid && statusUpdate.status) { console.log(`Status Update for ${statusUpdate.message_uuid}: ${statusUpdate.status}`); if(statusUpdate.error) { console.error(`Error (${statusUpdate.error.code}): ${statusUpdate.error.reason}`); } // You could update a database record here based on message_uuid and status // Statuses include: submitted, delivered, rejected, undeliverable } else { console.warn('Received incomplete status update data.'); } // IMPORTANT: Always respond with 200 OK quickly. res.status(200).end(); });- Also uses the
verifyVonageSignaturemiddleware. - Logs the status update payload. Key fields include
message_uuid,status(submitted,delivered,rejected,undeliverable),timestamp, and potentially anerrorobject. See Vonage Messages API Webhook Reference. - Again, always sends a
200 OKresponse.
- Also uses the
5. Test Your WhatsApp and SMS Integration
Now you can run the application and test the integrations.
-
Ensure
ngrokis Running:Make sure the
ngrok http 8000command is still running in its separate terminal and that the correctngrokForwarding URL is configured in your Vonage Application and Sandbox settings. -
Start the Node.js Server:
In the terminal window within your project directory, run:
bashnode server.jsYou should see:
textServer is listening on port 8000 Ensure ngrok is running and pointing to http://localhost:8000 -
Test Sending SMS:
- Open your web browser and navigate to
http://localhost:8000/test/sms. - Check the terminal running
node server.js. You should see logs indicating an attempt to send SMS and then success with amessage_uuid. - Check your phone (the
MY_PHONE_NUMBERspecified in.env). You should receive the SMS message shortly. - Check the terminal again. You might see Status Webhook logs related to the SMS delivery (
submitted,delivered).
- Open your web browser and navigate to
-
Test Sending WhatsApp:
- Important: Ensure your
MY_PHONE_NUMBERis allowlisted in the Vonage WhatsApp Sandbox. - Open your web browser and navigate to
http://localhost:8000/test/whatsapp. - Check the
node server.jsterminal for logs indicating the attempt and success. - Check your WhatsApp on your phone. You should receive the message from the Vonage Sandbox number.
- Check the terminal for Status Webhook logs related to the WhatsApp message.
- Important: Ensure your
-
Test Receiving SMS:
- Using your physical phone, send an SMS message to your
VONAGE_SMS_NUMBER. - Check the
node server.jsterminal. You should see the "Inbound Webhook Received" logs, including the message details (channel: sms, your phone number, the text).
- Using your physical phone, send an SMS message to your
-
Test Receiving WhatsApp (and Auto-Reply):
- Using your physical phone (with the allowlisted WhatsApp number), send a WhatsApp message to the
VONAGE_WHATSAPP_NUMBER(the Sandbox number). - Check the
node server.jsterminal. You should see:- "Inbound Webhook Received" logs (
channel: whatsapp, your number, the text). - Logs indicating an attempt to send the WhatsApp auto-reply.
- Logs indicating the auto-reply was sent successfully.
- "Inbound Webhook Received" logs (
- Check your WhatsApp. You should receive the auto-reply message from the Sandbox.
- Check the terminal for Status Webhook logs related to the auto-reply message you just sent.
- Using your physical phone (with the allowlisted WhatsApp number), send a WhatsApp message to the
6. Handle Errors and Add Logging
The current implementation has basic console.log and console.error. For production:
- Use a Dedicated Logger: Integrate a more robust logging library like
winstonorpino. This enables structured logging (e.g., JSON format), different log levels (debug, info, warn, error), and routing logs to files or external services. - Centralized Error Handling Middleware: Implement an Express error-handling middleware to catch unhandled errors, log them consistently, and return appropriate HTTP responses (e.g., generic 500 errors to the client while logging detailed stack traces).
- Specific Vonage Error Codes: The
err.response.dataobject in thecatchblocks of thesendfunctions often contains detailed error information from the Vonage API, including specific error codes and descriptions. Log these details and potentially handle specific, retryable errors (like network issues) differently from non-retryable ones (like invalid numbers). Refer to Vonage API Error Codes. - Webhook Reliability: While we send
200 OKimmediately, implement background job queues (e.g., using BullMQ with Redis) for processing inbound messages or sending replies if the logic is complex or involves external dependencies. This prevents webhook timeouts and allows for retries on processing failures without blocking the Vonage retry mechanism.
7. Secure Your Messaging Application
- Webhook Signature Verification: We've implemented JWT verification, which is crucial. Always keep your
VONAGE_API_SIGNATURE_SECRETsecure. - Environment Variables: Never commit
.envfiles orprivate.keyto source control. Use environment variables provided by your deployment platform in production. - Input Validation/Sanitization: If you process the content of incoming messages (
message.text) for anything beyond simple logging or replies (e.g., saving to a database, using it in commands), sanitize it thoroughly to prevent injection attacks (XSS, SQLi, etc.). Libraries likeexpress-validatorcan help. - Rate Limiting: If your application exposes APIs that trigger messages, implement rate limiting (e.g., using
express-rate-limit) to prevent abuse and control costs. - Authentication/Authorization: If you build a frontend or API layer on top of this, ensure proper user authentication and authorization before allowing actions like sending messages.
8. Troubleshoot Common Issues
- Ngrok URL Mismatch: Ensure the exact
ngrokURL (includinghttps://) is updated in both the Vonage Application settings and the WhatsApp Sandbox settings whenever you restartngrok. A common mistake is forgetting one or usinghttpinstead ofhttps. - Firewall Issues: If
ngrokis running but you don't receive webhooks, a local firewall might be blockingngrokor Node.js. .envNot Loaded: Ensurerequire('dotenv').config();is the very first line inserver.js. Make sure the.envfile is in the root directory where you runnode server.js.- Incorrect Credentials: Double-check API Key, Secret, Application ID, Signature Secret, and Private Key Path in
.env. Ensure theprivate.keyfile exists at the specified path. - Number Formatting: All phone numbers (
to,from,MY_PHONE_NUMBER) should be in E.164 format without leading+or00(e.g.,14155551212). - WhatsApp Sandbox Limitations:
- Only works with allowlisted numbers.
- Uses the fixed Sandbox number (
14157386102or similar) as thefromnumber. - Has time limits for conversations initiated by the business (requires user opt-in for production).
- JWT Verification Errors: Usually caused by an incorrect
VONAGE_API_SIGNATURE_SECRETin.envor issues with the system clock if the discrepancy is large. - Missing
200 OK: Forgetting to sendres.status(200).end()in webhook handlers will cause Vonage to repeatedly retry sending the webhook, leading to duplicate processing and logs. - Vonage API Errors: Check the
node server.jslogs for specific errors returned by the Vonage API (e.g., insufficient funds, invalid recipient number, permission issues).
Frequently Asked Questions
How do I send WhatsApp messages with Vonage in Node.js?
Install the Vonage SDK (npm install @vonage/server-sdk @vonage/messages), authenticate with your API credentials and private key, then use vonage.messages.send(new WhatsAppText()) with your sandbox number as from and recipient number in E.164 format as to. Configure webhooks for delivery status and inbound messages through ngrok during development.
What is the Vonage WhatsApp Sandbox?
The Vonage WhatsApp Sandbox is a testing environment that lets you send and receive WhatsApp messages without a full WhatsApp Business Account. Users must allowlist their phone numbers by sending a specific message to the sandbox number (+14157386102). It's free but requires user opt-in and has conversation time limits.
How do I verify Vonage webhook signatures in Express.js?
Use the verifySignature function from @vonage/jwt to validate the Bearer token in the Authorization header against your Vonage API Signature Secret. This JWT verification ensures webhook requests genuinely originate from Vonage and haven't been tampered with, preventing unauthorized webhook abuse.
What is E.164 phone number format?
E.164 is the international phone numbering format: country code + national number without leading + or 00. Example: US number (415) 555-1212 becomes 14155551212. Vonage requires E.164 format for all phone numbers in API calls to ensure proper routing across global carriers.
How do I handle inbound WhatsApp messages with Vonage?
Create a POST endpoint (e.g., /webhooks/inbound) secured with JWT verification middleware. Parse req.body to extract message.text, message.from.id, and message.channel. Always respond with res.status(200).end() immediately to acknowledge receipt. Process messages asynchronously using background queues for complex logic.
Can I send SMS and WhatsApp from the same Vonage application?
Yes, the Vonage Messages API supports multi-channel messaging from a single application. Use different from numbers (Vonage SMS number for SMS, Sandbox number for WhatsApp) and specify channel: 'sms' or use WhatsAppText class. Configure webhooks once – they receive both SMS and WhatsApp inbound messages.
What Node.js version does Vonage SDK require?
Vonage SDK requires Node.js version 18 or higher for optimal compatibility with modern JavaScript features and security updates. Check your version with node --version. Update via nodejs.org or nvm. The SDK uses ES modules and async/await patterns requiring recent Node.js runtime features.
How do I deploy a Vonage webhook application to production?
Deploy to cloud platforms (AWS, Google Cloud, Heroku, Render) with HTTPS support. Use environment variables for credentials (never commit .env), implement pm2 process manager for reliability, configure production webhook URLs in Vonage Dashboard pointing to your public domain, and enable rate limiting with express-rate-limit to prevent abuse.
9. Deploy to Production
Deploying this application involves running the Node.js process on a server and ensuring it's publicly accessible for webhooks.
- Choose a Platform: Options include cloud providers (AWS, Google Cloud, Azure), PaaS (Heroku, Render, Vercel - ensure long-running process support), or traditional VPS.
- Environment Variables: Configure production environment variables securely on your chosen platform instead of using a
.envfile. - Process Manager: Use a process manager like
pm2to keep your Node.js application running reliably (handles crashes, restarts, logging). - HTTPS: Ensure your public server uses HTTPS for webhook URLs. Platforms often handle this, or use a reverse proxy like Nginx or Caddy with Let's Encrypt.
- Update Webhook URLs: Update the Inbound and Status URLs in your Vonage Application and Sandbox settings to point to your permanent public server URL.
- Firewall Rules: Ensure your server's firewall allows incoming traffic on the port your application listens on (or port 443 if using a reverse proxy).
Related Resources
Learn more about WhatsApp messaging and Node.js integrations:
- Vonage Messages API Documentation - Official API reference
- Node.js SMS Integration Guide - Learn SMS-specific features
- Express.js Webhook Best Practices - Advanced webhook patterns
- WhatsApp Business API Overview - Production WhatsApp setup
Frequently Asked Questions
How to send SMS messages with Node.js and Vonage
Use the Vonage Messages API and Node.js SDK. The `sendSms` function handles sending text messages by specifying the recipient's number, message content, your Vonage SMS number, and the 'sms' channel. Ensure numbers are in E.164 format (e.g., 14155551212).
How to receive SMS messages with Vonage and Express
Set up an inbound webhook URL in your Vonage application settings and handle incoming messages in your Express app's `/webhooks/inbound` endpoint. Verify the webhook signature using `@vonage/jwt` to ensure security. Always respond with a 200 OK status, even if processing fails, to prevent Vonage retries.
How to send WhatsApp messages using Node.js and Vonage
Use the `sendWhatsApp` function, providing recipient and message text. The 'from' number must be your Sandbox number for testing or your provisioned WhatsApp Business number for production. This leverages the `WhatsAppText` class from `@vonage/messages`.
What is the Vonage Messages API?
The Vonage Messages API is a unified platform for sending and receiving messages across various channels, including SMS, MMS, WhatsApp, Viber, and Facebook Messenger. It offers a simplified way to integrate messaging into applications using a single API.
Why use ngrok for Vonage webhook development?
Ngrok creates a secure tunnel that exposes your locally running Express.js application to the internet, allowing Vonage to deliver webhooks to your development environment. This is essential for testing webhooks before deploying to a public server.
How to set up Vonage WhatsApp Sandbox
Allowlist your WhatsApp number by sending a specific message to the Sandbox number provided in the Vonage Dashboard. Then, configure your Sandbox webhooks (inbound and status) to point to your application's URLs (e.g., your ngrok URL during development).
What is a Vonage Application ID, and why is it necessary?
A Vonage Application ID is a unique identifier assigned to your application in the Vonage Dashboard. It's essential for associating your application with your Vonage account settings, such as API keys, webhooks, and linked phone numbers.
When should I replace ngrok with a public server?
Replace ngrok with a publicly accessible server with a static IP or domain name before deploying your application to production. Ngrok is a development tool and not suitable for production use due to its temporary URLs and security implications.
Can I receive WhatsApp messages without a business account?
Yes, using the Vonage WhatsApp Sandbox. It's designed for development and testing purposes. For production, a WhatsApp Business Account is required.
What Vonage SDKs do I need for a Node.js messaging application?
You need `@vonage/server-sdk` (core SDK), `@vonage/messages` (Messages API SDK), and `@vonage/jwt` (for verifying webhook signatures) to interact with the Vonage Messages API effectively in your Node.js project.
How to handle Vonage webhook security in Express
Use the `verifyVonageSignature` middleware to verify the JWT (JSON Web Token) signature attached to incoming webhooks. This confirms authenticity and prevents unauthorized requests from reaching your application logic.
Why does Vonage send message status updates?
Vonage sends message status updates via webhooks to inform your application about the delivery status of sent messages. These updates include statuses like 'submitted', 'delivered', 'rejected', or 'undeliverable', which are crucial for monitoring message delivery.
How to verify Vonage webhook signatures
Verify webhook signatures by extracting the JWT (JSON Web Token) from the Authorization header of the webhook request. Use the `@vonage/jwt` library with your `VONAGE_API_SIGNATURE_SECRET` to decode and verify the token, ensuring its origin and integrity.
What is the purpose of the .env file?
The `.env` file stores environment variables like API keys, secrets, and configuration settings. It's essential for managing sensitive credentials securely during development and should never be committed to version control.
When to use the apiHost override in the Vonage client initialization?
The `apiHost` override was typically needed with older versions of the Vonage Node.js SDK, particularly when interacting with the Sandbox. Check the official documentation; recent SDK versions mostly handle the correct endpoint automatically.