django-meta-whatsapp includes a built-in webhook endpoint that receives and processes all events from Meta — inbound messages, message delivery receipts, read receipts, and In-App Signup subscriptions — automatically updating your database and firing Django signals.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/rahul-baberwal/django-meta-whatsapp/llms.txt
Use this file to discover all available pages before exploring further.
Webhook URL
The webhook endpoint is mounted wherever you includedjango_meta_whatsapp.urls. With the standard mount path:
urls.py
Configuring in Meta
Open your Meta App dashboard
Go to Meta for Developers → select your App → WhatsApp → Configuration in the left sidebar.
Set the Callback URL
Enter your full webhook URL:
https://yourdomain.com/whatsapp/webhook/Meta requires a valid HTTPS endpoint. Use a tool like ngrok for local development.Set the Verify Token
Enter the same token you configured in your
WHATSAPP settings (or the verify_token field on your WhatsAppAccount record):settings.py
How Verification Works
Meta verifies your webhook by sending aGET request with three query parameters:
| Parameter | Value |
|---|---|
hub.mode | "subscribe" |
hub.verify_token | The token you configured |
hub.challenge | A random string Meta wants echoed back |
hub.verify_token against all active WhatsAppAccount records first (checking the verify_token field), then falls back to WHATSAPP["VERIFY_TOKEN"] in settings. On a match, it responds with the bare hub.challenge string. On mismatch, it returns 403 Forbidden.
This means you can use per-account verify tokens for multi-account setups, or a single global fallback token — both are handled automatically.
What Gets Processed Automatically
Every POST payload from Meta is stored inWhatsAppWebhookLog before processing begins, so nothing is ever lost. The _process() handler then routes each event:
Inbound Messages
When a user sends your business a WhatsApp message:- Creates a
WhatsAppMessagerecord withdirection="inbound" - Creates a
WhatsAppContactrecord for the sender if one does not exist (get_or_createonphone) - Creates or retrieves the
WhatsAppConversationfor that phone number - Increments
WhatsAppConversation.unread_countby 1 and updateslast_message_at - Stores the full raw webhook payload in
WhatsAppMessage.raw_payloadfor debugging - Fires the
whatsapp_message_receivedsignal
text, image, video, audio, document, location, reaction, interactive, button, sticker, voice.
Message Status Updates
When a message’s delivery status changes (sent → delivered → read, or failed):- Updates
WhatsAppMessage.statusby matching onmessage_id - Updates
WhatsAppCampaignRecipient.statusif the message was part of a campaign
WhatsAppMessage.status directly if you need to react to delivery events.
In-App Signup Subscriptions
When a user subscribes through awa.me deep link:
- Creates or updates the
WhatsAppContactrecord for the subscriber’s phone number (wa_id) - Sets
contact.subscribed_via_signupandcontact.subscribed_at - Increments
WhatsAppSignup.subscriber_count - Adds the contact to
signup.auto_add_to_labelif one is configured - Fires the
whatsapp_user_subscribedsignal
Webhook Logs
Every webhook payload received is stored inWhatsAppWebhookLog before processing. You can view all logs in the admin UI at /whatsapp/settings/webhook-logs/.
Each log record contains:
| Field | Description |
|---|---|
received_at | DateTimeField — timestamp when the payload arrived |
payload | JSONField — the complete raw payload from Meta |
processed | BooleanField — present on the model; defaults to False |
error | TextField — present on the model; blank by default |
Webhook logs are written before processing, so even if your signal receivers raise an exception, the raw payload is preserved for replay or debugging. The
processed and error fields are available on the model for you to set in custom post-processing hooks — the built-in webhook view does not update them after creation.Multi-Account Webhook
A single webhook URL serves all WhatsApp Business Accounts. The webhook payload from Meta includes aphone_number_id inside value.metadata. The webhook engine looks this up against all WhatsAppAccount records to route the event to the correct account:
Security
- The webhook endpoint is CSRF-exempt — this is required by Meta, which cannot include a Django CSRF token in its outbound POST requests.
- All other views in django-meta-whatsapp require authentication via
LoginRequiredMixin. - The verify token check on
GETrequests acts as the authentication mechanism for Meta’s subscription verification.
- Always use HTTPS in production. Meta requires a valid SSL certificate on your callback URL — self-signed certificates are rejected.
- Rotate your verify tokens periodically and update both your settings and the Meta App dashboard together.
- Monitor
WhatsAppWebhookLogfor unexpected payloads or high error rates.
Reacting to Inbound Messages
Connect to thewhatsapp_message_received signal to add custom logic for every message that arrives. Here’s a complete auto-reply example:
myapp/handlers.py
AppConfig.ready():
myapp/apps.py
Signal receivers execute synchronously inside the webhook request. Keep them fast — offload slow operations to Celery tasks to avoid delaying Meta’s response and triggering retries.