Meta WhatsApp Cloud API Integration
Meta Business Manager setup, webhooks, HMAC-SHA256 signatures, and notifications.
This guide describes how to configure the official Meta WhatsApp Cloud API for production B2B message ingestion and automated notifications.
1. Prerequisites (Meta Business Manager)
To connect Kholo to WhatsApp Cloud API, you need:
- A Meta Business Portfolio account (verified at business.facebook.com).
- A Meta Developer App of type Business.
- A Dedicated Business Phone Number (e.g.
+27 8X XXX XXXX) that is not currently registered on a personal WhatsApp mobile device. - A Permanent System User Access Token with permissions:
whatsapp_business_messagingwhatsapp_business_management
2. Environment Variables Configuration
Add the following keys to apps/api/.env:
# Meta WhatsApp Cloud API Credentials
META_WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id_here"
META_WHATSAPP_ACCESS_TOKEN="EAAxxxxxxx_permanent_system_token"
META_APP_SECRET="your_meta_app_secret_here"
WHATSAPP_VERIFY_TOKEN="kholo_custom_verify_token_here"3. Webhook Configuration in Meta App Dashboard
- Navigate to WhatsApp $\rightarrow$ Configuration inside the Meta App Dashboard.
- In the Callback URL field, enter:
https://api.yourdomain.co.za/api/webhooks/whatsapp - In the Verify Token field, enter the value from your
WHATSAPP_VERIFY_TOKENenvironment variable. - Click "Verify and Save".
- Under Webhook Fields, click Manage and subscribe to:
messages(Mandatory - captures incoming texts, audio notes, and media).
4. Webhook Security: HMAC-SHA256 Verification
Kholo inspects the cryptographic signature attached by Meta on every single incoming HTTP request:
[!CAUTION] If
META_APP_SECRETis configured, requests without a validX-Hub-Signature-256header will be immediately rejected with an HTTP 401 status to protect your queue from denial-of-service or spoofing attacks.
5. Webhook Handlers in Kholo (apps/api/src/server.ts)
Verification Challenge (GET /api/webhooks/whatsapp):
Handles the initial subscription handshake from Meta:
fastify.get('/api/webhooks/whatsapp', async (req, reply) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.WHATSAPP_VERIFY_TOKEN) {
return reply.status(200).send(challenge);
}
return reply.status(403).send('Forbidden');
});Event Ingestion (POST /api/webhooks/whatsapp):
Receives the message, responds with an immediate 200 OK within $< 50\text{ms}$, and passes the payload to Background Task Queue:
fastify.post('/api/webhooks/whatsapp', async (req, reply) => {
// 1. Verify signature
// 2. Push to queue
await orderQueue.add('IngestMessage', req.body);
// 3. Return 200 OK immediately
return reply.status(200).send({ status: 'queued' });
});6. Sending Outbound WhatsApp Notifications
When orders are approved or updated, Kholo posts messages back to the customer using the Graph API:
POST https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {META_WHATSAPP_ACCESS_TOKEN}
Content-Type: application/json
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "+27 00 000 0000",
"type": "text",
"text": {
"body": "Thanks Clerk! Your order has been approved (Sage SO #SO-2026-8454). Total: R 1,937.85. Our warehouse is packing now."
}
}