code examples
code examples
MessageBird WhatsApp API Integration with Next.js and Node.js (2025 Guide)
Complete tutorial: Integrate MessageBird WhatsApp Business API with Next.js 15 and Node.js. Learn to send messages, receive webhooks, handle templates, and build production-ready WhatsApp messaging apps.
Developer Guide: Integrating MessageBird WhatsApp with Next.js and Node.js
This comprehensive guide shows you how to integrate the MessageBird WhatsApp Business API into a Next.js application. You'll build a production-ready system that sends and receives WhatsApp messages, leveraging Next.js API routes, Node.js runtime, and MessageBird's WhatsApp Business platform.
This integration enables businesses to engage customers directly on WhatsApp for customer support, notifications, alerts, two-factor authentication, and automated messaging – all managed through a modern, scalable Next.js application. Whether you're building a customer service platform, notification system, or marketing automation tool, this guide provides the foundation for WhatsApp Business API integration.
Important: MessageBird rebranded to Bird in February 2024, though the API endpoints, SDK (messagebird npm package), and functionality remain unchanged. This guide uses the original MessageBird SDK and terminology interchangeably with Bird.
Project Overview and Goals
What You'll Build:
A Next.js application with the following capabilities:
- API endpoint to send outbound WhatsApp messages (text and pre-approved templates) via MessageBird.
- API endpoint configured as a MessageBird webhook to receive inbound WhatsApp messages.
- Secure handling of API credentials and webhook secrets.
- Basic logging and error handling for message operations.
- (Optional) Basic database integration to log message history.
Problem Solved:
This guide helps you programmatically interact with customers on WhatsApp using MessageBird, integrated within Next.js. It simplifies the process compared to managing the complexities of the direct WhatsApp Business API infrastructure.
<!-- GAP: Lacks comparison of MessageBird vs. direct WhatsApp Business API vs. competitors (Type: Substantive) -->Technologies Used:
- Next.js: A React framework for building full-stack web applications, providing server-side rendering, static site generation, and API routes for building RESTful endpoints.
- Node.js: The JavaScript runtime environment used by Next.js for backend operations.
- MessageBird: A communication platform as a service (CPaaS) providing APIs for various channels, including WhatsApp. You'll use their Node.js SDK.
- dotenv: For managing environment variables securely.
- (Optional) Prisma: A modern database toolkit for Node.js and TypeScript, used here for optional message logging.
- (Optional) PostgreSQL/SQLite: Relational databases for storing message logs.
System Architecture:
+-----------------+ +---------------------+ +-----------------+ +----------+
| User/Client App |----->| Next.js Application |----->| MessageBird API |----->| WhatsApp |
| (Browser/API) | | (API Routes) |<-----| (Webhooks) |<-----| |
+-----------------+ +---------------------+ +-----------------+ +----------+
| |
| (Optional) |
| +-----------------+ |
| | Database | |
| | (Prisma) | |
| +-----------------+ |
+---------------------+Prerequisites:
- Node.js (v20 LTS or v22 LTS recommended for 2025; Next.js 15 requires minimum v18.18.0, but v18 was deprecated in Next.js 15.4) and npm/yarn installed.
- A MessageBird account with access to the WhatsApp Business API.
- A WhatsApp Business channel configured in your MessageBird dashboard (follow their onboarding quickstart).
- A registered and approved WhatsApp number linked to your MessageBird channel (must be E.164 format: +[country code][number], up to 15 digits).
- Basic understanding of Next.js, React, and API concepts.
- (Optional) Docker and Docker Compose for containerized database setup.
- (Optional)
ngrokor alternatives like Pinggy, Cloudflare Tunnel, or Tunnelmole for testing webhooks locally. FreengrokURLs are temporary and change each restart, requiring webhook URL updates in MessageBird; consider Pinggy (unlimited bandwidth, $3/month) or Cloudflare Tunnel (free, no bandwidth limits) for persistent URLs during development.
Expected Outcome:
A functional Next.js application that sends text and template messages via MessageBird's WhatsApp API and receives incoming messages through a configured webhook. This guide provides a functional starting point. While it includes patterns for production (security checks, logging suggestions), the core code examples use basic constructs (like console.log) and include placeholders (// TODO:) requiring further implementation for a truly production-ready system.
1. Set Up Your Next.js Project for MessageBird WhatsApp Integration
Initialize your Next.js project and install the necessary dependencies for WhatsApp Business API integration.
1.1 Create Next.js App:
Open your terminal and run:
npx create-next-app@latest messagebird-whatsapp-nextjs --typescript --eslint --tailwind --src-dir --app --import-alias "@/*"
cd messagebird-whatsapp-nextjs(Adjust flags like --typescript, --tailwind based on your preferences. This guide assumes TypeScript and the src/ directory structure).
1.2 Install Dependencies:
Install the MessageBird Node.js SDK and dotenv for environment variables. If using Prisma, install it as well.
npm install messagebird dotenv
# Optional: Install Prisma for database logging (Prisma 6.7.0+ compatible with Next.js 15.3.1+)
npm install prisma @prisma/client
# Optional: Initialize Prisma (choose your database provider)
npx prisma init --datasource-provider postgresql # or sqlite, mysql, etc.1.3 Configure Environment Variables:
Create a file named .env.local in your project root. This file stores sensitive credentials and configuration. Never commit this file to version control.
# .env.local
# MessageBird Credentials (Get from MessageBird Dashboard -> Developers -> API Access)
MESSAGEBIRD_API_KEY="YOUR_LIVE_OR_TEST_API_KEY"
# MessageBird WhatsApp Channel ID (Get from MessageBird Dashboard -> Channels -> WhatsApp -> Your Channel)
MESSAGEBIRD_CHANNEL_ID="YOUR_WHATSAPP_CHANNEL_ID"
# MessageBird Webhook Signing Secret (Generate/Get from Webhook settings in MessageBird Dashboard)
MESSAGEBIRD_WEBHOOK_SECRET="YOUR_WEBHOOK_SIGNING_SECRET"
# (Optional) Database URL for Prisma
# Example for PostgreSQL using Docker Compose (see section 6)
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
# Example for SQLite
# DATABASE_URL="file:./dev.db"
# Base URL for your deployed application (needed for setting the webhook)
# For local development with ngrok, this might be like: https://xxxxx.ngrok-free.app
# For production, it will be your domain: https://yourapp.com
NEXT_PUBLIC_APP_URL="http://localhost:3000" # Default, update as neededExplanation:
MESSAGEBIRD_API_KEY: Authenticates your application with the MessageBird API. Use your Live key for production. Caution: Never commit your.env.localfile containing live keys to version control. Use Test keys during development when possible.MESSAGEBIRD_CHANNEL_ID: Identifies the specific WhatsApp channel you configured in MessageBird (a long alphanumeric string found in Dashboard > Channels > WhatsApp).MESSAGEBIRD_WEBHOOK_SECRET: Verifies that incoming webhook requests genuinely originate from MessageBird. MessageBird signs requests using HMAC-SHA256 with this secret, sent via theMessageBird-Signature-JWTheader.DATABASE_URL: Connection string for Prisma if you're implementing database logging.NEXT_PUBLIC_APP_URL: The publicly accessible base URL of your application, used when configuring the webhook URL in the MessageBird dashboard.
1.4 Project Structure:
Your relevant project structure should look similar to this:
messagebird-whatsapp-nextjs/
├── src/
│ ├── app/
│ │ ├── api/ # API Routes
│ │ │ ├── send-whatsapp/ # Endpoint for sending messages
│ │ │ │ └── route.ts
│ │ │ ├── messagebird-webhook/ # Endpoint for receiving messages
│ │ │ │ └── route.ts
│ │ │ └── health/ # Basic health check
│ │ │ └── route.ts
│ │ ├── page.tsx # Example frontend page (optional)
│ │ └── layout.tsx
│ ├── lib/ # Utility functions/SDK initialization
│ │ ├── messagebird.ts
│ │ └── prisma.ts # (Optional) Prisma client instance
│ └── ... (other Next.js files)
├── prisma/ # (Optional) Prisma schema and migrations
│ └── schema.prisma
├── .env.local # Environment variables (DO NOT COMMIT)
├── next.config.mjs
├── package.json
└── tsconfig.json1.5 Initialize MessageBird SDK:
Create a utility file to initialize the MessageBird client.
// src/lib/messagebird.ts
import messagebird from 'messagebird';
// Ensure environment variable is loaded (though Next.js handles .env.local automatically)
if (!process.env.MESSAGEBIRD_API_KEY) {
throw new Error("Missing MESSAGEBIRD_API_KEY environment variable");
}
const mbClient = messagebird(process.env.MESSAGEBIRD_API_KEY);
export default mbClient;This initializes the SDK using the API key from your environment variables, making it ready to use in your API routes.
<!-- DEPTH: Missing discussion of SDK client lifecycle, connection pooling, or singleton patterns (Priority: Medium) --> <!-- GAP: No error handling for invalid API keys or network failures during initialization (Type: Substantive) -->2. How to Send WhatsApp Messages Using MessageBird API
Create the Next.js API route responsible for sending outbound WhatsApp messages through MessageBird. This endpoint handles both text messages and pre-approved WhatsApp templates for business-initiated conversations.
<!-- GAP: Missing overview of WhatsApp message types and when to use each (Type: Substantive) --> <!-- GAP: No discussion of message templates approval process and timeline (Type: Critical) -->2.1 Create the API Route:
Create the file src/app/api/send-whatsapp/route.ts.
// src/app/api/send-whatsapp/route.ts
import { NextRequest, NextResponse } from 'next/server';
import mbClient from '@/lib/messagebird'; // Import initialized client
import { MessageBird } from 'messagebird/types'; // Import types for better DX
// Define expected request body structure (using interface or Zod for validation)
interface SendMessageRequestBody {
to: string; // Recipient phone number in E.164 format (e.g., +14155552671). Required: +[country code][number], up to 15 digits. Excludes short codes and numbers from Crimea, Cuba, Iran, North Korea, Syria.
text?: string; // For simple text messages
template?: { // For template messages
name: string; // Approved template name
language: string; // e.g., 'en_US'
components?: MessageBird.MessageRequestTemplate['components']; // Parameters
};
}
export async function POST(request: NextRequest) {
console.log("Received request to /api/send-whatsapp"); // Note: Replace with structured logger in production
let body: SendMessageRequestBody;
try {
body = await request.json();
console.log("Request body:", body); // Note: Replace with structured logger in production
// Basic validation (consider using Zod for robust validation)
if (!body.to || (!body.text && !body.template)) {
console.error("Validation failed: Missing 'to' or message content ('text'/'template')"); // Note: Replace with structured logger in production
return NextResponse.json({ error: "Missing 'to' or message content ('text'/'template')" }, { status: 400 });
}
if (!process.env.MESSAGEBIRD_CHANNEL_ID) {
console.error("Configuration error: Missing MESSAGEBIRD_CHANNEL_ID"); // Note: Replace with structured logger in production
return NextResponse.json({ error: "Server configuration error: Channel ID missing" }, { status: 500 });
}
} catch (error) {
console.error("Failed to parse request body:", error); // Note: Replace with structured logger in production
return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
}
const params: MessageBird.MessageRequest = {
to: body.to,
from: process.env.MESSAGEBIRD_CHANNEL_ID, // Your MessageBird WhatsApp Channel ID
type: 'text', // Default to text, override if template is present
content: {},
};
// Construct message content based on request
if (body.template) {
params.type = 'hsm'; // HSM (Highly Structured Message) is used for templates
params.content = {
hsm: {
// IMPORTANT: Replace with your WABA (WhatsApp Business Account) template namespace!
// Find in MessageBird Dashboard > Templates > Template Details (e.g., 'cdb2df51_9816_c754_c5a4_64cdabdcad3e')
// The namespace is a bundle of language packs for your business, required for template message routing.
namespace: 'your_messagebird_template_namespace', // Get this from MessageBird template details
templateName: body.template.name,
language: {
policy: 'deterministic',
code: body.template.language,
},
components: body.template.components || [], // Pass parameters if provided
},
};
console.log("Sending template message:", params); // Note: Replace with structured logger in production
} else if (body.text) {
params.type = 'text';
params.content = { text: body.text };
console.log("Sending text message:", params); // Note: Replace with structured logger in production
} else {
// This case should be caught by initial validation, but good to have defensive coding
console.error("No message content provided."); // Note: Replace with structured logger in production
return NextResponse.json({ error: "No message content provided" }, { status: 400 });
}
try {
// Wrap the callback-based SDK method in a Promise
const result = await new Promise<MessageBird.Message>((resolve, reject) => {
mbClient.messages.create(params, (err, response) => {
if (err) {
console.error("MessageBird API Error:", err); // Note: Replace with structured logger in production
// Extract more specific error info if available
const errorDetails = err.errors ? JSON.stringify(err.errors) : err.message;
reject(new Error(`MessageBird API Error: ${errorDetails}`));
} else if (response) {
console.log("MessageBird API Success:", response); // Note: Replace with structured logger in production
resolve(response);
} else {
reject(new Error("MessageBird API returned undefined response"));
}
});
});
return NextResponse.json({ success: true, messageId: result.id, status: result.status }, { status: 201 }); // 201 Created is suitable
} catch (error: any) {
console.error("Error sending message:", error); // Note: Replace with structured logger in production
// Return a generic error to the client, log the specific error server-side
return NextResponse.json({ success: false, error: 'Failed to send message', details: error.message }, { status: 500 });
}
}
// Optional: Add a GET handler for basic testing or documentation
export async function GET() {
return NextResponse.json({ message: "POST to this endpoint to send a WhatsApp message." });
}Explanation:
- Import Dependencies: Import
NextRequest,NextResponse, the initializedmbClient, and MessageBird types. - Define Request Body: An interface
SendMessageRequestBodydefines the expected structure of incoming POST requests. Use a validation library like Zod for production. - POST Handler: This asynchronous function handles POST requests.
- Parse & Validate: Parse the JSON body and perform basic validation to ensure the recipient (
to) and message content (textortemplate) are present. Check for theMESSAGEBIRD_CHANNEL_ID. - Construct Parameters: Build the
paramsobject required by themessagebird.messages.createmethod.to: Recipient number.from: Your MessageBird WhatsApp Channel ID (from.env.local).type: Set to'text'or'hsm'(for templates).content: An object containing either{ text: "…" }or{ hsm: { … } }.
- Template Handling: If
body.templateexists, settypetohsmand construct thehsmobject.namespace: Crucial: Find your template namespace in the MessageBird dashboard (usually associated with your Facebook Business Manager or template details). Replace'your_messagebird_template_namespace'with this value.templateName: The name of your pre-approved template.language: The language code (e.g.,en_US).components: An array to pass parameters (variables) for your template placeholders. The structure depends on your template (header, body, buttons). Refer to MessageBird's documentation for component structure.
- Text Handling: If
body.textexists, settypetotextandcontentaccordingly. - API Call: Use
mbClient.messages.createto send the message. (Note: This example wraps the callback-based SDK method in aPromisefor use withasync/await. Check the documentation for the specific version of themessagebirdSDK you're using; newer versions might offer direct Promise support.) - Error Handling: A
try…catchblock handles potential errors during parsing, validation, or the API call itself. Specific MessageBird API errors are logged server-side. - Response: Return a JSON response indicating success (with message ID) or failure.
- Logging: Uses
console.log/console.errorfor simplicity in this example. For production, replace with a structured logger (see Section 5.2).
3. How to Receive WhatsApp Messages Using MessageBird Webhooks
Create the webhook endpoint that MessageBird calls whenever your WhatsApp Business number receives an inbound message or delivery status update. This enables real-time two-way messaging capabilities in your Next.js application.
<!-- GAP: Missing overview of webhook event types and their meanings (Type: Substantive) --> <!-- DEPTH: Lacks discussion of webhook reliability, retry mechanisms, and idempotency (Priority: High) -->3.1 Create the Webhook API Route:
Create the file src/app/api/messagebird-webhook/route.ts.
// src/app/api/messagebird-webhook/route.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
import { MessageBird } from 'messagebird/types'; // Webhook payload types
// Optional: Import Prisma client if logging messages
// import prisma from '@/lib/prisma';
// --- Webhook Signature Verification ---
async function verifySignature(request: NextRequest): Promise<boolean> {
// MessageBird signs webhooks using HMAC-SHA256 with MessageBird-Signature-JWT header (updated 2024)
const signature = request.headers.get('messagebird-signature');
const timestamp = request.headers.get('messagebird-request-timestamp');
const webhookSecret = process.env.MESSAGEBIRD_WEBHOOK_SECRET;
if (!signature || !timestamp || !webhookSecret) {
console.warn('Webhook verification failed: Missing signature, timestamp, or secret'); // Note: Replace with structured logger in production
return false;
}
// Clone the request to read the body safely
const requestClone = request.clone();
const bodyText = await requestClone.text(); // Read body as raw text
// Construct the signed payload string (MessageBird format: timestamp.secret.body)
const signedPayload = `${timestamp}.${webhookSecret}.${bodyText}`;
// Calculate the expected signature using HMAC-SHA256
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload)
.digest('hex');
// Compare signatures securely using timing-safe comparison to prevent timing attacks
const isValid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
if (!isValid) {
console.warn('Webhook verification failed: Invalid signature'); // Note: Replace with structured logger in production
console.log(`Received Signature: ${signature}`); // Note: Replace with structured logger in production
console.log(`Expected Signature: ${expectedSignature}`); // Note: Replace with structured logger in production
// console.log(`Timestamp: ${timestamp}`); // Be cautious logging timestamps if needed for debugging
// console.log(`Body: ${bodyText}`); // Be very cautious logging raw body content
}
return isValid;
}
// --- Main Webhook Handler ---
export async function POST(request: NextRequest) {
console.log("Received POST request to /api/messagebird-webhook"); // Note: Replace with structured logger in production
// 1. Verify the signature FIRST
const isVerified = await verifySignature(request);
if (!isVerified) {
console.error("Webhook signature verification failed."); // Note: Replace with structured logger in production
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
console.log("Webhook signature verified successfully."); // Note: Replace with structured logger in production
// 2. Parse the JSON body (only after verification)
let payload: MessageBird.WebhookPayload;
try {
payload = await request.json();
console.log('Received webhook payload:', JSON.stringify(payload, null, 2)); // Log the full payload for debugging - Replace with structured logger in production
} catch (error) {
console.error('Failed to parse webhook payload:', error); // Note: Replace with structured logger in production
return NextResponse.json({ error: 'Invalid payload' }, { status: 400 });
}
// 3. Process the event based on type
try {
switch (payload.type) {
case 'message.created':
case 'message.updated': // Handle status updates (sent, delivered, read)
const messageEvent = payload as MessageBird.MessageEvent; // Type assertion
console.log(`Processing ${payload.type} event for message ID: ${messageEvent.message.id}`); // Note: Replace with structured logger in production
console.log(`Direction: ${messageEvent.message.direction}, Status: ${messageEvent.message.status}`); // Note: Replace with structured logger in production
// Only process incoming messages ('mo' - mobile originated)
if (messageEvent.message.direction === 'mo') {
console.log(`Received message from ${messageEvent.message.originator}:`); // Note: Replace with structured logger in production
console.log(`Type: ${messageEvent.message.content.type}, Content:`, messageEvent.message.content); // Note: Replace with structured logger in production
// --- Optional: Log incoming message to Database ---
/*
if (prisma) {
try {
await prisma.messageLog.create({
data: {
messageId: messageEvent.message.id,
channelId: messageEvent.message.channelId,
conversationId: messageEvent.message.conversationId,
direction: 'incoming',
status: messageEvent.message.status ?? 'received',
sender: messageEvent.message.originator ?? 'unknown',
recipient: messageEvent.message.recipient, // Your number/channel
content: JSON.stringify(messageEvent.message.content), // Store full content as JSON string
receivedAt: new Date(messageEvent.message.createdDatetime ?? Date.now()),
platform: 'whatsapp'
}
});
console.log(`Logged incoming message ${messageEvent.message.id} to DB`); // Note: Replace with structured logger in production
} catch (dbError) {
console.error("Failed to log incoming message to DB:", dbError); // Note: Replace with structured logger in production
// Decide if you should still return 200 OK to MessageBird
}
}
*/
// --- End Optional DB Log ---
// TODO: Implement your business logic here based on the incoming message
// Example: Route to support, trigger automated reply, etc.
} else if (messageEvent.message.direction === 'mt') { // Mobile Terminated (outgoing) status updates
console.log(`Status update for outgoing message ${messageEvent.message.id}: ${messageEvent.message.status}`); // Note: Replace with structured logger in production
// --- Optional: Update message status in Database ---
/*
if (prisma) {
try {
await prisma.messageLog.updateMany({ // Use updateMany in case the initial record wasn't created yet
where: { messageId: messageEvent.message.id },
data: {
status: messageEvent.message.status ?? 'unknown',
// Optionally update other fields like deliveredAt, readAt based on status
}
});
console.log(`Updated status for message ${messageEvent.message.id} to ${messageEvent.message.status} in DB`); // Note: Replace with structured logger in production
} catch (dbError) {
console.error("Failed to update message status in DB:", dbError); // Note: Replace with structured logger in production
}
}
*/
// --- End Optional DB Log ---
}
break;
// Handle other event types if needed (e.g., conversation events)
// case 'conversation.created':
// // Handle conversation events
// break;
default:
console.log(`Received unhandled event type: ${payload.type}`); // Note: Replace with structured logger in production
}
// 4. Acknowledge receipt to MessageBird
// Respond quickly with a 200 OK, even if background processing continues.
return NextResponse.json({ success: true }, { status: 200 });
} catch (processingError) {
console.error('Error processing webhook payload:', processingError); // Note: Replace with structured logger in production
// Still return 200 OK if possible to prevent MessageBird retries,
// but log the error for investigation. Use a 500 for critical failures.
// Depending on the error, you might want to respond differently.
return NextResponse.json({ success: false, error: 'Failed to process webhook' }, { status: 500 });
}
}
// Optional: Add GET handler for basic testing/verification during webhook setup
export async function GET() {
console.log("Received GET request to /api/messagebird-webhook"); // Note: Replace with structured logger in production
// MessageBird doesn't typically use GET for verification, but it can be useful for simple reachability tests.
return NextResponse.json({ message: "MessageBird Webhook endpoint is active. Use POST for events." });
}Explanation:
- Import Dependencies: Includes
cryptofor signature verification and optionalprisma. verifySignatureFunction:- Retrieves the
messagebird-signature,messagebird-request-timestampheaders, and yourMESSAGEBIRD_WEBHOOK_SECRET. - Reads the raw request body without parsing it as JSON first. Cloning the request is essential as the body stream can only be read once.
- Constructs the
signedPayloadstring exactly as MessageBird requires:timestamp.webhookSecret.rawBody. - Calculates the expected HMAC SHA256 hash using your secret.
- Uses
crypto.timingSafeEqualfor secure comparison to prevent timing attacks. - Returns
trueif signatures match,falseotherwise. Logs warnings on failure.
- Retrieves the
- POST Handler:
- Verify Signature: Call
verifySignatureimmediately. If verification fails, return a401 Unauthorizedresponse and stop processing. This is critical for security. - Parse Payload: Only after verification succeeds, parse the JSON payload.
- Process Event: Use a
switchstatement onpayload.typeto handle different events.message.created/message.updated: Log details about the message, including direction (mofor incoming,mtfor outgoing) and status.- Incoming Message Logic (
mo): Extract sender (originator), content type, and content. Includes commented-out Prisma code for logging the message to a database. The// TODO:block is where you implement your specific business logic for responding to or acting upon received messages. - Outgoing Status Update Logic (
mt): Log status updates for messages you sent. Includes commented-out Prisma code for updating the status in the database.
- Acknowledge Receipt: Return a
200 OKresponse to MessageBird promptly. This signals that you've received the event. Delaying this response can cause MessageBird to retry sending the webhook. - Error Handling: Catch errors during payload processing and log them. Return
200 OKfor non-critical processing errors to avoid webhook retries, but return500if the error prevents meaningful processing. - Logging: Uses
console.log/console.error/console.warnfor simplicity. For production, replace with a structured logger (see Section 5.2).
- Verify Signature: Call
4. Configure MessageBird Dashboard for WhatsApp API Access
Connect your Next.js application to your MessageBird account by obtaining API credentials, configuring the WhatsApp channel, and setting up webhooks for message delivery.
<!-- DEPTH: Section lacks screenshots or visual guides showing dashboard navigation (Priority: High) -->4.1 Obtain API Key:
- Navigate to your MessageBird Dashboard.
- Go to Developers > API access.
- Copy your Live API key (or Test API key for development).
- Paste this value into your
.env.localfile forMESSAGEBIRD_API_KEY.
4.2 Obtain WhatsApp Channel ID:
- In the MessageBird Dashboard, go to Channels.
- Select your configured WhatsApp channel.
- Find the Channel ID (usually a long alphanumeric string).
- Paste this value into
MESSAGEBIRD_CHANNEL_IDin your.env.local.
4.3 Configure Webhook:
- In your WhatsApp Channel settings in the MessageBird Dashboard, find the Webhook or Events section.
- You need to provide a publicly accessible URL for your webhook handler.
- Production: Use your deployed application's URL:
https://your-app-domain.com/api/messagebird-webhook - Local Development:
- Start your Next.js app:
npm run dev - Expose local server using a tunneling service:
- ngrok (free, temporary URLs):
ngrok http 3000→ Use thehttpsURL (e.g.,https://a1b2-c3d4.ngrok-free.app/api/messagebird-webhook). URLs change on restart. - Pinggy (recommended for development):
ssh -p 443 -R0:localhost:3000 a.pinggy.io→ Provides persistent URLs, unlimited bandwidth. Free tier available, paid plans start at $3/month. - Cloudflare Tunnel (free, persistent): Install
cloudflared, runcloudflared tunnel --url http://localhost:3000→ No bandwidth limits, stable URLs. - Tunnelmole (open-source):
tmole 3000→ Simple, fast, free for normal use.
- ngrok (free, temporary URLs):
- Use the tunnel URL in MessageBird:
https://your-tunnel-url/api/messagebird-webhook
- Start your Next.js app:
- Production: Use your deployed application's URL:
- Paste the appropriate URL into the Webhook URL field in MessageBird.
- Webhook Signing Secret:
- MessageBird allows you to view or generate a Signing Key (or Secret) for your webhook.
- Copy this secret value.
- Paste it into
MESSAGEBIRD_WEBHOOK_SECRETin your.env.local.
- Select Events: Ensure you subscribe to relevant events, at least
message.createdandmessage.updated. - Save the webhook configuration in MessageBird.
4.4 Update NEXT_PUBLIC_APP_URL:
Ensure the NEXT_PUBLIC_APP_URL in your .env.local matches the base URL you used for the webhook (especially important if using ngrok or deploying).
5. Implement Error Handling and Logging for Production
Robust error handling and logging are essential for production WhatsApp Business API integrations. Proper logging helps you troubleshoot issues, track message delivery, and monitor system performance.
<!-- DEPTH: Section focuses only on logging setup, missing comprehensive error handling patterns (Priority: High) --> <!-- GAP: No discussion of monitoring, alerting, and observability tools (Type: Substantive) -->5.1 Error Handling Strategy:
- API Routes: Use
try…catchblocks extensively in bothsend-whatsappandmessagebird-webhookroutes. - Validation: Implement input validation (e.g., using Zod) early in the request lifecycle to catch malformed requests.
- Specific Errors: Catch specific errors from the MessageBird SDK and database operations. Log detailed error information server-side (stack trace, request context).
- Client Responses: Return clear, concise error messages to the client for API calls (
/api/send-whatsapp), but avoid exposing sensitive internal details. For webhooks (/api/messagebird-webhook), prioritize returning a200 OKquickly unless there's a critical failure preventing processing. - Environment Checks: Check for the existence of necessary environment variables on startup or early in request handling.
5.2 Logging:
While console.log/console.error is used in this guide's examples for simplicity, replace it with a structured logging library for production (e.g., pino, winston).
- Install Pino:
npm install pino pino-pretty(pino-prettyfor development). - Configure Logger: Create a logger instance (e.g.,
src/lib/logger.ts) and use it throughout your API routes.
// Example: src/lib/logger.ts (Basic Pino Setup)
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty' } // Pretty print in development
: undefined, // Default JSON output in production
});
export default logger;
// Usage in API routes:
// import logger from '@/lib/logger';
// logger.info('Processing request...');
// logger.error({ err: error }, 'Failed to send message');- Log Levels: Use appropriate levels (
info,warn,error,debug). - Context: Include relevant context in logs (request ID, user ID if applicable, message ID).
- Note: The code examples in Sections 2.1 and 3.1 use
console.logfor simplicity. For production applications, replace these with structured logging using a library like Pino, as demonstrated here.
Frequently Asked Questions (FAQ)
What Node.js version do I need for MessageBird WhatsApp integration with Next.js 15?
Use Node.js v20 LTS or v22 LTS for Next.js 15 in 2025. Next.js 15 requires minimum v18.18.0, but v18 was deprecated in Next.js 15.4. v20 and v22 provide long-term support and compatibility.
How do I format phone numbers for WhatsApp Business API?
WhatsApp requires E.164 format: +[country code][number], up to 15 digits total (e.g., +14155552671). Include the country code with no spaces or special characters. Landlines and mobile numbers work, but short codes and numbers from sanctioned countries (Crimea, Cuba, Iran, North Korea, Syria) are excluded.
What is the WABA template namespace in MessageBird?
The WABA (WhatsApp Business Account) template namespace is a unique identifier bundle for your business's message templates. Find it in MessageBird Dashboard > Templates > Template Details (format: cdb2df51_9816_c754_c5a4_64cdabdcad3e). This namespace is required for sending HSM (Highly Structured Message) template messages.
How does MessageBird webhook signature verification work?
MessageBird signs webhooks using HMAC-SHA256 with the MessageBird-Signature-JWT header. Your application must construct the signed payload as timestamp.secret.body, compute the HMAC-SHA256 hash using your webhook secret, and compare it with the received signature using crypto.timingSafeEqual() to prevent timing attacks.
What's the difference between MessageBird and Bird?
MessageBird rebranded to Bird in February 2024 with 90% price cuts on SMS. However, the API endpoints, Node.js SDK (messagebird npm package), and WhatsApp functionality remain unchanged. Existing integrations continue working without modification.
Which webhook tunneling tool is best for local WhatsApp development?
For persistent URLs during development, use Pinggy ($3/month, unlimited bandwidth) or Cloudflare Tunnel (free, no bandwidth limits). While ngrok works, free URLs are temporary and change on restart, requiring frequent webhook URL updates in MessageBird. Tunnelmole offers an open-source alternative.
Can I use Prisma with Next.js 15 for logging WhatsApp messages?
Yes, Prisma 6.7.0+ is fully compatible with Next.js 15.3.1+. Use Prisma to log incoming and outgoing WhatsApp messages, track delivery status, and maintain conversation history. The guide includes commented code examples for database integration.
<!-- GAP: Missing Prisma schema example and migration instructions (Type: Substantive) -->How do I send WhatsApp template messages vs. regular text messages?
Regular text messages set type: 'text' with content: { text: "…" }. Template messages set type: 'hsm' with your approved template name, WABA namespace, language code, and component parameters. Templates are required for business-initiated conversations or re-engagement after 24 hours of inactivity.
What are the benefits of using MessageBird WhatsApp API with Next.js?
MessageBird WhatsApp API with Next.js provides serverless deployment capabilities, easy API route management, built-in TypeScript support, and seamless integration with modern React frameworks. You get automatic scaling, edge network distribution, and simplified webhook handling through Next.js API routes.
How do I troubleshoot WhatsApp message delivery failures?
Check MessageBird API error codes in your logs, verify E.164 phone number formatting, ensure your WhatsApp channel is active, confirm template approval status, and validate webhook signature verification. For SMS-related troubleshooting, similar principles apply to phone number formatting and API authentication.
<!-- GAP: Missing FAQ about common errors and troubleshooting (Type: Substantive) --> <!-- GAP: No FAQ about deployment best practices and platform-specific considerations (Type: Substantive) --> <!-- GAP: Missing FAQ about testing strategies and tools (Type: Substantive) -->Frequently Asked Questions
how to send whatsapp messages with next.js
You can send WhatsApp messages within a Next.js app using the MessageBird API and their Node.js SDK. Create an API route in your Next.js application that handles POST requests, constructs the message payload (text or template), and uses the MessageBird SDK to send the message via your WhatsApp Business Channel ID.
what is messagebird whatsapp business api
The MessageBird WhatsApp Business API is a service that lets developers programmatically send and receive WhatsApp messages. This guide integrates it with Next.js and Node.js to manage customer interactions, notifications, alerts, and more, all within a web application.
why use messagebird with next.js for whatsapp
MessageBird simplifies WhatsApp integration by handling the complexities of the direct WhatsApp Business API. Combined with Next.js's full-stack capabilities, developers can build robust WhatsApp communication features within a modern web framework, improving development efficiency.
when should I use a whatsapp template message
Use WhatsApp template messages for pre-approved, structured communications like order confirmations or appointment reminders. For general chat or support, send standard text messages. Template messages require a namespace and parameters defined in your MessageBird account.
can i receive whatsapp messages in my next.js app
Yes, by setting up a webhook. Configure a dedicated API route in your Next.js app to handle incoming messages. Then, in your MessageBird dashboard, configure your WhatsApp channel's webhook to point to this route, ensuring it's publicly accessible (e.g., via ngrok for development or your deployment domain for production).
how to set up messagebird webhook with next.js
Create an API route in Next.js to act as your webhook endpoint. In your MessageBird dashboard, configure your WhatsApp Channel to send events to this URL. Make sure to use a publicly accessible URL, such as one provided by ngrok during development. Implement signature verification to ensure security.
what is messagebird channel id and where to find it
The MessageBird Channel ID is a unique identifier for your WhatsApp Business Channel. You can find it in your MessageBird dashboard under Channels -> WhatsApp -> Your Channel. This ID is essential for sending and receiving messages.
how to verify messagebird webhook signature in next js
Webhook signature verification is crucial for security. Retrieve the 'messagebird-signature' and 'messagebird-request-timestamp' headers from the incoming request, along with your Webhook Signing Secret from your MessageBird dashboard. Construct the signed payload string using these and calculate the HMAC SHA256 hash. Compare the calculated hash with the 'messagebird-signature' using a constant-time comparison method like `crypto.timingSafeEqual`.
what is the purpose of .env.local file
The `.env.local` file stores sensitive information like your MessageBird API Key, Channel ID, Webhook Secret, and database URL. This file should never be committed to version control. Next.js automatically loads environment variables from this file during development.
how to integrate messagebird with next.js api routes
First, install the 'messagebird' npm package. Then, initialize the MessageBird client in a utility file (`lib/messagebird.ts`) using your API Key from `.env.local`. Import and use this initialized client in your API routes to send messages and interact with the MessageBird API.
what are the prerequisites for messagebird whatsapp integration
You need a Node.js environment, a MessageBird account, a configured WhatsApp Business channel within MessageBird, a registered and approved WhatsApp number, a basic understanding of Next.js and APIs, and optionally Docker, a database, and ngrok for local testing.
how to handle incoming whatsapp messages in next.js
Your webhook route should process 'message.created' events. Extract the message content, sender, and other relevant details from the payload. Implement your business logic based on these details: route to support, trigger an automated reply, store message data, etc. Remember to always respond to the webhook with a 200 OK status, even if your processing continues asynchronously.
what is ngrok and why is it needed
ngrok creates temporary public URLs for your locally running services. It is useful for testing webhooks during development, allowing MessageBird to deliver events to your local machine. Note that free ngrok URLs change each time you restart ngrok, so they are not suitable for permanent webhook configurations.
how to use prisma for message logging with messagebird
Install Prisma and the necessary database connectors. Define your data models in `prisma/schema.prisma`. Use the Prisma Client in your webhook handler to create database records for incoming messages, storing details like sender, content, and timestamp.
what is the system architecture for integrating messagebird and next.js
The user/client application interacts with the Next.js frontend and API routes. These routes communicate with the MessageBird API to send and receive WhatsApp messages. Optionally, a database (using Prisma) can log message history.