WhatsApp Business API Integration for ChatGPT Apps: Complete Implementation Guide
WhatsApp Business API enables your ChatGPT app to reach 2.4 billion users through the world's most popular messaging platform. Whether you're building customer support automation, appointment scheduling, order tracking, or marketing campaigns, WhatsApp integration transforms your ChatGPT app from a conversational interface into a multi-channel communication platform.
This guide provides production-ready code and architecture patterns for integrating WhatsApp Business API with ChatGPT apps. You'll learn Cloud API implementation, webhook handling, message templates, interactive messages, media uploads, opt-in management, and rate limiting strategies that handle enterprise-scale traffic.
By the end of this guide, you'll understand:
- Why WhatsApp Business API is critical for global ChatGPT app distribution
- How to implement Cloud API authentication and send your first message
- Message template creation and management for compliant broadcasts
- Interactive message implementation (buttons, lists, quick replies)
- Media handling for images, videos, documents, and location sharing
- Opt-in management and GDPR-compliant subscriber tracking
- Webhook architecture for real-time message delivery and status updates
- Rate limiting strategies that prevent API throttling
- Business verification requirements and approval workflows
- Real-world code examples for 5 production-ready integrations
Let's build a WhatsApp-powered ChatGPT app that reaches billions of users worldwide.
1. Why WhatsApp Business API Matters for ChatGPT Apps
The Global Messaging Distribution Opportunity
WhatsApp dominates global messaging with 2.4 billion monthly active users across 180+ countries. In regions like Latin America, India, Southeast Asia, Europe, and Africa, WhatsApp is the primary communication channel for businesses and consumers.
ChatGPT Apps × WhatsApp Business API:
- 800 million ChatGPT users + 2.4 billion WhatsApp users = massive cross-platform reach
- 98% open rates on WhatsApp messages (vs. 20-30% email open rates)
- Real-time delivery with read receipts and typing indicators
- Rich media support (images, videos, documents, location, contacts)
- Two-way conversations with interactive buttons and quick replies
- End-to-end encryption for privacy-conscious users
- Global scale with local phone number support in 100+ countries
Business Use Cases That Demand WhatsApp Integration
Customer Support Automation:
- Fitness studios sending class reminders and booking confirmations
- Restaurants handling reservation requests and menu updates
- Healthcare providers sending appointment reminders (HIPAA-compliant)
- Real estate agents sharing property listings and scheduling tours
Marketing & Engagement:
- E-commerce order tracking and delivery notifications
- Educational platforms sending course updates and assignments
- SaaS companies sending product updates and feature announcements
- Event organizers sending reminders and last-minute changes
Transactional Messaging:
- Payment confirmations and invoice delivery
- Shipping updates with tracking links
- Appointment confirmations with calendar invites
- Password resets and security alerts
Why This Matters Now: Meta's WhatsApp Cloud API (launched June 2022) removed the previous requirement to work with Business Solution Providers. Now any developer can integrate WhatsApp Business API directly into their ChatGPT apps without third-party intermediaries.
2. WhatsApp Cloud API Setup: Authentication & Configuration
Prerequisites for WhatsApp Business API
Before implementing code, you need:
- Meta Business Account (create at business.facebook.com)
- WhatsApp Business API Access (enable at developers.facebook.com)
- Phone Number Verification (dedicated phone number for your business)
- Business Verification (required for production access beyond 1,000 conversations/month)
WhatsApp Client Implementation (120 lines)
This production-ready client handles authentication, message sending, and error recovery:
// whatsapp-client.js
const axios = require('axios');
class WhatsAppClient {
constructor(config) {
this.phoneNumberId = config.phoneNumberId; // Your WhatsApp phone number ID
this.accessToken = config.accessToken; // Meta access token
this.apiVersion = config.apiVersion || 'v18.0';
this.baseURL = `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}`;
}
/**
* Send text message to recipient
* @param {string} to - Recipient phone number (E.164 format: +1234567890)
* @param {string} text - Message content (max 4096 characters)
* @returns {Promise<object>} Message delivery response
*/
async sendTextMessage(to, text) {
try {
const response = await axios.post(
`${this.baseURL}/messages`,
{
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: to,
type: 'text',
text: { body: text }
},
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return {
success: true,
messageId: response.data.messages[0].id,
contact: response.data.contacts[0]
};
} catch (error) {
return this.handleError(error);
}
}
/**
* Send template message (required for outbound broadcasts)
* @param {string} to - Recipient phone number
* @param {string} templateName - Approved template name
* @param {string} languageCode - Template language (e.g., 'en_US')
* @param {Array} components - Template variables and buttons
* @returns {Promise<object>} Message delivery response
*/
async sendTemplateMessage(to, templateName, languageCode, components = []) {
try {
const response = await axios.post(
`${this.baseURL}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'template',
template: {
name: templateName,
language: { code: languageCode },
components: components
}
},
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return {
success: true,
messageId: response.data.messages[0].id
};
} catch (error) {
return this.handleError(error);
}
}
/**
* Mark message as read (updates UI for sender)
* @param {string} messageId - WhatsApp message ID
* @returns {Promise<object>} Read receipt confirmation
*/
async markAsRead(messageId) {
try {
await axios.post(
`${this.baseURL}/messages`,
{
messaging_product: 'whatsapp',
status: 'read',
message_id: messageId
},
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true };
} catch (error) {
return this.handleError(error);
}
}
/**
* Handle WhatsApp API errors with actionable messages
*/
handleError(error) {
if (error.response) {
const { status, data } = error.response;
// Rate limiting (429 errors)
if (status === 429) {
return {
success: false,
error: 'RATE_LIMIT_EXCEEDED',
message: 'WhatsApp rate limit exceeded. Retry after delay.',
retryAfter: error.response.headers['retry-after'] || 60
};
}
// Authentication errors (401, 403)
if (status === 401 || status === 403) {
return {
success: false,
error: 'AUTHENTICATION_FAILED',
message: 'Invalid access token. Regenerate token in Meta Business Manager.'
};
}
// Invalid phone number (400)
if (data.error?.code === 131026) {
return {
success: false,
error: 'INVALID_PHONE_NUMBER',
message: 'Recipient phone number is invalid or not on WhatsApp.'
};
}
// Template not approved
if (data.error?.code === 132000) {
return {
success: false,
error: 'TEMPLATE_NOT_APPROVED',
message: 'Message template must be approved before sending.'
};
}
}
return {
success: false,
error: 'UNKNOWN_ERROR',
message: error.message
};
}
}
module.exports = WhatsAppClient;
Usage Example:
const WhatsAppClient = require('./whatsapp-client');
const client = new WhatsAppClient({
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
accessToken: process.env.WHATSAPP_ACCESS_TOKEN
});
// Send simple text message
await client.sendTextMessage('+1234567890', 'Hello from ChatGPT app!');
3. Message Templates: Compliant Outbound Messaging
Why Templates Are Required
WhatsApp enforces strict anti-spam policies. You cannot send outbound messages to users unless:
- They messaged you first (24-hour session window), OR
- You use an approved message template
Message templates must be pre-approved by Meta and can only contain approved content. This prevents spam while enabling legitimate business communications.
Template Manager Implementation (130 lines)
Manage template creation, submission, and usage:
// template-manager.js
const axios = require('axios');
class TemplateManager {
constructor(config) {
this.businessAccountId = config.businessAccountId; // Meta Business Account ID
this.accessToken = config.accessToken;
this.apiVersion = config.apiVersion || 'v18.0';
this.baseURL = `https://graph.facebook.com/${this.apiVersion}/${this.businessAccountId}`;
}
/**
* Create message template for approval
* @param {object} template - Template definition
* @returns {Promise<object>} Template creation response
*/
async createTemplate(template) {
try {
const response = await axios.post(
`${this.baseURL}/message_templates`,
{
name: template.name, // Lowercase, alphanumeric, underscores only
language: template.language, // e.g., 'en_US'
category: template.category, // MARKETING, UTILITY, AUTHENTICATION
components: template.components
},
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return {
success: true,
templateId: response.data.id,
status: response.data.status // PENDING, APPROVED, REJECTED
};
} catch (error) {
return this.handleError(error);
}
}
/**
* Get template by ID with approval status
* @param {string} templateId - Template ID
* @returns {Promise<object>} Template details
*/
async getTemplate(templateId) {
try {
const response = await axios.get(
`${this.baseURL}/message_templates/${templateId}`,
{
headers: { 'Authorization': `Bearer ${this.accessToken}` }
}
);
return {
success: true,
template: response.data
};
} catch (error) {
return this.handleError(error);
}
}
/**
* List all templates with filtering
* @param {object} filters - { status: 'APPROVED', category: 'UTILITY' }
* @returns {Promise<object>} Template list
*/
async listTemplates(filters = {}) {
try {
const params = new URLSearchParams(filters);
const response = await axios.get(
`${this.baseURL}/message_templates?${params.toString()}`,
{
headers: { 'Authorization': `Bearer ${this.accessToken}` }
}
);
return {
success: true,
templates: response.data.data,
pagination: response.data.paging
};
} catch (error) {
return this.handleError(error);
}
}
/**
* Build template components for common use cases
*/
buildAppointmentReminder(variables) {
return {
name: 'appointment_reminder',
language: 'en_US',
category: 'UTILITY',
components: [
{
type: 'HEADER',
format: 'TEXT',
text: 'Appointment Reminder'
},
{
type: 'BODY',
text: `Hi {{1}}, your appointment is scheduled for {{2}} at {{3}}. Reply CONFIRM to confirm or RESCHEDULE to change.`,
example: {
body_text: [
['John', 'January 15, 2026', '2:00 PM']
]
}
},
{
type: 'FOOTER',
text: 'Powered by ChatGPT'
},
{
type: 'BUTTONS',
buttons: [
{
type: 'QUICK_REPLY',
text: 'CONFIRM'
},
{
type: 'QUICK_REPLY',
text: 'RESCHEDULE'
}
]
}
]
};
}
handleError(error) {
if (error.response) {
return {
success: false,
error: error.response.data.error?.message || 'Template operation failed',
details: error.response.data.error
};
}
return {
success: false,
error: error.message
};
}
}
module.exports = TemplateManager;
Template Best Practices:
- Name format:
lowercase_with_underscores(e.g.,order_confirmation) - Categories: MARKETING (promotional), UTILITY (transactional), AUTHENTICATION (OTP codes)
- Variables: Use
{{1}},{{2}}, etc. for dynamic content - Approval time: 1-48 hours (expedited for AUTHENTICATION templates)
- Rejection reasons: Spam, misleading content, missing opt-out language
Learn more about webhook implementation best practices for real-time template status updates.
4. Interactive Messages: Buttons, Lists & Quick Replies
Interactive messages dramatically increase engagement by providing structured response options instead of free-text replies. WhatsApp supports three interactive message types:
- Reply Buttons (up to 3 buttons)
- List Messages (up to 10 rows per section)
- Quick Reply Buttons (template-based, up to 3 quick replies)
Interactive Message Handler (110 lines)
// interactive-handler.js
class InteractiveMessageHandler {
constructor(whatsappClient) {
this.client = whatsappClient;
}
/**
* Send reply buttons message (max 3 buttons)
* @param {string} to - Recipient phone number
* @param {object} message - Message with buttons
* @returns {Promise<object>} Delivery response
*/
async sendReplyButtons(to, message) {
try {
const response = await axios.post(
`${this.client.baseURL}/messages`,
{
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: to,
type: 'interactive',
interactive: {
type: 'button',
body: {
text: message.body
},
action: {
buttons: message.buttons.map((btn, index) => ({
type: 'reply',
reply: {
id: btn.id || `btn_${index}`,
title: btn.title // Max 20 characters
}
}))
}
}
},
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true, messageId: response.data.messages[0].id };
} catch (error) {
return this.client.handleError(error);
}
}
/**
* Send list message (scrollable list with sections)
* @param {string} to - Recipient phone number
* @param {object} message - Message with list sections
* @returns {Promise<object>} Delivery response
*/
async sendListMessage(to, message) {
try {
const response = await axios.post(
`${this.client.baseURL}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'interactive',
interactive: {
type: 'list',
header: {
type: 'text',
text: message.header
},
body: {
text: message.body
},
footer: {
text: message.footer || 'Select an option'
},
action: {
button: message.buttonText || 'View Options',
sections: message.sections.map(section => ({
title: section.title,
rows: section.rows.map(row => ({
id: row.id,
title: row.title, // Max 24 characters
description: row.description // Max 72 characters
}))
}))
}
}
},
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true, messageId: response.data.messages[0].id };
} catch (error) {
return this.client.handleError(error);
}
}
/**
* Parse interactive message response from webhook
* @param {object} webhookPayload - Incoming webhook data
* @returns {object} Parsed user response
*/
parseInteractiveResponse(webhookPayload) {
const message = webhookPayload.entry[0]?.changes[0]?.value?.messages[0];
if (!message || message.type !== 'interactive') {
return { type: 'text', text: message?.text?.body };
}
const interactive = message.interactive;
// Button reply
if (interactive.type === 'button_reply') {
return {
type: 'button',
buttonId: interactive.button_reply.id,
buttonTitle: interactive.button_reply.title
};
}
// List reply
if (interactive.type === 'list_reply') {
return {
type: 'list',
listId: interactive.list_reply.id,
listTitle: interactive.list_reply.title,
listDescription: interactive.list_reply.description
};
}
return { type: 'unknown' };
}
}
module.exports = InteractiveMessageHandler;
Example: Fitness Class Booking Flow
const handler = new InteractiveMessageHandler(whatsappClient);
// Step 1: Show class categories
await handler.sendListMessage('+1234567890', {
header: 'Choose Your Class',
body: 'Select a fitness class category to see available times:',
buttonText: 'View Classes',
sections: [
{
title: 'Cardio Classes',
rows: [
{ id: 'spin', title: 'Spin Class', description: 'High-intensity cycling' },
{ id: 'hiit', title: 'HIIT', description: '30-min interval training' }
]
},
{
title: 'Strength Classes',
rows: [
{ id: 'weights', title: 'Weight Training', description: 'Build muscle strength' },
{ id: 'yoga', title: 'Yoga', description: 'Flexibility & mindfulness' }
]
}
]
});
// Step 2: User selects "spin", show time slots
await handler.sendReplyButtons('+1234567890', {
body: 'Spin Class available times for tomorrow:',
buttons: [
{ id: 'spin_9am', title: '9:00 AM' },
{ id: 'spin_12pm', title: '12:00 PM' },
{ id: 'spin_6pm', title: '6:00 PM' }
]
});
See inline card optimization strategies for similar UI patterns in ChatGPT widgets.
5. Media Handling: Images, Videos, Documents & Location
WhatsApp supports rich media messages that enhance user experience beyond text. Supported media types:
- Images (JPEG, PNG - max 5MB)
- Videos (MP4, 3GP - max 16MB)
- Documents (PDF, DOCX, XLSX - max 100MB)
- Audio (AAC, MP3, OGG - max 16MB)
- Location (latitude/longitude coordinates)
- Contacts (vCard format)
Media Upload Handler (100 lines)
// media-uploader.js
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
class MediaUploader {
constructor(whatsappClient) {
this.client = whatsappClient;
}
/**
* Upload media file to WhatsApp servers
* @param {string} filePath - Local file path
* @param {string} mimeType - MIME type (e.g., 'image/jpeg')
* @returns {Promise<object>} Media ID for sending
*/
async uploadMedia(filePath, mimeType) {
try {
const formData = new FormData();
formData.append('file', fs.createReadStream(filePath));
formData.append('messaging_product', 'whatsapp');
formData.append('type', mimeType);
const response = await axios.post(
`${this.client.baseURL}/media`,
formData,
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
...formData.getHeaders()
}
}
);
return {
success: true,
mediaId: response.data.id
};
} catch (error) {
return this.client.handleError(error);
}
}
/**
* Send image message
* @param {string} to - Recipient phone number
* @param {string} mediaId - Uploaded media ID or URL
* @param {string} caption - Optional image caption
* @returns {Promise<object>} Delivery response
*/
async sendImage(to, mediaId, caption = '') {
try {
const response = await axios.post(
`${this.client.baseURL}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'image',
image: {
id: mediaId, // Use uploaded media ID
// OR link: 'https://example.com/image.jpg' for external URLs
caption: caption
}
},
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true, messageId: response.data.messages[0].id };
} catch (error) {
return this.client.handleError(error);
}
}
/**
* Send document (PDF, DOCX, etc.)
* @param {string} to - Recipient phone number
* @param {string} mediaId - Uploaded document ID
* @param {string} filename - Display filename
* @returns {Promise<object>} Delivery response
*/
async sendDocument(to, mediaId, filename) {
try {
const response = await axios.post(
`${this.client.baseURL}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'document',
document: {
id: mediaId,
filename: filename,
caption: `Your document: ${filename}`
}
},
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true, messageId: response.data.messages[0].id };
} catch (error) {
return this.client.handleError(error);
}
}
/**
* Send location message
* @param {string} to - Recipient phone number
* @param {object} location - { latitude, longitude, name, address }
* @returns {Promise<object>} Delivery response
*/
async sendLocation(to, location) {
try {
const response = await axios.post(
`${this.client.baseURL}/messages`,
{
messaging_product: 'whatsapp',
to: to,
type: 'location',
location: {
latitude: location.latitude,
longitude: location.longitude,
name: location.name,
address: location.address
}
},
{
headers: {
'Authorization': `Bearer ${this.client.accessToken}`,
'Content-Type': 'application/json'
}
}
);
return { success: true, messageId: response.data.messages[0].id };
} catch (error) {
return this.client.handleError(error);
}
}
}
module.exports = MediaUploader;
Example: Real Estate Property Sharing
const uploader = new MediaUploader(whatsappClient);
// Upload property image
const { mediaId } = await uploader.uploadMedia(
'./property-photos/123-main-st.jpg',
'image/jpeg'
);
// Send image with listing details
await uploader.sendImage(
'+1234567890',
mediaId,
'3 bed, 2 bath, 2,000 sqft - $450,000\n123 Main St, San Francisco, CA'
);
// Send property location
await uploader.sendLocation('+1234567890', {
latitude: 37.7749,
longitude: -122.4194,
name: '123 Main St',
address: 'San Francisco, CA 94102'
});
// Send PDF brochure
const { mediaId: pdfId } = await uploader.uploadMedia(
'./brochures/123-main-st.pdf',
'application/pdf'
);
await uploader.sendDocument('+1234567890', pdfId, '123-Main-St-Brochure.pdf');
Learn more about image optimization techniques for faster media delivery.
6. Opt-In Management & GDPR Compliance
WhatsApp requires explicit user consent before sending marketing messages. This protects users from spam and ensures GDPR compliance for European users.
Opt-In Tracker Implementation (80 lines)
// opt-in-tracker.js
class OptInTracker {
constructor(database) {
this.db = database; // Firestore, PostgreSQL, etc.
}
/**
* Record user opt-in with timestamp and source
* @param {string} phoneNumber - User phone number (E.164 format)
* @param {object} consentData - Opt-in metadata
* @returns {Promise<object>} Opt-in record
*/
async recordOptIn(phoneNumber, consentData) {
const optInRecord = {
phoneNumber: phoneNumber,
consentGranted: true,
consentDate: new Date().toISOString(),
consentSource: consentData.source, // 'website', 'chatgpt_app', 'sms'
consentMethod: consentData.method, // 'checkbox', 'keyword', 'button'
ipAddress: consentData.ipAddress,
userAgent: consentData.userAgent,
marketingConsent: consentData.marketingConsent || false,
transactionalConsent: consentData.transactionalConsent || true
};
await this.db.collection('whatsapp_opt_ins').doc(phoneNumber).set(optInRecord);
return { success: true, record: optInRecord };
}
/**
* Record user opt-out (unsubscribe)
* @param {string} phoneNumber - User phone number
* @returns {Promise<object>} Opt-out confirmation
*/
async recordOptOut(phoneNumber) {
await this.db.collection('whatsapp_opt_ins').doc(phoneNumber).update({
consentGranted: false,
optOutDate: new Date().toISOString()
});
return { success: true, message: 'User opted out successfully' };
}
/**
* Check if user has valid opt-in
* @param {string} phoneNumber - User phone number
* @param {string} messageType - 'marketing' or 'transactional'
* @returns {Promise<boolean>} Consent status
*/
async hasConsent(phoneNumber, messageType = 'transactional') {
const record = await this.db.collection('whatsapp_opt_ins').doc(phoneNumber).get();
if (!record.exists) {
return false;
}
const data = record.data();
// Consent revoked
if (!data.consentGranted) {
return false;
}
// Check message type consent
if (messageType === 'marketing' && !data.marketingConsent) {
return false;
}
return true;
}
/**
* Handle opt-in keyword from incoming message
* @param {string} phoneNumber - User phone number
* @param {string} keyword - Opt-in keyword (e.g., 'START', 'SUBSCRIBE')
* @returns {Promise<object>} Opt-in confirmation
*/
async processOptInKeyword(phoneNumber, keyword) {
const validKeywords = ['START', 'SUBSCRIBE', 'YES', 'OPTIN'];
if (validKeywords.includes(keyword.toUpperCase())) {
await this.recordOptIn(phoneNumber, {
source: 'whatsapp_keyword',
method: 'keyword',
marketingConsent: true
});
return {
success: true,
message: 'Thank you! You\'re now subscribed to updates. Reply STOP to unsubscribe.'
};
}
return { success: false, message: 'Invalid opt-in keyword' };
}
}
module.exports = OptInTracker;
GDPR Compliance Checklist:
- ✅ Explicit consent before first message (no pre-checked boxes)
- ✅ Clear opt-out mechanism ("Reply STOP to unsubscribe")
- ✅ Store consent timestamp, source, and IP address
- ✅ Separate marketing vs. transactional consent
- ✅ Honor opt-outs within 24 hours
- ✅ Data retention policy (delete after X years)
- ✅ Right to access (users can request their data)
- ✅ Right to deletion (users can request account deletion)
Learn more about GDPR and CCPA compliance for ChatGPT apps.
7. Webhooks: Real-Time Message Delivery & Status Updates
WhatsApp uses webhooks to notify your server about incoming messages, delivery status, and read receipts. Proper webhook implementation is critical for responsive ChatGPT apps.
Webhook Events You Must Handle
- messages - Incoming user messages (text, media, interactive responses)
- message_status - Delivery status (sent, delivered, read, failed)
- message_template_status_update - Template approval/rejection notifications
Webhook Verification & Message Processing
// webhook-handler.js (Express.js example)
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook verification (GET request from Meta)
app.get('/webhooks/whatsapp', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
const VERIFY_TOKEN = process.env.WHATSAPP_VERIFY_TOKEN;
if (mode === 'subscribe' && token === VERIFY_TOKEN) {
console.log('Webhook verified');
res.status(200).send(challenge);
} else {
res.sendStatus(403);
}
});
// Webhook message handler (POST request from Meta)
app.post('/webhooks/whatsapp', async (req, res) => {
// Verify signature
const signature = req.headers['x-hub-signature-256'];
const isValid = verifySignature(req.body, signature);
if (!isValid) {
return res.sendStatus(403);
}
// Acknowledge receipt immediately (Meta requires < 20 second response)
res.sendStatus(200);
// Process webhook asynchronously
processWebhook(req.body);
});
function verifySignature(payload, signature) {
const expectedSignature = crypto
.createHmac('sha256', process.env.WHATSAPP_APP_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}
async function processWebhook(payload) {
const entry = payload.entry[0];
const changes = entry.changes[0];
const value = changes.value;
// Handle incoming message
if (value.messages) {
const message = value.messages[0];
const from = message.from; // Sender phone number
const messageType = message.type; // 'text', 'image', 'interactive', etc.
if (messageType === 'text') {
const text = message.text.body;
console.log(`Received message from ${from}: ${text}`);
// Forward to ChatGPT app logic
await handleChatGPTMessage(from, text);
}
// Mark as read
await whatsappClient.markAsRead(message.id);
}
// Handle message status update
if (value.statuses) {
const status = value.statuses[0];
console.log(`Message ${status.id} status: ${status.status}`);
// Update database with delivery status
}
}
app.listen(3000, () => {
console.log('WhatsApp webhook server running on port 3000');
});
See webhook implementation best practices for production-ready error handling and retry logic.
8. Rate Limiting & Quota Management
WhatsApp enforces strict rate limits to prevent abuse. Exceeding limits results in temporary bans (1-24 hours) and degraded message quality ratings.
Rate Limit Tiers (Cloud API)
| Quality Rating | Messaging Tier | Daily Limit | Use Case |
|---|---|---|---|
| High (Green) | Tier 1 | 1,000 conversations | New businesses |
| High (Green) | Tier 2 | 10,000 conversations | Established businesses |
| High (Green) | Tier 3 | 100,000 conversations | High-volume businesses |
| High (Green) | Unlimited | Unlimited | Enterprise (requires approval) |
| Medium (Yellow) | Current tier -1 | Reduced capacity | Warning: improve quality |
| Low (Red) | Restricted | Severely limited | Critical: risk of ban |
Quality Rating Factors:
- User blocks your number (-10 points)
- User reports as spam (-25 points)
- High response rate (+5 points)
- Low opt-out rate (+3 points)
Rate Limiting Strategy
// rate-limiter.js
class RateLimiter {
constructor(maxMessagesPerSecond = 80) {
this.limit = maxMessagesPerSecond; // Cloud API: 80 msg/sec max
this.queue = [];
this.processing = false;
}
async sendMessage(sendFunction, ...args) {
return new Promise((resolve, reject) => {
this.queue.push({ sendFunction, args, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
const batchSize = Math.min(this.limit, this.queue.length);
const batch = this.queue.splice(0, batchSize);
const results = await Promise.allSettled(
batch.map(({ sendFunction, args }) => sendFunction(...args))
);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
batch[index].resolve(result.value);
} else {
batch[index].reject(result.reason);
}
});
this.processing = false;
// Continue processing queue after 1-second delay
if (this.queue.length > 0) {
setTimeout(() => this.processQueue(), 1000);
}
}
}
// Usage
const limiter = new RateLimiter(80);
for (let i = 0; i < 10000; i++) {
await limiter.sendMessage(
whatsappClient.sendTextMessage,
`+123456789${i}`,
'Your order has shipped!'
);
}
Learn more about API rate limiting strategies for production-grade implementations.
9. Business Verification & Production Access
Why Verification Matters
Unverified businesses face strict limitations:
- ❌ Max 1,000 conversations/month
- ❌ Lower quality ratings
- ❌ Restricted template approvals
- ❌ No green checkmark badge
Verified businesses unlock:
- ✅ 100,000+ conversations/month
- ✅ Higher quality ratings
- ✅ Faster template approvals
- ✅ Green verified badge (trust signal)
- ✅ Priority support
Verification Requirements
Meta Business Verification:
- Business documents (incorporation papers, tax ID)
- Phone number verification
- Business address verification
- Business website or social media presence
- 2-factor authentication enabled
Official Business Account (OBA) Verification:
- Meta Business Verification completed
- Business trademark registration
- Unique display name (no generic names)
- High-quality profile photo (logo)
- Detailed business description
Timeline:
- Meta Business Verification: 1-3 business days
- OBA Verification: 2-14 business days
10. Real-World Integration Examples
Example 1: Fitness Studio Appointment Reminders
// Send automated class reminders 24 hours before
const templateManager = new TemplateManager(config);
const optInTracker = new OptInTracker(firestore);
async function sendClassReminder(user, classDetails) {
// Check opt-in consent
const hasConsent = await optInTracker.hasConsent(user.phone, 'transactional');
if (!hasConsent) return;
// Send template message
await whatsappClient.sendTemplateMessage(
user.phone,
'class_reminder_24h',
'en_US',
[
{
type: 'body',
parameters: [
{ type: 'text', text: user.firstName },
{ type: 'text', text: classDetails.date },
{ type: 'text', text: classDetails.time }
]
}
]
);
}
See fitness studio implementation guide for complete architecture.
Example 2: Restaurant Order Tracking
// Send order status updates with interactive buttons
const handler = new InteractiveMessageHandler(whatsappClient);
async function sendOrderUpdate(order) {
await handler.sendReplyButtons(order.customerPhone, {
body: `Your order #${order.id} is being prepared! Estimated delivery: ${order.eta}`,
buttons: [
{ id: 'track', title: 'Track Order' },
{ id: 'call', title: 'Call Restaurant' },
{ id: 'cancel', title: 'Cancel Order' }
]
});
}
See restaurant ChatGPT apps guide for complete implementation.
Conclusion: Building WhatsApp-Powered ChatGPT Apps
WhatsApp Business API integration transforms your ChatGPT app from a single-channel conversational interface into a global messaging platform that reaches 2.4 billion users. By implementing Cloud API authentication, message templates, interactive messages, media handling, opt-in management, and webhook processing, you've built the foundation for enterprise-scale WhatsApp automation.
Key Takeaways:
- WhatsApp's 98% open rate makes it superior to email for transactional messaging
- Message templates are mandatory for outbound broadcasts (approval required)
- Interactive messages (buttons, lists) increase engagement by 3-5x
- Opt-in management and GDPR compliance are non-negotiable for legal operation
- Webhooks enable real-time bidirectional conversations
- Rate limiting prevents API bans and maintains quality ratings
- Business verification unlocks 100x higher messaging capacity
Next Steps:
- Integrate with MCP Servers: Connect WhatsApp handlers to your MCP server architecture
- Add Authentication: Implement OAuth 2.1 flows for user identity
- Deploy to Production: Follow deployment best practices for scalability
- Monitor Performance: Track analytics and optimize conversion
- Submit to ChatGPT Store: Complete submission checklist
Ready to build your WhatsApp-powered ChatGPT app without writing code? Try MakeAIHQ's no-code ChatGPT app builder and deploy WhatsApp integration in minutes, not months.
About MakeAIHQ: We're the leading no-code platform for building ChatGPT apps with native WhatsApp Business API integration. From conversation design to production deployment, MakeAIHQ handles the complexity so you can focus on your business logic. Start building for free.