This guide provides a complete walkthrough for building a Node.js application using the Express framework to send and receive WhatsApp messages via the Vonage Messages API. We will cover everything from initial project setup to deployment considerations.
This application enables two-way communication over WhatsApp, allowing your Node.js service to react to incoming messages and send replies. It solves the need for programmatic interaction with users on a globally popular messaging platform.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications. (v20 or higher recommended)
- Express: A minimal and flexible Node.js web application framework used to create the server and API endpoints.
- Vonage Messages API: A unified API for sending and receiving messages across various channels, including WhatsApp, SMS, MMS, and more.
- Vonage Node SDK: Simplifies interaction with Vonage APIs within a Node.js environment.
- ngrok: A tool to expose your local development server to the internet, necessary for Vonage webhooks to reach your machine during development.
- dotenv: A module to load environment variables from a
.env
file intoprocess.env
.
System Architecture:
The communication flow involves the user, WhatsApp, Vonage, your application (via ngrok during development), and potentially logs or a database:
- A WhatsApp user sends a message.
- WhatsApp forwards the message to the Vonage platform.
- Vonage sends an inbound message webhook to your ngrok tunnel URL.
- ngrok forwards the webhook to your local Node.js/Express application.
- Your application processes the message (e.g., logs it, stores it in a DB).
- Your application uses the Vonage SDK to send a reply message back to Vonage.
- Vonage sends the reply message to WhatsApp.
- WhatsApp delivers the reply to the original user.
- Vonage also sends message status webhooks (e.g., 'delivered', 'read') to your ngrok tunnel URL.
- ngrok forwards the status webhook to your application.
- Your application processes the status update.
Prerequisites:
- A Vonage API account. Sign up here if you don't have one.
- Node.js and npm (or yarn) installed (v20 or higher recommended). Verify with
node -v
andnpm -v
. - ngrok installed and authenticated. Download and setup ngrok here.
- A WhatsApp-enabled mobile device for testing.
Final Outcome:
By the end of this guide, you will have a running Node.js Express application capable of:
- Receiving incoming WhatsApp messages sent to your Vonage Sandbox number.
- Automatically sending a predefined reply ('Message received.') back to the sender via WhatsApp.
- Receiving and logging message status updates (e.g., delivered, read).
- Verifying webhook signatures to ensure requests originate from Vonage.
1. Setting up the Project
Let's initialize our Node.js project and set up the basic structure.
-
Create Project Directory: Open your terminal or command prompt and create a new directory for your project, then navigate into it.
mkdir vonage-whatsapp-guide cd vonage-whatsapp-guide
-
Initialize Node.js Project: Initialize the project using npm. The
-y
flag accepts default settings.npm init -y
This creates a
package.json
file. -
Create
.gitignore
: Create a.gitignore
file to prevent sensitive information and unnecessary files from being committed to version control.touch .gitignore
Add the following lines to
.gitignore
:# Dependencies node_modules/ # Environment variables .env # Optional Editor/OS files .DS_Store *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Vonage Private Key (if stored in project) private.key *.key
Why
.gitignore
? It's crucial for security (keeping.env
and keys out of Git) and keeps your repository clean by excluding generated files likenode_modules
. -
Create Project Files: Create the main application file and the environment configuration file.
mkdir src touch src/server.js touch .env
Your project structure should now look like this:
vonage-whatsapp-guide/ ├── src/ │ └── server.js ├── .env ├── .gitignore └── package.json
2. Environment Variable Configuration
We'll use a .env
file to store sensitive credentials and configuration settings.
-
Populate
.env
: Open the.env
file and add the following variable keys. We will fill in the values in the next steps.# Vonage API Credentials & Application Settings VONAGE_API_KEY= VONAGE_API_SECRET= VONAGE_APPLICATION_ID= VONAGE_PRIVATE_KEY= # Path to your private key file VONAGE_API_SIGNATURE_SECRET= # Vonage WhatsApp Sandbox Number (or your purchased number for production) VONAGE_WHATSAPP_NUMBER=14157386102 # Default Sandbox Number # Server Configuration PORT=8000
-
Obtain Vonage Credentials:
VONAGE_API_KEY
&VONAGE_API_SECRET
:- Log in to your Vonage API Dashboard.
- Your API Key and Secret are displayed prominently on the main dashboard page.
- Copy and paste these values into your
.env
file.
VONAGE_API_SIGNATURE_SECRET
:- Navigate to Dashboard Settings.
- Find the ""API secrets"" section and click ""Edit"" or ""Add new secret"".
- Copy an existing Signature Secret or generate a new one.
- Paste this value into
VONAGE_API_SIGNATURE_SECRET
in your.env
file. This is used to verify webhook signatures.
VONAGE_APPLICATION_ID
&VONAGE_PRIVATE_KEY
: These will be generated in the next step when creating the Vonage Application.VONAGE_WHATSAPP_NUMBER
: For this tutorial using the sandbox, the number is pre-filled (14157386102
). If transitioning to production with a purchased Vonage number enabled for WhatsApp, replace this with that number (in E.164 format without leading+
or00
, e.g.,447700900000
).PORT
: The local port your Express server will listen on.8000
is a common choice, but you can change it if needed.
3. Vonage Account and Application Setup
A Vonage Application acts as a container for your communication settings, linking phone numbers and webhooks.
-
Navigate to Applications: In the Vonage API Dashboard, go to "Applications" under the "Build & Manage" section in the left-hand menu. Click "Create a new application".
-
Configure Application:
- Name: Give your application a descriptive name (e.g., "Node WhatsApp Guide App").
- Generate Public and Private Key: Click the "Generate public and private key" button. Immediately save the
private.key
file that downloads. Move this file into the root of yourvonage-whatsapp-guide
project directory.- Security Note: Treat this
private.key
file like a password. Do not commit it to version control (it should be in your.gitignore
). For production, consider more secure key management strategies.
- Security Note: Treat this
- Update
.env
: Set theVONAGE_PRIVATE_KEY
variable in your.env
file to the path of the downloaded key (e.g.,./private.key
if it's in the root).# Example assuming private.key is in the project root VONAGE_PRIVATE_KEY=./private.key
- Capabilities: Find the "Capabilities" section and toggle "Messages" ON.
- Webhooks (Placeholders for now): You'll see fields for "Inbound URL" and "Status URL". We need ngrok running to get the actual URLs. For now, you can leave them blank or enter temporary placeholders like
http://localhost:8000/inbound
andhttp://localhost:8000/status
. We will update these later. - Save Application: Scroll down and click "Generate new application".
-
Get Application ID: After saving, you'll be taken to the application's details page. Copy the Application ID.
- Update
.env
: Paste the copied ID into theVONAGE_APPLICATION_ID
field in your.env
file.
- Update
-
Link Number (Optional but Recommended for Production): If you intend to use a specific Vonage number you've purchased (required for production WhatsApp Business API usage), scroll down to the "Linked numbers" section on the application details page and link your desired number. For the sandbox, this step isn't strictly necessary as we use the shared sandbox number.
4. WhatsApp Sandbox Setup
The Vonage WhatsApp Sandbox provides a testing environment without needing a fully approved WhatsApp Business Account initially.
-
Navigate to Sandbox: In the Vonage API Dashboard, go to ""Messages API Sandbox"" under the ""Build & Manage"" section.
-
Activate Sandbox: Follow the on-screen instructions. This usually involves:
- Scanning a QR code with your WhatsApp mobile app OR
- Sending a specific message from your WhatsApp number to the Vonage Sandbox number displayed (+14157386102).
- This whitelists your personal WhatsApp number to interact with the sandbox. Only whitelisted numbers can send messages to the sandbox.
-
Configure Sandbox Webhooks: Find the ""Webhooks"" section on the Sandbox page. Like the application webhooks, we need ngrok running first. Leave these blank for now or use temporary placeholders. We'll update them shortly.
5. ngrok Setup and Webhook Configuration
Vonage needs a publicly accessible URL to send webhook events (incoming messages, status updates) to your local machine. ngrok creates a secure tunnel for this.
-
Start ngrok: Open a new terminal window (keep the first one for running the Node app later). Run ngrok, telling it to forward traffic to the port your Express server will use (defined as
PORT
in.env
, default8000
).ngrok http 8000
-
Copy ngrok URL: ngrok will display session information, including a "Forwarding" URL that looks like
https://<random-string>.ngrok-free.app
(or similar, depending on your ngrok plan). Copy this HTTPS URL. This is your public base URL. Note: Free ngrok plans generate a new random URL each time you restart it. You'll need to update your Vonage webhooks whenever this URL changes. Paid plans offer stable subdomains. -
Update Vonage Application Webhooks:
- Go back to your Vonage Application settings (Dashboard > Applications > Your App Name).
- Under "Capabilities" > "Messages":
- Set Inbound URL:
YOUR_NGROK_HTTPS_URL/inbound
(e.g.,https://<random-string>.ngrok-free.app/inbound
) - Set Status URL:
YOUR_NGROK_HTTPS_URL/status
(e.g.,https://<random-string>.ngrok-free.app/status
)
- Set Inbound URL:
- Click "Save changes".
-
Update Vonage Sandbox Webhooks:
- Go back to the "Messages API Sandbox" page in the dashboard.
- Under the "Webhooks" section:
- Set Inbound Message URL:
YOUR_NGROK_HTTPS_URL/inbound
- Set Message Status URL:
YOUR_NGROK_HTTPS_URL/status
- Set Inbound Message URL:
- Click "Save webhooks".
Why two sets of webhooks? The Application webhooks are the general configuration. The Sandbox webhooks specifically override these for sandbox traffic only, allowing you to test without affecting potential production configurations on the same application.
6. Installing Dependencies
Install the necessary npm packages for our application.
-
Run Install Command: In your project's terminal window (the one in the
vonage-whatsapp-guide
directory), run:npm install @vonage/server-sdk @vonage/messages @vonage/jwt express dotenv
@vonage/server-sdk
: The main Vonage SDK for Node.js, includes authentication and core functionalities.@vonage/messages
: Specific helpers for constructing message objects for the Messages API (likeWhatsAppText
).@vonage/jwt
: Used for verifying the signature of incoming webhook requests.express
: The web framework.dotenv
: To load variables from the.env
file.
This will install the packages and add them to your
package.json
andpackage-lock.json
.
src/server.js
)
7. Implementing Core Functionality (Now, let's write the code for our Express server to handle incoming messages and send replies.
Open src/server.js
and add the following code, section by section:
-
Import Dependencies and Initialize Express:
// src/server.js require('dotenv').config(); // Load environment variables from .env file first const express = require('express'); const { Vonage } = require('@vonage/server-sdk'); const { WhatsAppText } = require('@vonage/messages'); const { verifySignature } = require('@vonage/jwt'); const app = express(); // Use Express's built-in JSON body parser app.use(express.json()); // Use Express's built-in URL-encoded body parser (optional, but good practice) app.use(express.urlencoded({ extended: true })); const PORT = process.env.PORT || 8000; // Use port from .env or default to 8000
Explanation: We load environment variables immediately, import necessary modules, initialize the Express app, and configure middleware to parse incoming JSON and URL-encoded request bodies. We also define the port.
-
Initialize Vonage Client:
// Initialize Vonage Client with credentials from environment variables 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 to your private key file }, { // Use the API host for the Messages API Sandbox // For production, remove this option to use the standard API endpoint apiHost: 'https://messages-sandbox.nexmo.com', } );
Explanation: We create an instance of the Vonage SDK, passing our credentials read securely from
process.env
. TheapiHost
option directs requests to the sandbox environment. Remember to removeapiHost
when moving to production. -
Implement JWT Signature Verification:
// Function to verify JWT signature on incoming status webhooks const verifyVonageSignature = (req, res, next) => { try { // Extract token from 'Authorization: Bearer <token>' header const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { console.error('Error: Missing or invalid Authorization header'); return res.status(401).send('Unauthorized: Missing bearer token'); } const token = authHeader.split(' ')[1]; // Verify the signature using the token and your signature secret if (!verifySignature(token, process.env.VONAGE_API_SIGNATURE_SECRET)) { console.error('Error: Invalid JWT signature'); return res.status(401).send('Unauthorized: Invalid signature'); } console.log('JWT Signature Verified'); // If signature is valid, proceed to the next middleware/route handler next(); } catch (error) { console.error('Error verifying JWT:', error.message); res.status(401).send('Unauthorized: Signature verification failed'); } };
Explanation: This middleware function checks the
Authorization
header of incoming requests (specifically for the/status
webhook). It extracts the JWT, verifies its signature against theVONAGE_API_SIGNATURE_SECRET
from your.env
. If verification fails or the header is missing, it sends a401 Unauthorized
response. If successful,next()
passes control to the actual route handler. This ensures that status updates genuinely come from Vonage. -
Implement Message Sending Function:
// Function to send a WhatsApp message using Vonage Messages API const sendWhatsAppReply = async (recipientNumber) => { const senderNumber = process.env.VONAGE_WHATSAPP_NUMBER; const messageText = 'Message received.'; // The reply message console.log(`Attempting to send reply to ${recipientNumber} from ${senderNumber}`); try { // Construct the message object using WhatsAppText helper const message = new WhatsAppText({ text: messageText, to: recipientNumber, from: senderNumber, }); // Send the message using the Vonage client const response = await vonage.messages.send(message); console.log(`Message sent successfully with UUID: ${response.messageUuid}`); return response; // Return the response object } catch (error) { // Log detailed error information console.error('Error sending WhatsApp message:'); if (error.response) { // Error from Vonage API (e.g., invalid number, authentication) console.error(' Status:', error.response.status); console.error(' Headers:', JSON.stringify(error.response.headers, null, 2)); console.error(' Body:', JSON.stringify(error.response.data, null, 2)); } else if (error.request) { // Request was made but no response received console.error(' Request Error:', error.request); } else { // Setup error or other issue console.error(' General Error:', error.message); } // Re-throw the error if needed for higher-level handling, or handle appropriately throw error; // Or return null, depending on desired behavior } };
Explanation: This asynchronous function takes the recipient's number, defines the sender (from
.env
) and the message text. It uses the@vonage/messages
helper (WhatsAppText
) to structure the payload correctly for thevonage.messages.send
method. Detailed error logging is included to help diagnose issues during sending. -
Implement Inbound Message Webhook:
// Route to handle incoming WhatsApp messages from Vonage app.post('/inbound', async (req, res) => { console.log('Received inbound message:'); console.log(JSON.stringify(req.body, null, 2)); // Log the full incoming request body const { from: senderNumber, message } = req.body; // Basic validation: Check if sender number is present if (!senderNumber || !senderNumber.number) { console.error('Error: Invalid inbound payload - missing sender number.'); // Respond 400 Bad Request, Vonage might retry but unlikely to succeed return res.status(400).send('Bad Request: Missing sender number.'); } // Basic validation: Check if message content is present if (!message || !message.content || !message.content.text) { // Could be an image, audio, etc. Handle based on message.content.type console.log('Received non-text message type:', message?.content?.type); // Decide how to handle non-text messages - for now, just acknowledge. return res.status(200).send('OK'); // Acknowledge receipt even if not processing text } console.log(`Message received from ${senderNumber.number}: "${message.content.text}"`); // Adjusted quoting for log clarity try { // Send the predefined reply back to the sender await sendWhatsAppReply(senderNumber.number); // Send a 200 OK response to Vonage to acknowledge receipt res.status(200).send('OK'); } catch (error) { console.error('Error processing inbound message or sending reply:', error.message); // Send a 500 Internal Server Error if sending reply fails res.status(500).send('Internal Server Error'); } });
Explanation: This defines the
/inbound
route that Vonage calls when a message arrives. It logs the incoming payload, extracts the sender's number (from.number
), validates basic payload structure, logs the message text, callssendWhatsAppReply
to send the response, and crucially, sends a200 OK
status back to Vonage. If you don't send a 200 OK, Vonage will assume delivery failed and may retry the webhook. -
Implement Status Update Webhook:
// Route to handle message status updates from Vonage // Apply the JWT verification middleware *before* the route handler app.post('/status', verifyVonageSignature, (req, res) => { console.log('Received status update:'); console.log(JSON.stringify(req.body, null, 2)); // Log the full status update body // Extract relevant info (e.g., message UUID, status, timestamp) const { message_uuid, status, timestamp, to } = req.body; console.log(`Status for message ${message_uuid} to ${to?.number}: ${status} at ${timestamp}`); // You can add logic here to update message status in a database, etc. // For example: updateDatabase(message_uuid, status); // Send a 200 OK response to Vonage to acknowledge receipt res.status(200).send('OK'); });
Explanation: This defines the
/status
route. Importantly, we apply theverifyVonageSignature
middleware before the main handler function. This ensures only authenticated requests from Vonage are processed. The handler logs the status update details (likedelivered
,read
) and responds with200 OK
. -
Start the Server:
// Start the Express server app.listen(PORT, () => { console.log(`Server is listening on http://localhost:${PORT}`); console.log('Ensure ngrok is running and webhooks are configured correctly.'); });
Explanation: This starts the Express server, making it listen for incoming connections on the specified
PORT
.
8. Building the API Layer
The API layer in this application consists of the two webhook endpoints we created:
-
POST /inbound
: Receives incoming WhatsApp messages from Vonage.- Authentication: None required from the sender (WhatsApp user), but Vonage handles the connection.
- Request Body (JSON Example):
{ ""from"": { ""type"": ""whatsapp"", ""number"": ""447700900001"" }, ""to"": { ""type"": ""whatsapp"", ""number"": ""14157386102"" }, ""message_uuid"": ""a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8"", ""message"": { ""content"": { ""type"": ""text"", ""text"": ""Hello Server!"" }, ""whatsapp"": { /* WhatsApp specific metadata */ } }, ""timestamp"": ""2025-04-20T10:30:00.123Z"" }
- Response:
200 OK
: Successfully received and processing initiated.400 Bad Request
: Payload missing essential information (e.g., sender number).500 Internal Server Error
: Error occurred during processing (e.g., failed to send reply).
- Testing: Not directly testable with
curl
as it requires Vonage to initiate the POST. Testing is done by sending a WhatsApp message from your whitelisted number to the Sandbox number.
-
POST /status
: Receives message status updates from Vonage (e.g., delivered, read).- Authentication: Requires a valid JWT in the
Authorization: Bearer <token>
header, verified usingVONAGE_API_SIGNATURE_SECRET
. - Request Body (JSON Example - Delivered):
{ ""message_uuid"": ""b2c3d4e5-f6g7-8901-h2i3-j4k5l6m7n8o9"", ""to"": { ""type"": ""whatsapp"", ""number"": ""447700900001"" }, ""from"": { ""type"": ""whatsapp"", ""number"": ""14157386102"" }, ""timestamp"": ""2025-04-20T10:30:05.456Z"", ""status"": ""delivered"", ""usage"": { ""currency"": ""EUR"", ""price"": ""0.0063"" }, ""client_ref"": ""optional-client-reference"" }
- Response:
200 OK
: Successfully received status update.401 Unauthorized
: Missing or invalid JWT signature.
- Testing: You can't easily generate a valid test JWT without mimicking Vonage's signing process. Testing relies on observing status updates after sending messages via the application.
- Authentication: Requires a valid JWT in the
9. Integrating with Vonage
Integration points are:
- SDK Initialization: The
Vonage
client is initialized using API Key, Secret, Application ID, and Private Key from.env
. - Sending Messages: The
vonage.messages.send()
method uses the initialized client to interact with the Vonage Messages API. - Receiving Webhooks: The Express server listens on
/inbound
and/status
endpoints configured in the Vonage Application and Sandbox settings, using ngrok for tunneling during development. - Webhook Security: The
/status
webhook uses JWT signature verification (@vonage/jwt
andVONAGE_API_SIGNATURE_SECRET
) to confirm authenticity.
Secure Handling of Credentials:
- All secrets (API Key, Secret, Signature Secret, Private Key path) are stored in the
.env
file. - The
.env
file is included in.gitignore
to prevent accidental commits. - The
dotenv
package loads these variables intoprocess.env
, avoiding hardcoding them in the source code. - The
private.key
file itself is also in.gitignore
.
10. Error Handling, Logging, and Retry Mechanisms
- Error Handling Strategy:
- Use
try...catch
blocks around asynchronous operations (likevonage.messages.send
). - Log errors using
console.error
with detailed information (status code, body for API errors). - Send appropriate HTTP status codes back to Vonage (e.g.,
500
if reply sending fails in/inbound
,401
for failed auth in/status
).
- Use
- Logging:
- Current implementation uses
console.log
andconsole.error
. - Production Recommendation: Use a dedicated logging library like Pino or Winston for structured logging (JSON format), different log levels (info, warn, error), and configurable output streams (e.g., file, external logging service).
// Example using Pino (after npm install pino) // const pino = require('pino')(); // pino.info('Server started'); // pino.error(error, 'Failed to send message');
- Current implementation uses
- Retry Mechanisms:
- Vonage Webhook Retries: If your
/inbound
or/status
endpoint doesn't return a200 OK
within a timeout period, Vonage will automatically retry sending the webhook several times with increasing delays. Ensure your endpoints respond promptly. - Application Send Retries: The current
sendWhatsAppReply
function doesn't implement retries if the initial send to Vonage fails. For production, you might add a retry mechanism (e.g., using a library like async-retry) with exponential backoff for transient network errors or temporary Vonage API issues when callingvonage.messages.send
.
- Vonage Webhook Retries: If your
11. Database Schema and Data Layer (Optional Extension)
This basic guide doesn't store messages. For persistence, you'd add a database.
- Potential Schema (Conceptual):
Messages
table:message_uuid
(TEXT, PRIMARY KEY) - From Vonage response/webhooksvonage_sender
(TEXT)vonage_recipient
(TEXT)content_text
(TEXT, nullable)content_type
(TEXT) - e.g., 'text', 'image'direction
(TEXT) - 'inbound' or 'outbound'status
(TEXT) - 'submitted', 'delivered', 'read', 'failed', 'rejected'vonage_timestamp
(TIMESTAMP WITH TIME ZONE)created_at
(TIMESTAMP WITH TIME ZONE, default NOW())updated_at
(TIMESTAMP WITH TIME ZONE)
- Implementation:
- Choose a database (PostgreSQL, MySQL, MongoDB).
- Use an ORM/Query Builder like Prisma, Sequelize, or TypeORM.
- Data Access: Create functions to insert messages on
/inbound
and outbound sends, and update status on/status
webhooks using themessage_uuid
. - Migrations: Use the ORM's migration tools (
prisma migrate dev
,sequelize db:migrate
) to manage schema changes.
12. Security Features
- Webhook Signature Verification: Already implemented for the
/status
webhook using JWT. Crucial to prevent spoofed status updates. While/inbound
doesn't typically use JWT, you could implement IP whitelisting for Vonage webhook IPs in production (though IPs can change). - Input Validation:
- Basic checks added in
/inbound
forsenderNumber
. - Recommendation: Use a library like Joi or express-validator for more robust validation of incoming webhook payloads (
req.body
).
- Basic checks added in
- Rate Limiting:
- Protect against abuse or accidental loops. Use middleware like express-rate-limit.
// Example (install first: npm install express-rate-limit) // const rateLimit = require('express-rate-limit'); // const limiter = rateLimit({ // windowMs: 15 * 60 * 1000, // 15 minutes // max: 100 // limit each IP to 100 requests per windowMs // }); // app.use(limiter); // Apply to all requests // app.use('/inbound', specificLimiter); // Apply differently per route
- Protect against abuse or accidental loops. Use middleware like express-rate-limit.
- Secure Headers: Use middleware like Helmet to set various HTTP headers that improve security (XSS protection, frameguard, etc.).
npm install helmet
// const helmet = require('helmet'); // app.use(helmet());
- Dependency Management: Regularly update dependencies (
npm update
) and use tools likenpm audit
to check for known vulnerabilities.
13. Handling Special Cases
- Non-Text Messages: The current
/inbound
handler only processesmessage.content.type === 'text'
. You need to add logic to handle other types (image
,audio
,video
,file
,location
, etc.) based onreq.body.message.content.type
and the corresponding content structure if required. - Sandbox Limitations:
- Only whitelisted numbers can initiate conversations with the sandbox number.
- The sandbox number (
14157386102
) is shared and has limitations. - Templates are not typically tested in the sandbox.
- Production (WhatsApp Business API - WABA):
- Requires a purchased Vonage number enabled for WhatsApp and Facebook Business Manager approval.
- 24-Hour Window: You can only send free-form messages within 24 hours of the last user-initiated message. Outside this window, you must use pre-approved Message Templates.
- Templates: Require approval by Meta/WhatsApp. The SDK supports sending templates (
vonage.messages.send(new WhatsAppTemplate(...))
).
- Rate Limits: Both Vonage and WhatsApp enforce rate limits. Implement appropriate error handling and potential backoff/retry logic if you hit these limits (
429 Too Many Requests
errors).
14. Performance Optimizations
For this simple application, performance is unlikely to be an issue initially. For high-volume scenarios:
- Node.js Clustering: Use Node.js's built-in
cluster
module or a process manager like PM2 in cluster mode to utilize multiple CPU cores. - Asynchronous Operations: Ensure all I/O (like database calls, API requests) is non-blocking (using
async/await
, Promises). The current code already does this. - Payload Size: Be mindful of large request/response bodies if adding complex features.
- Load Testing: Use tools like k6, Artillery, or ApacheBench to simulate traffic to your webhook endpoints (especially
/inbound
) and identify bottlenecks.