API Reference v1.0

Webhooks

Receive real-time notifications when messages arrive, statuses change, or other events occur on your WhatsApp Business account. WhatsBizAPI forwards these events to your server via HTTP POST requests.

Setting Up Webhooks

WhatsBizAPI provides a Webhook Relay feature that forwards incoming WhatsApp events directly to your server. Instead of polling for updates, your application receives events in real time as they happen.

Configuration Steps

  1. Log in to your WhatsBizAPI Dashboard.
  2. Navigate to Dashboard → Webhook Relay.
  3. Enter your Webhook URL — the HTTPS endpoint on your server that will receive event payloads.
  4. Enable the Webhook Relay toggle.
  5. Save your settings.

HTTPS Required: Your webhook URL must use HTTPS. Plain HTTP endpoints will be rejected for security reasons. Use a valid SSL certificate — self-signed certificates are not supported.

Public Accessibility: Your webhook endpoint must be publicly accessible from the internet. If you are developing locally, use a tunneling service such as ngrok to expose your local server.

How It Works

Once enabled, WhatsBizAPI acts as a relay between WhatsApp's Cloud API and your server:

  1. A user sends a message to your WhatsApp Business number, or a message status changes.
  2. WhatsApp delivers the event to WhatsBizAPI.
  3. WhatsBizAPI processes the event and forwards a structured JSON payload to your configured webhook URL via an HTTP POST request.
  4. Your server processes the payload and returns an HTTP 200 response.

You can enable or disable webhook relay at any time from the dashboard without losing your configuration.

Incoming Message Webhooks

When a customer sends a message to your WhatsApp Business number, WhatsBizAPI forwards the message details to your webhook URL. The payload structure varies based on the message type.

Text Message

Received when a customer sends a plain text message.

Webhook Payload — Text Message
{ "event": "message_received", "phone_number_id": "123456", "from": "+919876543210", "message_id": "wamid.HBgNOTE5ODc2NTQzMjEwFQIAERgSMEI4...", "timestamp": "1677000000", "type": "text", "text": { "body": "Hello!" }, "contact": { "name": "John Doe", "wa_id": "919876543210" } }
Field Type Description
event string Always message_received for incoming messages.
phone_number_id string The ID of the WhatsApp Business phone number that received the message.
from string The sender's phone number in international format.
message_id string Unique WhatsApp message identifier (wamid). Use this for deduplication.
timestamp string Unix timestamp of when the message was sent.
type string The message type: text, image, video, audio, document, location, interactive, sticker.
contact object Contains the sender's WhatsApp profile name and WhatsApp ID.

Image Message

Received when a customer sends an image. The payload includes a media ID that you can use to download the file.

Webhook Payload — Image Message
{ "event": "message_received", "phone_number_id": "123456", "from": "+919876543210", "message_id": "wamid.HBgNOTE5ODc2NTQzMjEwFQIAERgSNEQ3...", "timestamp": "1677000000", "type": "image", "image": { "id": "MEDIA_ID", "mime_type": "image/jpeg", "sha256": "a1b2c3d4e5f6...", "caption": "Photo" }, "contact": { "name": "John Doe", "wa_id": "919876543210" } }

Button Reply

Received when a customer taps a quick-reply button on an interactive message you sent.

Webhook Payload — Button Reply
{ "event": "message_received", "phone_number_id": "123456", "from": "+919876543210", "message_id": "wamid.HBgNOTE5ODc2NTQzMjEwFQIAERgSODk2...", "timestamp": "1677000000", "type": "interactive", "interactive": { "type": "button_reply", "button_reply": { "id": "btn_yes", "title": "Yes" } }, "contact": { "name": "John Doe", "wa_id": "919876543210" } }

List Reply

Received when a customer selects an item from a list message.

Webhook Payload — List Reply
{ "event": "message_received", "from": "+919876543210", "type": "interactive", "interactive": { "type": "list_reply", "list_reply": { "id": "row_1", "title": "Premium Plan", "description": "Best for growing businesses" } } }

Location Message

Received when a customer shares their location.

Webhook Payload — Location Message
{ "event": "message_received", "from": "+919876543210", "type": "location", "location": { "latitude": 12.9716, "longitude": 77.5946, "name": "Bangalore, India", "address": "MG Road, Bangalore, Karnataka 560001" } }

Document Message

Received when a customer sends a document (PDF, spreadsheet, etc.).

