8.1 KiB
WhatsApp Cloud API Debugging - Complete Implementation
Problem Summary
WhatsApp Cloud API returns HTTP 200 with a wamid, but recipients don't receive messages.
Root Cause: The initial API response (HTTP 200) only means Meta accepted the message, NOT that it was delivered. The actual delivery status comes from webhook callbacks which were not implemented.
Solution Implemented
1. ✅ Message Status Tracking
File: models.py
Added WhatsAppMessage model to track:
wamid- WhatsApp message ID from Metastatus- sent, delivered, read, failedevent_id,guest_id- Context for trackingerror_code,error_title,error_message- Failure details- Timestamps:
sent_at,delivered_at,read_at,failed_at
2. ✅ Webhook Endpoint
File: main.py
Added two webhook endpoints:
GET /whatsapp/webhook # Webhook verification
POST /whatsapp/webhook # Status updates from Meta
What it does:
- Receives delivery status updates from Meta
- Updates message status in database
- Logs all webhook payloads for debugging
- Handles: sent, delivered, read, failed statuses
- Captures error details when messages fail
3. ✅ Enhanced WhatsApp Service
File: whatsapp.py
Changes:
- Added
event_idandguest_idparameters tosend_by_template_key() - Saves messages to database after sending
- Supports dynamic image headers via
header_handle_key - Enhanced logging with full payload dumps
4. ✅ Fixed hina_invitation Template
File: whatsapp_templates.py
Configuration:
{
"meta_name": "hina_invitation",
"language_code": "he",
"header_type": "IMAGE",
"header_handle_key": "invitation_image_url", # Dynamic image from params
"body_params": ["contact_name"],
"button_type": "URL",
"button_url": "https://invy.dvirlabs.com/guest/{{1}}",
"button_param_key": "event_id",
}
5. ✅ Comprehensive Test Script
File: test_whatsapp_debug.py
Features:
- Tests phone normalization
- Validates template configuration
- Sends test message with full logging
- Shows webhook setup instructions
- Provides clear debugging output
6. ✅ Database Migration
File: add_whatsapp_messages_table.py
Creates the whatsapp_messages table.
Setup Instructions
Step 1: Run Database Migration
cd backend
python add_whatsapp_messages_table.py
Step 2: Configure Webhook in Meta Business Manager
-
Go to Meta Business Manager → WhatsApp → Configuration → Webhook
-
Callback URL:
https://invy.dvirlabs.com/whatsapp/webhook -
Verify Token:
- Set this in
.envasWHATSAPP_VERIFY_TOKEN=your_secret_token_here - Use the same value in Meta's webhook configuration
- Set this in
-
Click Verify and Save
-
Subscribe to Fields:
- ✅ messages
Step 3: Update Environment Variables
Add to .env:
# Existing WhatsApp credentials
WHATSAPP_ACCESS_TOKEN=your_meta_access_token
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
WHATSAPP_API_VERSION=v20.0
# NEW: Webhook verification token
WHATSAPP_VERIFY_TOKEN=your_webhook_secret_token_here
Step 4: Test the Integration
cd backend
python test_whatsapp_debug.py
What to check:
- Phone normalization: ✓ All pass
- Template configuration: ✓ All correct
- Message send: ✓ Returns wamid
- Webhook configured: ✓ Verify token set
Step 5: Send Test Message
The script will prompt you to send a test message to: 0504370045
Expected Result:
{
"message_id": "wamid.HBgLOTcyNTA0MzcwMDQ1FQIAERgSQzI4QkE1RkQ5MzFFNTY1RTEwAA==",
"status": "sent",
"to": "+972504370045",
"template": "hina_invitation"
}
How to Debug Delivery Issues
Check 1: Backend Logs
Watch for webhook callbacks:
tail -f backend.log | grep "WhatsApp Webhook"
Expected logs:
[WhatsApp Webhook] Status update: wamid=xxx, status=delivered
[WhatsApp Webhook] ✓ Updated message xxx to status: delivered
Check 2: Database Status
SELECT
wamid,
to_phone,
status,
template_name,
sent_at,
delivered_at,
failed_at,
error_code,
error_message
FROM whatsapp_messages
ORDER BY sent_at DESC
LIMIT 10;
Status meanings:
sent- Meta accepted the messagedelivered- Recipient's phone received itread- Recipient opened the messagefailed- Delivery failed (check error_code/error_message)
Check 3: Common Failure Reasons
If status stays "sent" and never reaches "delivered":
-
Phone number not on WhatsApp
- Error code: 131026
- Solution: Verify the recipient has WhatsApp
-
Template parameters mismatch
- Error code: 132000
- Solution: Check template in Meta matches your params
-
Image URL not accessible
- Error code: 132015
- Solution: Ensure image URL is publicly accessible
-
Phone number blocked your business
- Error code: 131031
- Solution: Recipient must unblock your business number
-
Rate limiting
- Error code: 130429
- Solution: Slow down sending rate
Check 4: Webhook Delivery Test
Test webhook is receiving callbacks:
curl -X GET "https://invy.dvirlabs.com/whatsapp/webhook?hub.mode=subscribe&hub.verify_token=YOUR_TOKEN&hub.challenge=test123"
Expected: test123 (echo challenge)
Example: Sending hina_invitation
from whatsapp import get_whatsapp_service
service = get_whatsapp_service(db)
params = {
"contact_name": "איילה חורב",
"event_id": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25",
"invitation_image_url": "https://api-invy.dvirlabs.com/uploads/1d32b5fbab0f494cae443b4188a83ca3.jpg",
}
result = await service.send_by_template_key(
template_key="hina_invitation",
to_phone="0504370045",
params=params,
event_id="f3122a7d-1d7c-4cc1-955d-1c6b7358bd25",
guest_id="guest-uuid-here",
)
print(f"Message ID: {result['message_id']}")
Generated payload:
{
"messaging_product": "whatsapp",
"to": "+972504370045",
"type": "template",
"template": {
"name": "hina_invitation",
"language": { "code": "he" },
"components": [
{
"type": "header",
"parameters": [{
"type": "image",
"image": {
"link": "https://api-invy.dvirlabs.com/uploads/1d32b5fbab0f494cae443b4188a83ca3.jpg"
}
}]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "איילה חורב" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25" }
]
}
]
}
}
Phone Normalization
Israeli numbers:
0504370045→+972504370045✓050-437-0045→+972504370045✓+972504370045→+972504370045✓972504370045→+972504370045✓
Files Modified
- ✅
models.py- Added WhatsAppMessage model - ✅
main.py- Added webhook endpoints, updated imports - ✅
whatsapp.py- Added event/guest tracking, dynamic image support - ✅
whatsapp_templates.py- Fixed hina_invitation template
Files Created
- ✅
test_whatsapp_debug.py- Comprehensive debugging script - ✅
add_whatsapp_messages_table.py- Database migration - ✅
WHATSAPP_DEBUGGING.md- This documentation
Next Steps
- Run migration:
python add_whatsapp_messages_table.py - Configure webhook in Meta Business Manager
- Set WHATSAPP_VERIFY_TOKEN in .env
- Run test script:
python test_whatsapp_debug.py - Monitor webhook logs for delivery status
- Check database for message status updates
Critical Understanding
HTTP 200 ≠ Message Delivered
When Meta returns HTTP 200, it means:
- ✓ Request was valid
- ✓ Message was queued for delivery
- ✗ NOT that the recipient received it
Actual delivery confirmation comes from webhook callbacks.
Without webhooks, you'll never know if messages were delivered, read, or failed.
This is why webhook implementation is CRITICAL.