code examples
code examples
MessageBird WhatsApp Integration with Node.js and Express: Complete Guide
Build a WhatsApp integration with Node.js and Express using MessageBird API. Learn to send messages, handle webhooks, implement security, and deploy to production.
MessageBird WhatsApp Integration with Node.js and Express
Build a production-ready Node.js WhatsApp integration using the Express framework and MessageBird Conversations API. Learn everything from project setup and core functionality to security, error handling, and deployment.
By the end of this tutorial, you'll have a functional Express server that:
- Sends text messages to WhatsApp users via the MessageBird API
- Receives incoming WhatsApp messages through MessageBird webhooks
- Securely handles API credentials and verifies webhook authenticity
This WhatsApp Business API integration enables programmatic customer communication on WhatsApp for use cases like automated notifications, customer support automation, and two-factor authentication. You'll use MessageBird's official Node.js SDK for simplified API interaction.
System Architecture:
Your client application triggers your Node.js server to send a message. Your Node.js server uses the MessageBird API Key to call the MessageBird API, which relays the message to WhatsApp and the end user. For incoming messages, WhatsApp sends to MessageBird, which forwards to your Node.js application via a configured webhook URL using a signing key for verification.
+-----------------+ +---------------------+ +-----------------+ +----------------+
| Your Client |----->| Node.js/Express App |<---->| MessageBird API| <--->| WhatsApp Network|
| (e.g., Web App) | | (This Guide) | | | | (Users) |
+-----------------+ +---------------------+ +-----------------+ +----------------+
| API Request (Send) ^ Webhook (Receive)
| |
v |
+--------------------------------------+
| Securely handles API Keys & Webhooks |
+--------------------------------------+Prerequisites:
- Node.js (LTS version recommended) and npm (or yarn)
- MessageBird account
- Provisioned WhatsApp Business channel on MessageBird or access to MessageBird WhatsApp Sandbox for testing
- Basic understanding of Node.js, Express, and REST APIs
- Tool for testing API endpoints (like
curlor Postman) ngrokor similar tunneling service for testing webhooks locally
1. Project Setup for WhatsApp Integration
Initialize your Node.js project and install the necessary dependencies for WhatsApp messaging.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
bashmkdir node-whatsapp-messagebird cd node-whatsapp-messagebird -
Initialize Node.js Project: Initialize the project using npm, which creates a
package.jsonfile. The-yflag accepts default settings.bashnpm init -y -
Install Dependencies: Install
expressfor your web server,dotenvto manage environment variables, and themessagebirdSDK. Modern Express includes body parsing, sobody-parserisn't needed separately.bashnpm install express dotenv messagebirdexpress: Web framework for Node.js. Includes built-in middleware for parsing JSON (express.json()) and raw (express.raw()) request bodies.dotenv: Loads environment variables from a.envfile intoprocess.env. Essential for keeping secrets out of code.messagebird: Official MessageBird Node.js SDK (v4.0.1 as of January 2023), simplifying API interactions and including webhook signature verification utilities.
-
Create Project Structure: Set up a basic file structure.
bashtouch server.js .env .gitignoreserver.js: Main entry point for your Express application..env: Stores sensitive information like API keys. Never commit this file to version control..gitignore: Specifies intentionally untracked files that Git should ignore (like.envandnode_modules).
-
Configure
.gitignore: Add the following lines to your.gitignorefile to prevent committing sensitive data and unnecessary files:text# Dependencies node_modules # Environment variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Optional directory for build output dist -
Set up Environment Variables (
.env): Open the.envfile and add placeholders for your MessageBird API Key and WhatsApp Channel ID. You also need a secret for webhook verification.dotenv# MessageBird API Credentials MESSAGEBIRD_API_KEY=YOUR_MESSAGEBIRD_LIVE_API_KEY MESSAGEBIRD_CHANNEL_ID=YOUR_WHATSAPP_CHANNEL_ID MESSAGEBIRD_WEBHOOK_SIGNING_KEY=YOUR_WEBHOOK_SIGNING_KEY # Generate a secure random string # Server Configuration PORT=3000MESSAGEBIRD_API_KEY: Your live API key from the MessageBird Dashboard (Developers → API access). Essential for server startup and API calls.MESSAGEBIRD_CHANNEL_ID: The ID of your installed WhatsApp channel in MessageBird (Channels → WhatsApp → ID). Essential for sending messages.MESSAGEBIRD_WEBHOOK_SIGNING_KEY: A unique, cryptographically secure random string you generate. This verifies incoming webhooks genuinely originated from MessageBird. Required only if you implement the webhook endpoint. Keep this secret.PORT: The port your Express server listens on.
2. Implementing WhatsApp Message Sending
Build the core logic to send WhatsApp messages using the MessageBird SDK.
-
Basic Server Setup (
server.js): Set up a minimal Express server that loads environment variables and initializes the MessageBird client.javascript// server.js require('dotenv').config(); // Load environment variables from .env file const express = require('express'); const messagebird = require('messagebird').initClient(process.env.MESSAGEBIRD_API_KEY); const app = express(); const port = process.env.PORT || 3000; // --- Middleware will be applied per-route as needed --- // We need express.json() for sending API, express.raw() for webhooks. // --- Routes will be added here --- app.listen(port, () => { console.log(`Server listening on port ${port}`); // Essential variable checks if (!process.env.MESSAGEBIRD_API_KEY) { console.warn('CRITICAL WARNING: MESSAGEBIRD_API_KEY environment variable not set. MessageBird client initialization may fail.'); } if (!process.env.MESSAGEBIRD_CHANNEL_ID) { console.warn('WARNING: MESSAGEBIRD_CHANNEL_ID environment variable not set. Sending messages will likely fail.'); } // Conditional variable check if (!process.env.MESSAGEBIRD_WEBHOOK_SIGNING_KEY) { console.info('INFO: MESSAGEBIRD_WEBHOOK_SIGNING_KEY environment variable not set. Webhook verification will not function if the webhook endpoint is used.'); } }); module.exports = app; // Export for potential testing- Initialize
dotenvfirst to ensure environment variables are available. - Initialize the
messagebirdclient with the API key from.env. - Add startup warnings for essential environment variables. The warning for the signing key clarifies its specific purpose.
- Initialize
-
Get MessageBird Credentials:
- API Key: Log in to your MessageBird Dashboard. Navigate to Developers → API access. Copy your live API key. Paste it into the
MESSAGEBIRD_API_KEYfield in your.envfile. Never use your test key for production traffic. - Channel ID: Navigate to Channels in the Dashboard. Select your installed WhatsApp channel. Copy the Channel ID displayed. Paste it into the
MESSAGEBIRD_CHANNEL_IDfield in your.envfile. - Webhook Signing Key: Generate a strong, random string (e.g., using a password manager or online generator). Paste this into
MESSAGEBIRD_WEBHOOK_SIGNING_KEYin your.envfile. You'll also need to enter this exact same key in the MessageBird dashboard when configuring your webhook later.
- API Key: Log in to your MessageBird Dashboard. Navigate to Developers → API access. Copy your live API key. Paste it into the
-
Create the Sending Endpoint (
server.js): Add an Express route to handle POST requests for sending messages usingasync/awaitfor cleaner asynchronous code.javascript// server.js (continued) // ... previous setup code ... // Apply JSON body parser specifically for this route using built-in Express middleware app.post('/send-whatsapp', express.json(), async (req, res) => { const { recipientPhoneNumber, messageText } = req.body; // Basic input validation if (!recipientPhoneNumber || !messageText) { return res.status(400).json({ error: 'Missing required fields: recipientPhoneNumber and messageText' }); } // Validate phone number format (basic example, enhance as needed) // E.164 format is generally recommended: +[country code][subscriber number] if (!/^\+[1-9]\d{1,14}$/.test(recipientPhoneNumber)) { return res.status(400).json({ error: 'Invalid recipient phone number format. Use E.164 format (e.g., +14155552671).' }); } const params = { to: recipientPhoneNumber, // The recipient's WhatsApp number in E.164 format from: process.env.MESSAGEBIRD_CHANNEL_ID, // Your WhatsApp Channel ID from MessageBird type: 'text', content: { text: messageText, }, }; console.log(`Attempting to send message via Conversation API:`, JSON.stringify(params, null, 2)); try { // Use the Conversations API to send the message with async/await const response = await messagebird.conversations.start(params); console.log('MessageBird API Response:', JSON.stringify(response, null, 2)); // The 'response' object contains details about the created conversation/message // Typically includes an 'id' for the conversation res.status(200).json({ success: true, messageId: response.id, status: response.status }); } catch (err) { console.error('Error sending WhatsApp message:', err); // Provide more specific error feedback if possible const errorMessage = err.errors ? err.errors.map(e => e.description).join(', ') : (err.message || 'Failed to send message'); const statusCode = err.statusCode || 500; // Use statusCode from MessageBird error if available return res.status(statusCode).json({ error: 'MessageBird API error', details: errorMessage }); } }); // --- Webhook route will be added later --- // ... app.listen code ...- Use
express.json()middleware built into Express for this route. - Perform basic validation.
- Construct the
paramsobject formessagebird.conversations.start. - Use
async/awaitwith atry...catchblock for the API call, improving readability and error handling flow. - The
catchblock handles errors from the SDK, attempting to extract specific details and status codes.
- Use
3. Building the WhatsApp API Layer
The /send-whatsapp endpoint created above constitutes your initial API layer for sending messages.
-
Authentication/Authorization:
- Current State: The endpoint is currently open. In production, secure this endpoint before deployment. Deploying this code to the public internet without authentication poses a significant security risk, allowing anyone to send messages using your MessageBird account.
- Recommendations:
- API Keys: Issue unique API keys to clients. Validate the key in middleware.
- JWT Tokens: Protect the route using JWT validation middleware if part of a larger system.
- IP Whitelisting: Restrict access to known IP addresses.
- Implement robust authentication before any production deployment.
-
Request Validation:
- The current implementation includes basic checks.
- Enhancements: Use libraries like
express-validatororjoifor schema validation, type checking, length constraints, etc.
-
API Endpoint Documentation:
-
Endpoint:
POST /send-whatsapp -
Description: Sends a text message to a specified WhatsApp number via MessageBird. Requires Authentication in Production.
-
Request Body (JSON):
json{ "recipientPhoneNumber": "+14155552671", "messageText": "Hello from our Node.js app!" }recipientPhoneNumber(string, required): Recipient's phone number in E.164 format.messageText(string, required): The text content of the message.
-
Success Response (200 OK – JSON):
json{ "success": true, "messageId": "mb_conv_xxxxxxxxxxxxxxxxxxxx", "status": "pending" }success(boolean): Indicates if MessageBird accepted the request.messageId(string): The MessageBird Conversation ID.status(string): The initial status of the conversation (e.g.,pending,accepted).
-
Error Responses:
-
400 Bad Request: Missing fields or invalid phone number format.json{ "error": "Missing required fields: recipientPhoneNumber and messageText" }json{ "error": "Invalid recipient phone number format. Use E.164 format (e.g., +14155552671)." } -
401 Unauthorized/403 Forbidden(If Authentication added): Invalid or missing credentials. -
5xx/ Other MessageBird Errors: Error communicating with the MessageBird API.json{ "error": "MessageBird API error", "details": "Authentication failure" }
-
-
-
Testing with
curl: Replace placeholders with your actual recipient number and message. Ensure your server is running (node server.js).bashcurl -X POST http://localhost:3000/send-whatsapp \ -H "Content-Type: application/json" \ # Add Authentication header if implemented, e.g.: -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "recipientPhoneNumber": "+1xxxxxxxxxx", "messageText": "Test message from curl!" }'
4. Integrating with MessageBird (Setup Recap)
This section consolidates the critical MessageBird configuration steps:
-
Obtain API Key:
- Path: MessageBird Dashboard → Developers → API access → API KEY → Live API key.
- Purpose: Authenticates your application's requests to the MessageBird API.
- Storage: Store securely in the
MESSAGEBIRD_API_KEYenvironment variable.
-
Obtain WhatsApp Channel ID:
- Path: MessageBird Dashboard → Channels → Select your WhatsApp Channel → Channel ID.
- Purpose: Identifies which of your WhatsApp numbers/channels should send the message.
- Storage: Store in the
MESSAGEBIRD_CHANNEL_IDenvironment variable.
-
Obtain/Generate Webhook Signing Key:
- Path: Generate this yourself (secure random string). Enter it in MessageBird Dashboard → Channels → Select your WhatsApp Channel → Edit → Webhook section → Signing Key.
- Purpose: Verifies incoming webhook requests genuinely originated from MessageBird.
- Storage: Store in the
MESSAGEBIRD_WEBHOOK_SIGNING_KEYenvironment variable. Must match the value in the MessageBird dashboard.
-
Configure Webhook URL (For Receiving Messages – Covered Later):
- Path: MessageBird Dashboard → Channels → Select your WhatsApp Channel → Edit → Webhook section → Webhook URL.
- Purpose: Tells MessageBird where to send incoming message events. Must point to your publicly accessible
/webhookendpoint. - Local Testing: Use
ngrok http 3000to get a public URL (https://<your-ngrok-id>.ngrok.io) and set the webhook URL in MessageBird tohttps://<your-ngrok-id>.ngrok.io/webhook. - Production: Use your deployed application's public URL (e.g.,
https://yourapp.yourdomain.com/webhook).
Fallback mechanisms typically involve ensuring your application's high availability and monitoring MessageBird's status. Implement retries to mitigate transient network issues.
5. Error Handling, Logging, and Retries
Production systems require robust error handling and logging.
-
Consistent Error Strategy:
- Use standard HTTP status codes.
- Return consistent JSON error responses.
- Log errors server-side with detail (stack traces, request context).
-
Logging:
- Current: Basic
console.log/error. - Production: Integrate
winstonorpinofor structured (JSON), leveled logging with configurable outputs (console, file, external services). (See Section 10 for more).
- Current: Basic
-
Retry Mechanisms:
- Implement retries with exponential backoff for transient errors (e.g., 5xx, network timeouts) using libraries like
async-retry. - Caution: Only retry idempotent operations. Sending messages might have side effects if the first attempt succeeded but the response failed. Consider tracking attempts if duplicate prevention is critical. Retries add complexity.
- Implementation omitted for brevity.
- Implement retries with exponential backoff for transient errors (e.g., 5xx, network timeouts) using libraries like
-
Testing Error Scenarios:
- Use invalid credentials in
.env. - Send invalid requests to
/send-whatsapp. - Simulate network errors.
- Trigger webhook processing errors.
- Use invalid credentials in
6. Database Schema and Data Layer (Optional Extension)
Storing message logs, user data, or conversation state often requires a database.
-
Use Cases: Auditing, user profiles, chatbot state, status tracking.
-
Technology: Choose a database (PostgreSQL, MongoDB, etc.) and ORM/Query Builder (Prisma, Sequelize, etc.).
-
Example Schema (Conceptual – PostgreSQL with Prisma):
prisma// schema.prisma datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model MessageLog { id String @id @default(cuid()) messagebirdId String? @unique direction String // "incoming" or "outgoing" channelId String sender String // E.164 or channel ID recipient String // E.164 or channel ID content Json? // Message content status String? // e.g., pending, sent, delivered, read, failed timestamp DateTime @default(now()) // ... other fields ... } -
Implementation: Involves setting up the DB, installing/configuring the ORM, running migrations, and using the ORM client in your code to save/retrieve data.
7. Adding Security Features
Security is non-negotiable.
-
Input Validation and Sanitization:
- Use robust validation libraries (
express-validator,joi) for API inputs and webhook payloads. - Sanitize data before database storage or display (prevent XSS/injection).
- Use robust validation libraries (
-
Webhook Security (Signature Verification):
- Critical for
/webhook. Ensures requests are from MessageBird. Implemented in the next section.
- Critical for
-
API Key Security:
- Use environment variables (
.env, platform secrets). Never hardcode keys. - Rotate keys periodically.
- Use environment variables (
-
Rate Limiting:
- Protect endpoints from abuse using
express-rate-limit.
bashnpm install express-rate-limitjavascript// server.js (Add near the top) const rateLimit = require('express-rate-limit'); const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per window message: 'Too many requests, try again later.', standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers }); // Apply to relevant routes app.use('/send-whatsapp', apiLimiter); // Consider limits for /webhook too - Protect endpoints from abuse using
-
Other Protections:
- Use
helmet(npm install helmet) for security headers:const helmet = require('helmet'); app.use(helmet()); - Keep dependencies updated (
npm audit fix). - Use security scanning tools.
- Use
8. Handling Special Cases: Receiving WhatsApp Messages (Webhook)
Implement the endpoint to receive incoming WhatsApp messages via MessageBird webhooks.
-
Set up
ngrok(Local Development):- Install:
https://ngrok.com/download - Run:
ngrok http 3000(or yourPORT) - Note the
https://Forwarding URL.
- Install:
-
Configure MessageBird Webhook:
- Go to MessageBird Dashboard → Channels → Your WhatsApp Channel → Edit → Webhook section.
- Webhook URL: Enter your public URL +
/webhook(e.g.,https://<ngrok-id>.ngrok.io/webhookorhttps://yourapp.com/webhook). - Events: Check
message.created,message.updated. - Signing Key: Enter your
MESSAGEBIRD_WEBHOOK_SIGNING_KEYfrom.env. - Save.
-
Create the Webhook Endpoint (
server.js): Handle POST requests, verify the signature, and process the message.javascript// server.js (continued) // ... other routes and setup ... const crypto = require('crypto'); // Node.js built-in crypto module // Helper function to verify JWT signature (adapt based on MessageBird's actual JWT structure) // This is a conceptual example; MessageBird might provide a utility or specific algorithm. // Consult MessageBird documentation for the official verification method. // The SDK's internal 'verifyRequest' might be the intended way, but using internal paths is risky. // A manual JWT verification approach is shown here for illustration if no stable utility exists. function verifyMessageBirdJwt(token, secret) { try { const [headerEncoded, payloadEncoded, signatureEncoded] = token.split('.'); if (!headerEncoded || !payloadEncoded || !signatureEncoded) { throw new Error('Invalid JWT format'); } const signature = Buffer.from(signatureEncoded, 'base64url'); const dataToSign = `${headerEncoded}.${payloadEncoded}`; // IMPORTANT: Verify the algorithm used by MessageBird (e.g., HS256) const expectedSignature = crypto.createHmac('sha256', secret) .update(dataToSign) .digest(); if (!crypto.timingSafeEqual(signature, expectedSignature)) { throw new Error('Invalid signature'); } const payload = JSON.parse(Buffer.from(payloadEncoded, 'base64url').toString('utf8')); // Optional: Add timestamp validation (using payload claims like 'nbf', 'exp', 'iat') // const now = Math.floor(Date.now() / 1000); // if (payload.nbf && now < payload.nbf) throw new Error('Token not yet valid'); // if (payload.exp && now >= payload.exp) throw new Error('Token expired'); return payload; // Return the parsed payload if valid } catch (err) { console.error('JWT Verification Error:', err.message); throw new Error(`JWT verification failed: ${err.message}`); // Re-throw for the route handler } } // Webhook endpoint - Use raw body parser (built-in Express) for signature verification app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => { const signatureJwt = req.get('MessageBird-Signature-JWT'); // Official header name // const timestamp = req.get('MessageBird-Request-Timestamp'); // Use if needed by verification logic const webhookSigningKey = process.env.MESSAGEBIRD_WEBHOOK_SIGNING_KEY; if (!signatureJwt || !webhookSigningKey) { console.warn('Webhook received without signature JWT or signing key not configured.'); return res.status(400).send('Signature required'); } try { // PREFERRED METHOD: Use MessageBird SDK's built-in verification (v4.0.1+) // The SDK provides Express middleware for webhook verification. // Example: const { verifyRequest } = require('messagebird/lib/middleware'); // However, using internal lib paths may break with SDK updates. // Check official MessageBird Node.js SDK docs for stable verification method. // ALTERNATIVE METHOD: Manual JWT verification (shown below for educational purposes) // Verify the webhook signature using the JWT token. // MessageBird uses HMAC-SHA256 with the signing key. const verifiedPayload = verifyMessageBirdJwt(signatureJwt, webhookSigningKey); // If verification passes, `verifiedPayload` contains the parsed JSON object from the JWT console.log('Webhook JWT verified successfully. Payload:', JSON.stringify(verifiedPayload, null, 2)); // --- Process the incoming event --- if (verifiedPayload.type === 'message.created') { const message = verifiedPayload.message; const conversation = verifiedPayload.conversation; // Ensure contact and display name exist before logging const senderName = conversation?.contact?.displayName || 'Unknown Sender'; // Verify 'msisdn' is the correct field for sender ID in the payload. const senderNumber = conversation?.contact?.msisdn || 'Unknown Number'; console.log(`Received message from ${senderName} (${senderNumber}): ${message?.content?.text || '[Non-text content]'}`); // Add your logic: store message, trigger replies, etc. // Example: Simple echo reply (use cautiously) // Check direction and if the conversation allows replies (e.g., based on lastReceivedDatetime) /* if (message.direction === 'received' && conversation.lastReceivedDatetime) { const replyParams = { // Ensure conversation.contact.msisdn is the correct identifier for replying. to: conversation.contact.msisdn, from: process.env.MESSAGEBIRD_CHANNEL_ID, type: 'text', content: { text: `You said: ${message.content.text}` } }; try { // Use reply method with async/await const replyRes = await messagebird.conversations.reply(conversation.id, replyParams); console.log('Reply sent successfully:', replyRes.id); } catch (replyErr) { console.error('Error sending reply:', replyErr); } } */ } else if (verifiedPayload.type === 'message.updated') { // Handle status updates (e.g., delivered, read) const message = verifiedPayload.message; console.log(`Message status update for ${message.id}: ${message.status}`); // Update status in your database if tracking messages } else { console.log(`Received unhandled webhook type: ${verifiedPayload.type}`); } // --- End processing --- // Always respond quickly with 200 OK to acknowledge receipt res.status(200).send('Webhook received'); } catch (error) { // Log the specific error from verifyMessageBirdJwt or other issues console.error('Webhook processing failed:', error.message); res.status(400).send('Signature verification failed or processing error'); } }); // ... app.listen code ...- Use
express.raw({ type: 'application/json' })built-in middleware for the raw body Buffer, which might be needed depending on the exact signature verification method. - Important: The code includes a conceptual
verifyMessageBirdJwtfunction using Node'scryptomodule. This demonstrates manual JWT verification. Consult the official MessageBird documentation for their recommended and stable method for webhook signature verification. Using internal SDK paths likemessagebird/lib/webhooks/verifyis highly discouraged due to potential breaking changes in SDK updates. If MessageBird provides a stable utility function, use that instead. - The example assumes the JWT payload contains the webhook data. Adjust if the raw body is needed alongside the JWT for verification.
- The
try...catchblock handles verification errors. - If successful, process based on
verifiedPayload.type. - Comments highlight potential areas needing verification against MessageBird's specific payload structure (e.g.,
msisdn,lastReceivedDatetime). - The example reply uses
async/await. - Crucially, respond
200 OKquickly. Offload heavy processing if necessary.
- Use
9. Performance Optimizations
For higher scale:
- Asynchronous Operations: Ensure all I/O (API calls, DB) is non-blocking (using
async/awaitor Promises correctly). - Caching: Use Redis/Memcached for frequently accessed, slow-changing data (e.g., config, user profiles).
- Load Balancing: Deploy multiple instances behind a load balancer (Nginx, AWS ELB). Design stateless or use sticky sessions/external session store if needed.
- Database Optimization: Use indexing, efficient queries, connection pooling.
- Resource Monitoring: Use
pm2or container orchestrator features to monitor CPU/memory. Optimize bottlenecks.
10. Monitoring, Observability, and Analytics
Understand production behavior:
-
Health Checks: Implement a
/healthendpoint.javascript// server.js (add this route) app.get('/health', (req, res) => { // Add more checks if needed (e.g., DB connectivity) res.status(200).json({ status: 'UP', timestamp: new Date().toISOString() }); });Monitor this endpoint externally.
-
Logging: Centralize structured logs (JSON via Winston/Pino) to services like Datadog, Logz.io, Papertrail, ELK stack.
-
Error Tracking: Use Sentry, Bugsnag to capture and alert on runtime errors.
-
Metrics: Track KPIs (API latency, webhook time, message counts, error rates) using
prom-client(Prometheus) or APM tools (Datadog APM, New Relic). -
Dashboards: Visualize logs and metrics (Kibana, Grafana, Datadog) for system overview.
11. Troubleshooting and Common Issues
Common issues when integrating WhatsApp with Node.js:
- Invalid API Key/Channel ID: Check
.envagainst MessageBird dashboard (use live key). Look forAuthentication failureor channel errors in logs or MessageBird API responses. - Webhook Verification Failed:
400 Bad Requeston/webhook.- Ensure
MESSAGEBIRD_WEBHOOK_SIGNING_KEYin.envexactly matches the key configured in the MessageBird dashboard webhook settings. - Confirm the correct body parser (
express.raw) is used for the webhook route if required by the verification method. - Verify the signature verification logic (JWT or other method) matches MessageBird's specification. Check required headers (
MessageBird-Signature-JWT,MessageBird-Request-Timestampif used). - Check timestamp tolerance if timestamp validation is enabled. Significant clock skew between MessageBird and your server can cause failures.
- Ensure
- Message Not Sent/Received:
- Check MessageBird Conversation Logs in their dashboard for detailed errors or status updates.
- Verify recipient number format (E.164:
+followed by country code and number) and ensure the user opted-in to receive messages from your business. - Check MessageBird account balance and channel status (active, configured correctly).
- Confirm the webhook URL configured in MessageBird points correctly to your running application (
ngrokURL for local testing, public deployment URL for production). - Check your server application logs (
console.log, structured logs) for any errors during message sending or webhook processing.
- Rate Limits:
429 Too Many Requestsresponse from MessageBird API or potential blocking. Implement client-side rate limiting if sending many messages; respect MessageBird and WhatsApp platform limits. Check MessageBird documentation for specific limits. - WhatsApp Template Messages: Crucial: To initiate conversations with users or to reply more than 24 hours after the user's last message, you must use pre-approved WhatsApp Message Templates. Sending free-form text (
type: 'text') is only permitted within this 24-hour customer service window. Attempting to send free-form text outside this window results in message delivery failures. Consult the official MessageBird and WhatsApp Business Platform documentation for details on template creation, approval process, and how to send template messages via the API. - Sandbox Limitations: The MessageBird WhatsApp Sandbox environment has limitations (e.g., only works with pre-registered test numbers, potential behavioral differences from live). Always test thoroughly on a live, provisioned WhatsApp channel before full production deployment.
- Opt-Ins: Critical: WhatsApp policy strictly requires businesses to obtain explicit user opt-in before initiating messages to them. Failure to comply with opt-in requirements can lead to suspension or banning of your WhatsApp channel. Ensure your user acquisition and communication flows clearly document user consent. Consult the official WhatsApp Business Policy and MessageBird documentation for detailed guidance on obtaining and managing opt-ins.
FAQ: MessageBird WhatsApp Integration
How do I send WhatsApp messages with Node.js?
Use the MessageBird Conversations API with their Node.js SDK. Install the messagebird package, initialize it with your API key, and call messagebird.conversations.start() with your WhatsApp channel ID, recipient number in E.164 format, and message content.
What is the 24-hour messaging window in WhatsApp Business API?
WhatsApp allows businesses to send free-form messages only within 24 hours of the user's last message. Outside this window, you must use pre-approved WhatsApp Message Templates. This policy ensures users only receive messages from businesses they've actively engaged with recently.
How do I verify MessageBird webhook signatures?
MessageBird sends webhooks with a MessageBird-Signature-JWT header containing an HMAC-SHA256 signed JWT token. Verify this signature using your webhook signing key (configured in both your .env file and MessageBird dashboard). The MessageBird Node.js SDK v4.0.1+ includes Express middleware for signature verification.
Do I need a WhatsApp Business Account for MessageBird integration?
Yes, you need a provisioned WhatsApp Business channel through MessageBird. You can start with the MessageBird WhatsApp Sandbox for testing, but production use requires an approved WhatsApp Business API account linked to your MessageBird dashboard.
What phone number format should I use for WhatsApp messages?
Always use E.164 format for phone numbers: a plus sign (+) followed by the country code and subscriber number with no spaces or special characters (e.g., +14155552671). This ensures proper message routing across international WhatsApp networks.
How do I handle incoming WhatsApp messages in Express?
Create a POST endpoint (e.g., /webhook) that accepts incoming webhooks from MessageBird. Use express.raw() middleware, verify the webhook signature, parse the message payload, and respond with 200 OK quickly. Configure this endpoint URL in your MessageBird dashboard under your WhatsApp channel settings.
What are the rate limits for sending WhatsApp messages?
Rate limits vary by your MessageBird account tier and WhatsApp Business API policies. Implement exponential backoff retry logic and respect 429 Too Many Requests responses. For high-volume messaging, contact MessageBird about enterprise tier limits and use queuing systems like Redis or RabbitMQ.
Can I use the same WhatsApp channel for multiple applications?
Yes, you can use the same WhatsApp Business channel for multiple applications, but each application should have its own API key and webhook configuration. Ensure each application's webhook endpoint is correctly configured in the MessageBird dashboard.
What are WhatsApp Message Templates?
WhatsApp Message Templates are pre-approved messages that businesses can send to users. They're required for initiating conversations with users or replying more than 24 hours after the user's last message. Templates can include placeholders for dynamic content and must be approved by WhatsApp before use.
How do I track WhatsApp message delivery status?
Track WhatsApp message delivery status by storing the MessageBird Conversation ID (messageId) and checking the conversation's status in the MessageBird dashboard. MessageBird provides detailed conversation logs that include delivery status updates.
Can I send WhatsApp messages to any phone number?
No, you can only send WhatsApp messages to users who opted-in to receive messages from your business. WhatsApp requires businesses to obtain explicit user consent before sending messages.
What happens if a WhatsApp user blocks my business?
If a WhatsApp user blocks your business, they won't receive any further messages from you. You can check the user's status in the MessageBird dashboard to see if they've blocked your business.
Can I send WhatsApp messages from a non-WhatsApp Business account?
No, WhatsApp Business API requires a provisioned WhatsApp Business channel. You cannot use a personal WhatsApp account to send business messages.
What happens if I send WhatsApp messages to a non-WhatsApp number?
If you send WhatsApp messages to a non-WhatsApp number, the messages will fail to deliver. WhatsApp only allows businesses to send messages to users who opted-in to receive messages from your business.
Can I send WhatsApp messages to multiple recipients at once?
Yes, you can send WhatsApp messages to multiple recipients by using the MessageBird Conversations API. You can send messages to up to 100 recipients in a single request.
Can I send WhatsApp messages from multiple channels?
Yes, you can send WhatsApp messages from multiple channels. Each channel has its own WhatsApp number and can be used to send messages to different recipients.