Webhook Payload — Document Message
{ "event": "message_received", "from": "+919876543210", "type": "document", "document": { "id": "MEDIA_ID", "mime_type": "application/pdf", "sha256": "f6e5d4c3b2a1...", "filename": "invoice.pdf", "caption": "Please find the invoice attached" } }

Audio & Video Messages

Audio and video messages follow the same structure as image messages, with the media object keyed by type.

Webhook Payload — Audio Message
{ "event": "message_received", "from": "+919876543210", "type": "audio", "audio": { "id": "MEDIA_ID", "mime_type": "audio/ogg; codecs=opus", "voice": true } }

Media Downloads: Media messages include an id field. Use this ID with the WhatsApp Cloud API media endpoint to download the actual file. Media URLs expire after a short period, so download promptly.

Message Status Webhooks

After you send a message, WhatsBizAPI forwards status updates as the message progresses through delivery stages. Use these events to track delivery, read receipts, and failures.

Webhook Payload — Message Status
{ "event": "message_status", "phone_number_id": "123456", "message_id": "wamid.HBgNOTE5ODc2NTQzMjEwFQIAERgSMEI4...", "status": "delivered", "timestamp": "1677000000", "recipient": "+919876543210" }

Status Values

Status Description
sent The message has been sent to WhatsApp servers.
delivered The message has been delivered to the recipient's device.
read The recipient has read the message (blue ticks). Only sent if the recipient has read receipts enabled.
failed The message could not be delivered. Check the errors array for details.

Failed Status with Error Details

When a message fails to deliver, the payload includes an errors array with details about the failure.

Webhook Payload — Failed Status
{ "event": "message_status", "message_id": "wamid.HBgNOTE5ODc2NTQzMjEwFQIAERgSMEI4...", "status": "failed", "timestamp": "1677000000", "recipient": "+919876543210", "errors": [ { "code": 131047, "title": "Message failed to send because more than 24 hours have passed since the customer last replied.", "details": "Re-engagement message required. Use a template message to restart the conversation." } ] }

Status Ordering: Status updates may not always arrive in order. For example, you might receive a read event before a delivered event. Design your application to handle out-of-order updates. The general progression is: sentdeliveredread.

Webhook Security

Every webhook request from WhatsBizAPI includes an HMAC-SHA256 signature that you must verify to ensure the request is authentic and has not been tampered with.

Signature Verification

Each POST request to your webhook URL includes an X-Hub-Signature-256 header. This header contains a SHA-256 HMAC signature of the request body, using your app secret as the key.

Header
X-Hub-Signature-256: sha256=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Verification Example — PHP

PHP Verification
$payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? ''; $appSecret = 'YOUR_APP_SECRET'; $expected = 'sha256=' . hash_hmac('sha256', $payload, $appSecret); if (!hash_equals($expected, $signature)) { http_response_code(403); exit('Invalid signature'); } // Signature valid — process the event $event = json_decode($payload, true);

Verification Example — Node.js

Node.js Verification
const crypto = require('crypto'); function verifyWebhook(req, appSecret) { const signature = req.headers['x-hub-signature-256'] || ''; const payload = JSON.stringify(req.body); const expected = 'sha256=' + crypto .createHmac('sha256', appSecret) .update(payload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); }

Verification Example — Python

Python Verification
import hmac import hashlib def verify_webhook(payload: bytes, signature: str, app_secret: str) -> bool: expected = 'sha256=' + hmac.new( app_secret.encode(), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)

Always Verify Signatures: Never process a webhook payload without verifying the X-Hub-Signature-256 header first. Skipping verification leaves your application vulnerable to spoofed requests. Use a constant-time comparison function (such as hash_equals in PHP or crypto.timingSafeEqual in Node.js) to prevent timing attacks.

Handling Webhook Deliveries

Responding to Webhooks

Your webhook endpoint must return an HTTP 200 OK response as quickly as possible. If your endpoint does not respond within the timeout period, WhatsBizAPI will consider the delivery failed and may retry.

