Key Features
Framework Agnostic
Works with Express, Next.js, NestJS, Fastify, AWS Lambda, and any Node.js framework
Auto Message Parsing
Automatically parses WhatsApp webhook payloads into structured message objects
Type-Safe Handlers
Strongly typed handler functions for each message type with full IntelliSense
Built-in Verification
Automatic webhook verification for security and authenticity
Message Buffer System
Optional message grouping for better context and performance optimization
Auto Storage
Automatically persist messages to database when storage is enabled
Quick Start
1. Create Webhook Processor
Copy
import { WhatsAppClient } from 'whatsapp-client-sdk';
const client = new WhatsAppClient({
accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,
webhookVerifyToken: process.env.WHATSAPP_WEBHOOK_TOKEN!,
});
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
console.log(`Text from ${message.from}: ${message.text}`);
await client.sendText(message.from, `Echo: ${message.text}`);
},
onButtonClick: async (message) => {
console.log(`Button clicked: ${message.interactive.button_id}`);
await client.sendText(message.from, 'Button received!');
},
onError: async (error, message) => {
console.error('Webhook error:', error.message);
// For enhanced error handling, see: /guides/error-handling
}
});
2. Universal Webhook Endpoint
Works with any framework:Copy
// Express, Fastify, Next.js, etc.
app.all('/webhook', async (req, res) => {
try {
const result = await webhookProcessor.processWebhook(req.body, req.query);
res.status(result.status).send(result.response);
} catch (error) {
res.status(500).send('Internal Server Error');
}
});
Webhook Handler Types
Message Handlers
Copy
interface WebhookHandlers {
onTextMessage?: (message: ProcessedIncomingMessage & { text: string }) => Promise<void>;
onImageMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
onVideoMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
onAudioMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
onDocumentMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
onLocationMessage?: (message: ProcessedIncomingMessage & { location: LocationInfo }) => Promise<void>;
onContactMessage?: (message: ProcessedIncomingMessage & { contacts: Contact[] }) => Promise<void>;
onStickerMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
onButtonClick?: (message: ProcessedIncomingMessage & { interactive: InteractiveInfo }) => Promise<void>;
onListSelect?: (message: ProcessedIncomingMessage & { interactive: InteractiveInfo }) => Promise<void>;
onMessageStatusUpdate?: (statusUpdate: MessageStatusUpdate) => Promise<void>;
onUnknownMessage?: (message: ProcessedIncomingMessage) => Promise<void>;
onError?: (error: Error, message?: ProcessedIncomingMessage) => Promise<void>;
}
Complete Example
Smart Auto-Responder
Copy
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
const text = message.text.toLowerCase();
if (text.includes('hello') || text.includes('hi')) {
await client.sendText(
message.from,
'👋 Hello! How can I help you today?'
);
} else if (text.includes('help')) {
await client.sendButtons(
message.from,
'How can I assist you?',
[
{ id: 'support', title: '🛟 Get Support' },
{ id: 'info', title: 'ℹ️ Information' },
{ id: 'contact', title: '📞 Contact Us' }
]
);
} else if (text.includes('price') || text.includes('cost')) {
await client.sendList(
message.from,
'Here are our pricing options:',
'View Pricing',
[
{
title: 'Plans',
rows: [
{ id: 'basic', title: 'Basic Plan', description: '$9.99/month' },
{ id: 'pro', title: 'Pro Plan', description: '$19.99/month' },
{ id: 'enterprise', title: 'Enterprise', description: 'Custom pricing' }
]
}
]
);
} else {
await client.sendText(
message.from,
'🤖 Thanks for your message! A human will respond shortly.'
);
}
},
onImageMessage: async (message) => {
await client.sendText(
message.from,
'📸 Great image! Thanks for sharing.'
);
// Optional: Download and process the image
// const buffer = await client.downloadMedia(message.media.id);
// await processImage(buffer);
},
onButtonClick: async (message) => {
const buttonId = message.interactive.button_id;
switch (buttonId) {
case 'support':
await client.sendText(
message.from,
'🛟 Our support team will contact you within 24 hours.\n\n' +
'For urgent issues, call: +1-800-555-0123'
);
break;
case 'info':
await client.sendText(
message.from,
'ℹ️ We\'re a tech company focused on innovative solutions.\n\n' +
'Learn more: https://company.example.com'
);
break;
case 'contact':
await client.sendContacts(message.from, [
{
name: { formatted_name: 'Customer Support' },
phones: [{ phone: '+1-800-555-0123', type: 'WORK' }],
emails: [{ email: 'support@company.com', type: 'WORK' }]
}
]);
break;
}
},
onListSelect: async (message) => {
const listId = message.interactive.list_id;
switch (listId) {
case 'basic':
await client.sendText(
message.from,
'📦 Basic Plan - $9.99/month\n\n' +
'✅ 10 GB Storage\n' +
'✅ Email Support\n' +
'✅ Basic Features\n\n' +
'Ready to upgrade? https://signup.example.com/basic'
);
break;
case 'pro':
await client.sendText(
message.from,
'🚀 Pro Plan - $19.99/month\n\n' +
'✅ 100 GB Storage\n' +
'✅ Priority Support\n' +
'✅ Advanced Features\n' +
'✅ API Access\n\n' +
'Start free trial: https://signup.example.com/pro'
);
break;
case 'enterprise':
await client.sendText(
message.from,
'🏢 Enterprise Plan\n\n' +
'✅ Unlimited Storage\n' +
'✅ 24/7 Phone Support\n' +
'✅ Custom Features\n' +
'✅ Dedicated Account Manager\n\n' +
'Contact sales: sales@company.com'
);
break;
}
},
onLocationMessage: async (message) => {
const { latitude, longitude, name } = message.location;
await client.sendText(
message.from,
`📍 Thanks for sharing your location!\n\n` +
`${name || 'Location'}: ${latitude}, ${longitude}\n\n` +
`We'll find the nearest service center for you.`
);
},
onDocumentMessage: async (message) => {
await client.sendText(
message.from,
`📄 Document "${message.media.filename}" received.\n\n` +
`Our team will review it and get back to you within 2 business days.`
);
},
onContactMessage: async (message) => {
await client.sendText(
message.from,
'📇 Thank you for sharing the contact information!\n\n' +
'We\'ve saved it to our system.'
);
},
onStickerMessage: async (message) => {
await client.sendText(
message.from,
'😄 Nice sticker! We love creative communication.'
);
},
onError: async (error, message) => {
console.error('Webhook processing error:', {
error: error.message,
messageId: message?.id,
from: message?.from
});
if (message) {
try {
await client.sendText(
message.from,
'⚠️ Sorry, we encountered a technical issue. Please try again or contact support.'
);
} catch (sendError) {
console.error('Failed to send error message:', sendError);
}
}
}
});
Framework Integration
The webhook processor works universally with any Node.js framework:Copy
// Universal pattern for any framework
app.all('/webhook', async (req, res) => {
const result = await webhookProcessor.processWebhook(req.body, req.query);
res.status(result.status).send(result.response);
});
For complete framework-specific examples including NestJS, Express, Next.js, Fastify, and AWS Lambda, see our Framework Integration Examples.For message grouping and performance optimization, check out the Message Buffer System.To automatically persist messages to a database, see Storage & Persistence.
Advanced Features
Message Context Tracking
Copy
// Track conversation context
const conversationContext = new Map();
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
const context = conversationContext.get(message.from) || { step: 'greeting' };
switch (context.step) {
case 'greeting':
await client.sendText(message.from, 'What\'s your name?');
conversationContext.set(message.from, { step: 'name' });
break;
case 'name':
context.name = message.text;
await client.sendText(message.from, `Nice to meet you, ${context.name}! What can I help you with?`);
conversationContext.set(message.from, { ...context, step: 'help' });
break;
case 'help':
await client.sendText(message.from, `Thanks for your question, ${context.name}. Let me help you with that.`);
conversationContext.delete(message.from); // Reset context
break;
}
}
});
Webhook Security
Copy
// Enhanced webhook verification
const webhookProcessor = client.createWebhookProcessor({
verifyToken: process.env.WHATSAPP_WEBHOOK_TOKEN,
onTextMessage: async (message) => {
// Message is already verified by processor
await handleMessage(message);
},
onError: async (error) => {
if (error.name === 'WebhookVerificationError') {
console.error('Webhook verification failed:', error.message);
// Handle security breach
}
}
});
Best Practices
1. Response Time
Keep webhook responses under 20 seconds:Copy
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
// Quick acknowledgment
await client.sendText(message.from, 'Message received! Processing...');
// Process asynchronously
processMessageAsync(message); // Don't await
}
});
async function processMessageAsync(message: any) {
// Long-running processing
const result = await complexProcessing(message.text);
await client.sendText(message.from, `Result: ${result}`);
}
2. Error Handling
Copy
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
try {
await processMessage(message);
} catch (error) {
console.error('Message processing failed:', error);
await client.sendText(
message.from,
'Sorry, something went wrong. Please try again.'
);
}
},
onError: async (error, message) => {
// Log error for monitoring
console.error('Webhook error:', { error: error.message, messageId: message?.id });
// For comprehensive error handling and monitoring patterns
// see our Enhanced Error Handling Guide: /guides/error-handling
await notifyErrorService(error, message);
}
});
3. Rate Limiting
Copy
const messageRateLimit = new Map();
const webhookProcessor = client.createWebhookProcessor({
onTextMessage: async (message) => {
const now = Date.now();
const userMessages = messageRateLimit.get(message.from) || [];
// Remove messages older than 1 minute
const recentMessages = userMessages.filter(time => now - time < 60000);
if (recentMessages.length >= 10) {
await client.sendText(
message.from,
'You\'re sending messages too quickly. Please slow down.'
);
return;
}
recentMessages.push(now);
messageRateLimit.set(message.from, recentMessages);
// Process message normally
await handleMessage(message);
}
});
Message Status Updates
Track the delivery status of sent messages through webhook notifications.onMessageStatusUpdate Handler
Copy
const webhookProcessor = client.createWebhookProcessor({
onMessageStatusUpdate: async (statusUpdate) => {
console.log(`Message ${statusUpdate.id}: ${statusUpdate.status}`);
switch (statusUpdate.status) {
case 'sent':
console.log('✅ Message sent successfully');
break;
case 'delivered':
console.log('📱 Message delivered to device');
break;
case 'read':
console.log('👀 Message read by user');
break;
case 'failed':
console.log('❌ Message delivery failed');
if (statusUpdate.errors) {
console.error('Error details:', statusUpdate.errors);
}
break;
}
}
});
MessageStatusUpdate Interface
Copy
interface MessageStatusUpdate {
id: string; // Message ID
status: MessageStatus; // Current status
timestamp: string; // Status timestamp
recipient_id: string; // Recipient WhatsApp ID
phoneNumberId: string; // Business phone number ID
businessId: string; // Business account ID
conversation?: {
id: string;
origin?: { type: string; };
};
pricing?: {
pricing_model: string;
billable: boolean;
category: string;
};
errors?: Array<{
code: number;
title: string;
message?: string;
error_data?: { details: string; };
}>;
}
enum MessageStatus {
SENT = 'sent',
DELIVERED = 'delivered',
READ = 'read',
FAILED = 'failed'
}
Webhook Payload Format
WhatsApp sends status updates in this format:Copy
{
"object": "whatsapp_business_account",
"entry": [{
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"phone_number_id": "BUSINESS_PHONE_ID"
},
"statuses": [{
"id": "MESSAGE_ID",
"status": "read",
"timestamp": "EPOCH_TIMESTAMP",
"recipient_id": "USER_WHATSAPP_ID",
"conversation": {
"id": "CONVERSATION_ID",
"origin": { "type": "business_initiated" }
},
"pricing": {
"pricing_model": "CBP",
"billable": true,
"category": "business_initiated"
}
}]
}
}]
}]
}