invy/WHATSAPP_DEBUGGING.md
dvirlabs f3537a66b2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Try to fix
2026-05-13 20:51:51 +03:00

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 Meta
  • status - sent, delivered, read, failed
  • event_id, guest_id - Context for tracking
  • error_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:

  1. Added event_id and guest_id parameters to send_by_template_key()
  2. Saves messages to database after sending
  3. Supports dynamic image headers via header_handle_key
  4. 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

  1. Go to Meta Business ManagerWhatsAppConfigurationWebhook

  2. Callback URL:

    https://invy.dvirlabs.com/whatsapp/webhook
    
  3. Verify Token:

    • Set this in .env as WHATSAPP_VERIFY_TOKEN=your_secret_token_here
    • Use the same value in Meta's webhook configuration
  4. Click Verify and Save

  5. 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:

  1. Phone normalization: ✓ All pass
  2. Template configuration: ✓ All correct
  3. Message send: ✓ Returns wamid
  4. 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 message
  • delivered - Recipient's phone received it
  • read - Recipient opened the message
  • failed - Delivery failed (check error_code/error_message)

Check 3: Common Failure Reasons

If status stays "sent" and never reaches "delivered":

  1. Phone number not on WhatsApp

    • Error code: 131026
    • Solution: Verify the recipient has WhatsApp
  2. Template parameters mismatch

    • Error code: 132000
    • Solution: Check template in Meta matches your params
  3. Image URL not accessible

    • Error code: 132015
    • Solution: Ensure image URL is publicly accessible
  4. Phone number blocked your business

    • Error code: 131031
    • Solution: Recipient must unblock your business number
  5. 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

  1. models.py - Added WhatsAppMessage model
  2. main.py - Added webhook endpoints, updated imports
  3. whatsapp.py - Added event/guest tracking, dynamic image support
  4. whatsapp_templates.py - Fixed hina_invitation template

Files Created

  1. test_whatsapp_debug.py - Comprehensive debugging script
  2. add_whatsapp_messages_table.py - Database migration
  3. WHATSAPP_DEBUGGING.md - This documentation

Next Steps

  1. Run migration: python add_whatsapp_messages_table.py
  2. Configure webhook in Meta Business Manager
  3. Set WHATSAPP_VERIFY_TOKEN in .env
  4. Run test script: python test_whatsapp_debug.py
  5. Monitor webhook logs for delivery status
  6. 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.