PHP — Respond Immediately
// Return 200 immediately, then process the event http_response_code(200); // Flush the response to the client if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } // Now process the webhook payload asynchronously $event = json_decode(file_get_contents('php://input'), true); processWebhookEvent($event);
Node.js (Express) — Respond Immediately
app.post('/webhook', (req, res) => { // Respond immediately res.status(200).send('OK'); // Process asynchronously processWebhookEvent(req.body).catch(console.error); });

Retry Behavior

If your endpoint returns a non-2xx response or times out, WhatsBizAPI will retry the delivery with exponential backoff. Retries continue for a limited number of attempts. Ensure your handler is idempotent to safely process retried deliveries.

Retry Delay Description
1st ~30 seconds First retry after initial failure.
2nd ~2 minutes Second retry with increased delay.
3rd ~15 minutes Final retry attempt. If this fails, the event is dropped.

Best Practices

Return HTTP 200 Immediately: Always respond with a 200 OK status before doing any processing. Offload heavy work (database writes, API calls, notifications) to a background job or queue. Long-running webhook handlers will cause timeout retries and duplicate events.

Implement Idempotency: WhatsBizAPI uses at-least-once delivery, meaning you may receive the same event more than once. Use the message_id field to deduplicate events. Store processed message IDs and skip any that you have already handled.

Use a Message Queue: For high-volume accounts, push webhook payloads onto a message queue (such as Redis, RabbitMQ, or Amazon SQS) and process them with background workers. This decouples receiving from processing and prevents your webhook endpoint from becoming a bottleneck.

Handle Duplicates Gracefully: Even with idempotency checks, design your business logic to be safe when replayed. For example, if a webhook triggers sending a confirmation email, check whether the email was already sent before sending it again.

Log All Incoming Webhooks: Store the raw JSON payload of every webhook event you receive. This makes debugging significantly easier and provides an audit trail. Log the X-Hub-Signature-256 header alongside the payload for security investigations.

Monitor Your Endpoint: Set up uptime monitoring and alerting for your webhook URL. If your endpoint goes down, you will miss events and they will not be retried indefinitely. Consider implementing a dead-letter queue to capture events that fail all retry attempts.

Complete Webhook Handler Example

Below is a complete example of a webhook handler that verifies the signature, deduplicates events, and processes different event types.

PHP — Complete Handler
<?php $appSecret = getenv('WHATSAPP_APP_SECRET'); $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? ''; // 1. Verify signature $expected = 'sha256=' . hash_hmac('sha256', $payload, $appSecret); if (!hash_equals($expected, $signature)) { http_response_code(403); exit('Invalid signature'); } // 2. Respond immediately http_response_code(200); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } // 3. Parse event $event = json_decode($payload, true); // 4. Deduplicate using message_id $messageId = $event['message_id'] ?? $event['message_id'] ?? null; if ($messageId && isAlreadyProcessed($messageId)) { exit; // Already handled } markAsProcessed($messageId); // 5. Route by event type switch ($event['event']) { case 'message_received': handleIncomingMessage($event); break; case 'message_status': handleStatusUpdate($event); break; } function handleIncomingMessage(array $event): void { $type = $event['type']; $from = $event['from']; switch ($type) { case 'text': $body = $event['text']['body']; // Process text message break; case 'image': case 'video': case 'audio': case 'document': $mediaId = $event[$type]['id']; // Download and process media break; case 'interactive': $reply = $event['interactive']; // Handle button_reply or list_reply break; case 'location': $lat = $event['location']['latitude']; $lng = $event['location']['longitude']; // Process location break; } } function handleStatusUpdate(array $event): void { $status = $event['status']; $messageId = $event['message_id']; switch ($status) { case 'sent': case 'delivered': case 'read': // Update message status in database break; case 'failed': $errors = $event['errors'] ?? []; // Log failure and notify team break; } }

Event Reference

Summary of all webhook event types and their payloads.

Event Trigger Key Fields
message_received A customer sends a message to your number. from, type, message_id, contact, message-type-specific object
message_status Status of a sent message changes. message_id, status, recipient, errors (if failed)

Notes

Testing Webhooks Locally: During development, use a tool like ngrok to create a public tunnel to your local server. Run ngrok http 8000 and use the generated HTTPS URL as your webhook endpoint in the WhatsBizAPI dashboard.

Payload Size: Webhook payloads are typically small (under 10 KB). However, if multiple events are batched, payloads may be larger. Ensure your server can handle request bodies up to 1 MB.

Need Help? If you are having trouble receiving webhooks, verify that your URL is publicly accessible, your SSL certificate is valid, and your endpoint returns a 200 response within 5 seconds. Check the Webhook Relay section of your dashboard for delivery logs and error details.