Files
whatshooked/WHATSAPP_BUSINESS.md
Hein 98fc28fc5f
Some checks failed
CI / Test (1.22) (push) Failing after -24m48s
CI / Test (1.23) (push) Failing after -24m45s
CI / Build (push) Successful in -27m0s
CI / Lint (push) Successful in -26m50s
docs(whatsapp): Update WhatsApp Business API setup guide
* Add registration process for new phone numbers
* Clarify importance of registration for sending messages and 2FA
* Improve formatting and clarity throughout the document
2026-01-30 17:06:04 +02:00

29 KiB

WhatsApp Business API Setup Guide

This guide will help you set up WhatsApp Business API credentials for use with WhatsHooked.

Common Error: "Object does not exist or missing permissions"

If you see this error:

Failed to connect client account_id=test error="API returned status 400:
{\"error\":{\"message\":\"Unsupported get request. Object with ID 'XXXXXXXXX' does not exist,
cannot be loaded due to missing permissions, or does not support this operation...\",
\"type\":\"GraphMethodException\",\"code\":100,\"error_subcode\":33...}}"

This means your access token lacks the required WhatsApp Business API permissions.

Prerequisites

Before you begin, ensure you have:

  1. A Meta Business Account
  2. WhatsApp Business API access (approved by Meta)
  3. A verified WhatsApp Business phone number
  4. Admin access to your Meta Business Manager

Step 1: Access Meta Business Manager

  1. Go to Meta Business Manager
  2. Select your business account
  3. Navigate to Business Settings (gear icon)

System Users provide permanent access tokens that don't expire with user sessions.

  1. In Business Settings, go to UsersSystem Users
  2. Click Add to create a new system user
  3. Enter a name (e.g., "WhatsHooked API Access")
  4. Select Admin role
  5. Click Create System User

Step 3: Assign the System User to WhatsApp

  1. In the System User details, scroll to Assign Assets
  2. Click Add Assets
  3. Select Apps
  4. Choose your WhatsApp Business app
  5. Grant Full Control
  6. Click Add People
  7. Select WhatsApp Accounts
  8. Choose your WhatsApp Business Account
  9. Grant Full Control
  10. Click Save Changes

Step 4: Generate Access Token with Required Permissions

  1. In the System User details, click Generate New Token
  2. Select your app from the dropdown
  3. IMPORTANT: Check these permissions:
    • whatsapp_business_management
    • whatsapp_business_messaging
  4. Set token expiration (choose "Never" for permanent tokens)
  5. Click Generate Token
  6. CRITICAL: Copy the token immediately - you won't see it again!

Verify Token Permissions

You can verify your token has the correct permissions:

# Replace YOUR_TOKEN with your actual access token
curl -X GET 'https://graph.facebook.com/v21.0/debug_token?input_token=YOUR_TOKEN' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Look for "scopes" in the response - it should include:

{
  "data": {
    "scopes": [
      "whatsapp_business_management",
      "whatsapp_business_messaging",
      ...
    ]
  }
}

Step 5: Get Your Phone Number ID

The Phone Number ID is NOT your actual phone number - it's a unique identifier from Meta.

Method 1: Via WhatsApp Manager (Easiest)

  1. Go to WhatsApp Manager
  2. Select your WhatsApp Business Account
  3. Click API Setup in the left sidebar
  4. Copy the Phone Number ID (looks like: 123456789012345)

Method 2: Via API

# Replace YOUR_TOKEN and YOUR_BUSINESS_ACCOUNT_ID
curl -X GET 'https://graph.facebook.com/v21.0/YOUR_BUSINESS_ACCOUNT_ID/phone_numbers' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Response:

{
  "data": [
    {
      "verified_name": "Your Business Name",
      "display_phone_number": "+1 234-567-8900",
      "id": "123456789012345", // <- This is your Phone Number ID
      "quality_rating": "GREEN"
    }
  ]
}

Step 6: Get Your Business Account ID (Optional)

# Get all WhatsApp Business Accounts you have access to
curl -X GET 'https://graph.facebook.com/v21.0/me/businesses' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Or find it in WhatsApp Manager:

  1. Go to WhatsApp Manager
  2. Click on Settings (gear icon)
  3. The Business Account ID is shown in the URL: https://business.facebook.com/wa/manage/home/?waba_id=XXXXXXXXX

Step 7: Register Your Phone Number (Required for New Numbers)

If this is a new phone number that hasn't been used for WhatsApp Business API before, you need to register it first.

Why Registration is Needed

Phone numbers must be registered with WhatsApp to:

  • Activate the number for sending messages
  • Set up two-factor authentication (2FA)
  • Verify ownership of the phone number

Registration Process

1. Get your 2FA PIN:

You should have received a 6-digit PIN when you set up your WhatsApp Business phone number. If you don't have it:

  • Go to WhatsApp Manager
  • Select your phone number
  • Go to SettingsTwo-step verification
  • Note or reset your PIN

2. Register the phone number:

# Replace PHONE_NUMBER_ID with your Phone Number ID
# Replace YOUR_TOKEN with your access token
# Replace 890523 with your actual 2FA PIN
curl -X POST 'https://graph.facebook.com/v21.0/PHONE_NUMBER_ID/register' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "messaging_product": "whatsapp",
    "pin": "123123"
  }'

Successful Response:

{
  "success": true
}

Error Response (if already registered):

{
  "error": {
    "message": "(#131030) Phone number has already been registered",
    "type": "OAuthException",
    "code": 131030,
    "fbtrace_id": "..."
  }
}

Note: If you get error code 131030, it means your number is already registered and you can skip this step.

Important Notes

  • ⚠️ You only need to register once - don't repeat this step unless you're setting up a new number
  • ⚠️ Keep your PIN secure - it's used for two-factor authentication
  • ⚠️ Registration can take a few minutes - wait before sending messages
  • ⚠️ If registration fails, verify:
    • Your PIN is correct (6 digits)
    • Your access token has whatsapp_business_management permission
    • The phone number is verified in Meta Business Manager

Step 8: Test Your Credentials

After registration, test your credentials to confirm everything is working:

# Replace PHONE_NUMBER_ID and YOUR_TOKEN
curl -X GET 'https://graph.facebook.com/v21.0/PHONE_NUMBER_ID' \
  -H 'Authorization: Bearer YOUR_TOKEN'

If successful, you'll get a response like:

{
  "verified_name": "Your Business Name",
  "display_phone_number": "+1 234-567-8900",
  "id": "123456789012345",
  "quality_rating": "GREEN"
}

If you get an error like "error_subcode":33, your token lacks permissions - go back to Step 4.

Step 9: Configure WhatsHooked

Update your config.json with the Business API configuration:

{
  "whatsapp": [
    {
      "id": "business",
      "type": "business-api",
      "phone_number": "+1234567890",
      "business_api": {
        "phone_number_id": "123456789012345",
        "access_token": "EAAxxxxxxxxxxxx_your_permanent_token_here",
        "business_account_id": "987654321098765",
        "api_version": "v21.0",
        "verify_token": "your_secure_random_token_here"
      }
    }
  ]
}

Configuration Fields

Field Required Description
id Yes Unique identifier for this account in WhatsHooked
type Yes Must be "business-api"
phone_number Yes Your WhatsApp Business phone number (E.164 format)
phone_number_id Yes Phone Number ID from Meta (from Step 5)
access_token Yes Permanent access token (from Step 4)
business_account_id No WhatsApp Business Account ID (optional, for reference)
api_version No Graph API version (defaults to "v21.0")
verify_token Yes Random string for webhook verification (see Step 9a)

Step 9a: Generate Verify Token

The verify token is used by Meta to verify your webhook endpoint. Generate a secure random string:

# Generate a random token
openssl rand -hex 32

# Or use any secure random string like:
# "my_secure_verify_token_abc123xyz789"

Add this token to your config.json (see above) and save it - you'll need it for webhook configuration in Step 11.

Step 10: Start WhatsHooked

./bin/whatshook-server -config config.json

You should see:

INFO Business API client connected account_id=business phone=+1234567890
INFO Hook manager started and subscribed to events event_types=13

If you see Failed to connect client, check the error message and verify:

  1. Phone Number ID is correct
  2. Access token has required permissions
  3. Access token hasn't expired
  4. Business Account has WhatsApp API access enabled

Step 11: Configure Webhook in Meta Developer Console

WhatsHooked provides a webhook endpoint to receive incoming messages and status updates from WhatsApp.

11.1: Webhook URL Format

Your webhook URL should be:

https://your-domain.com/webhooks/whatsapp/{account_id}

Where {account_id} matches the id field in your config (e.g., "business").

Example: If your domain is api.example.com and account ID is business:

https://api.example.com/webhooks/whatsapp/business

11.2: Configure in Meta Developer Console

  1. Go to Meta Developers
  2. Select your app
  3. Navigate to WhatsAppConfiguration
  4. Under "Webhook", click Edit
  5. Enter:
    • Callback URL: https://your-domain.com/webhooks/whatsapp/business
    • Verify Token: The same token from your config.json (verify_token field)
  6. Click Verify and Save

Meta will send a GET request to verify your endpoint. If verification succeeds, you'll see a green checkmark.

11.3: Subscribe to Webhook Events

After verification, subscribe to these webhook fields:

  • messages - Incoming messages and message status updates
  • message_template_status_update - Template approval/rejection (optional)
  • account_update - Account changes (optional)
  • phone_number_quality_update - Quality rating changes (optional)

Click Subscribe for each field you want to receive.

Supported Webhook Events

WhatsHooked supports all WhatsApp Business API webhook events and message types:

Message Types

Type Supported Downloads Media Description
text N/A Text messages
image Images with optional caption
video Videos with optional caption
document PDFs, docs, etc. with filename
audio Voice messages and audio files
sticker Animated and static stickers
location N/A GPS coordinates with name/address
contacts N/A Shared contact cards (vCard)
interactive N/A Button/list/flow replies
button N/A Quick reply button responses
reaction N/A Emoji reactions to messages
order N/A Catalog/commerce orders
system N/A System notifications

Status Updates

Status Event Description
sent message.sent Message sent from your number
delivered message.delivered Message delivered to recipient
read message.read Message read by recipient
failed message.failed Message delivery failed

Webhook Notification Types

Field Description Events Published
messages Message events message.received, message status updates
message_template_status_update Template changes Logged to console
account_update Account config changes Logged to console
phone_number_quality_update Quality rating changes Logged to console
phone_number_name_update Display name changes Logged to console
account_alerts Important alerts Logged to console

Webhook Security

WhatsHooked implements proper webhook security:

  1. Verification: Uses the verify_token to verify Meta's webhook setup request
  2. Account isolation: Each account has its own webhook endpoint path
  3. No authentication required: Meta's webhooks don't support custom auth headers
  4. Validation: Verifies webhook payload structure

Webhook Verification Flow

Meta sends: GET /webhooks/whatsapp/business?hub.mode=subscribe&hub.verify_token=YOUR_TOKEN&hub.challenge=CHALLENGE
                                              ↓
                                    WhatsHooked verifies token
                                              ↓
                                 Returns CHALLENGE (200 OK) if valid
                                       403 Forbidden if invalid

Receiving Messages

Meta sends: POST /webhooks/whatsapp/business
                                              ↓
                                    WhatsHooked processes webhook
                                              ↓
                                    Downloads media (if present)
                                              ↓
                                    Publishes to event bus
                                              ↓
                                    Triggers your configured hooks
                                              ↓
                                    Returns 200 OK

Testing Webhooks

Test with Meta's Test Button

  1. In WhatsApp Configuration → Webhooks
  2. Click Test next to "messages"
  3. Select a sample event (e.g., "Text Message")
  4. Click Send to My Server
  5. Check WhatsHooked logs for the received event

Test with Real Messages

  1. Send a message to your WhatsApp Business number
  2. Check WhatsHooked logs (set "log_level": "debug" for details):
DEBUG Publishing message received event account_id=business message_id=wamid.xxx from=1234567890 type=text
DEBUG Hook manager received event event_type=message.received
DEBUG Hook matches event hook_id=message_hook event_type=message.received
DEBUG Found relevant hooks for event event_type=message.received hook_count=1
DEBUG Sending to hook hook_id=message_hook url=https://your-webhook.com/messages
  1. Your webhook should receive the payload

Webhook Payload Example

{
  "account_id": "business",
  "message_id": "wamid.HBgNMTIzNDU2Nzg5MAUCABEYEjQyMzRGRDhENzk5MkY5OUFBMQA",
  "from": "1234567890",
  "to": "1234567890",
  "text": "Hello World",
  "timestamp": "2026-01-30T12:00:00Z",
  "is_group": false,
  "sender_name": "John Doe",
  "message_type": "text"
}

Step 12: Configure Your Webhooks

WhatsHooked forwards events to your own webhook URLs. Configure them in config.json:

{
  "hooks": [
    {
      "id": "message_hook",
      "name": "Message Handler",
      "url": "https://your-app.com/api/whatsapp/messages",
      "method": "POST",
      "headers": {
        "Authorization": "Bearer your-app-token"
      },
      "active": true,
      "events": [
        "message.received",
        "message.sent",
        "message.delivered",
        "message.read"
      ],
      "description": "Receives all message events"
    }
  ]
}

Hook Configuration Fields

Field Required Description
id Yes Unique identifier for this hook
name Yes Human-readable name
url Yes Your webhook URL to receive events
method Yes HTTP method (usually "POST")
headers No Custom headers (for authentication, etc.)
active Yes Enable/disable this hook
events No Event types to receive (empty = all events)
description No Description for documentation

Available Event Types

Message Events:

  • message.received - Incoming messages
  • message.sent - Outgoing messages
  • message.delivered - Delivery confirmations
  • message.read - Read receipts
  • message.failed - Delivery failures

Connection Events:

  • whatsapp.connected - Account connected
  • whatsapp.disconnected - Account disconnected

QR Code Events (whatsmeow only):

  • whatsapp.qr.code - QR code for pairing
  • whatsapp.qr.timeout - QR code expired
  • whatsapp.qr.error - QR code error

Hook Events:

  • hook.triggered - Hook was called
  • hook.success - Hook responded successfully
  • hook.failed - Hook call failed

Query Parameters

WhatsHooked automatically adds query parameters to your webhook URL:

https://your-app.com/api/whatsapp/messages?event=message.received&account_id=business
  • event - The event type
  • account_id - The WhatsApp account that triggered the event

Message Cache System

WhatsHooked includes a message cache that stores events when no active webhooks are configured. This ensures zero message loss.

Enable Message Cache

Add to your config.json:

{
  "message_cache": {
    "enabled": true,
    "data_path": "./data/message_cache",
    "max_age_days": 7,
    "max_events": 10000
  }
}

When Events Are Cached

Events are automatically cached when:

  • No webhooks are configured for the event type
  • All webhooks are inactive ("active": false)
  • No webhooks match the event in their events array

Cache Management API

List cached events:

curl -u username:password http://localhost:8080/api/cache

Get cache statistics:

curl -u username:password http://localhost:8080/api/cache/stats

Replay all cached events:

curl -X POST -u username:password http://localhost:8080/api/cache/replay

Replay specific event:

curl -X POST -u username:password \
  "http://localhost:8080/api/cache/event/replay?id=EVENT_ID"

Delete cached event:

curl -X DELETE -u username:password \
  "http://localhost:8080/api/cache/event/delete?id=EVENT_ID"

Clear all cache:

curl -X DELETE -u username:password \
  "http://localhost:8080/api/cache/clear?confirm=true"

Cache Workflow Example

  1. Disable webhooks → New messages get cached
  2. Configure/enable webhooks → Future messages delivered immediately
  3. Call replay API → Cached messages delivered to webhooks
  4. Successful delivery → Events removed from cache automatically

Troubleshooting

Webhooks Not Receiving Events

Check these items:

  1. Verify token is correct in both config.json and Meta Developer Console
  2. Check webhook is active in Meta console (green checkmark)
  3. Verify URL is accessible from internet (Meta needs to reach it)
  4. Check logs with "log_level": "debug":
    DEBUG Publishing message received event account_id=business
    DEBUG Hook manager received event event_type=message.received
    DEBUG Hook matches event hook_id=message_hook
    
  5. Test with curl:
    # Send test message to your WhatsApp Business number
    # Check if webhook receives it
    

Webhook Verification Fails

Error: "The callback URL or verify token couldn't be validated"

Causes:

  • verify_token mismatch between config.json and Meta console
  • WhatsHooked server not running
  • Firewall blocking Meta's IP ranges
  • Wrong webhook URL format

Fix:

  1. Ensure server is running: ./bin/whatshook-server -config config.json
  2. Check logs for verification attempt
  3. Verify token matches exactly (case-sensitive)
  4. Test URL is accessible: curl https://your-domain.com/webhooks/whatsapp/business

Messages Not Cached

Check:

  1. message_cache.enabled is true in config
  2. Hooks are actually inactive or not matching events
  3. Check cache stats: curl -u user:pass http://localhost:8080/api/cache/stats

No Hooks Configured Error

If events are being cached but you have hooks configured, check:

  • Hook "active" is true
  • Hook "events" array includes the event type (or is empty for all events)
  • Hook URL is reachable and responding with 2xx status

Enable debug logging to trace the issue:

{
  "log_level": "debug"
}

Webhook Payload Examples

Text Message

{
  "account_id": "business",
  "message_id": "wamid.HBgNMTIzNDU2Nzg5MAUCABEYEjQyMzRGRDhENzk5MkY5OUFBMQA",
  "from": "1234567890",
  "to": "1234567890",
  "text": "Hello, how can I help?",
  "timestamp": "2026-01-30T12:00:00Z",
  "is_group": false,
  "sender_name": "John Doe",
  "message_type": "text"
}

Image Message (with media)

{
  "account_id": "business",
  "message_id": "wamid.xxx",
  "from": "1234567890",
  "to": "1234567890",
  "text": "Check this out!",
  "timestamp": "2026-01-30T12:00:00Z",
  "is_group": false,
  "sender_name": "John Doe",
  "message_type": "image",
  "media": {
    "type": "image",
    "mime_type": "image/jpeg",
    "filename": "wamid.xxx_a1b2c3d4.jpg",
    "url": "http://localhost:8080/api/media/business/wamid.xxx_a1b2c3d4.jpg",
    "base64": "..." // Only if media.mode is "base64" or "both"
  }
}

Location Message

{
  "account_id": "business",
  "message_id": "wamid.xxx",
  "from": "1234567890",
  "to": "1234567890",
  "text": "Location: Office (123 Main St) - 40.712800, -74.006000",
  "timestamp": "2026-01-30T12:00:00Z",
  "is_group": false,
  "sender_name": "John Doe",
  "message_type": "location"
}

Button Reply (Interactive)

{
  "account_id": "business",
  "message_id": "wamid.xxx",
  "from": "1234567890",
  "to": "1234567890",
  "text": "Yes, I'm interested",
  "timestamp": "2026-01-30T12:00:00Z",
  "is_group": false,
  "sender_name": "John Doe",
  "message_type": "interactive"
}

Delivery Status

{
  "event_type": "message.delivered",
  "timestamp": "2026-01-30T12:00:05Z",
  "data": {
    "account_id": "business",
    "message_id": "wamid.xxx",
    "from": "1234567890",
    "timestamp": "2026-01-30T12:00:05Z"
  }
}

Complete Configuration Example

Here's a complete config.json with all Business API features:

{
  "server": {
    "host": "0.0.0.0",
    "port": 8080,
    "default_country_code": "1",
    "username": "admin",
    "password": "secure_password",
    "auth_key": "optional_api_key"
  },
  "whatsapp": [
    {
      "id": "business",
      "type": "business-api",
      "phone_number": "+1234567890",
      "business_api": {
        "phone_number_id": "123456789012345",
        "access_token": "EAAxxxxxxxxxxxx",
        "business_account_id": "987654321098765",
        "api_version": "v21.0",
        "verify_token": "my_secure_random_token_abc123"
      }
    }
  ],
  "hooks": [
    {
      "id": "message_hook",
      "name": "Message Handler",
      "url": "https://your-app.com/api/whatsapp/messages",
      "method": "POST",
      "headers": {
        "Authorization": "Bearer your-app-secret-token",
        "X-Custom-Header": "value"
      },
      "active": true,
      "events": [
        "message.received",
        "message.sent",
        "message.delivered",
        "message.read"
      ],
      "description": "Handles all message events"
    },
    {
      "id": "status_hook",
      "name": "Connection Monitor",
      "url": "https://your-app.com/api/whatsapp/status",
      "method": "POST",
      "active": true,
      "events": ["whatsapp.connected", "whatsapp.disconnected"],
      "description": "Monitors connection status"
    }
  ],
  "media": {
    "data_path": "./data/media",
    "mode": "link",
    "base_url": "https://your-domain.com"
  },
  "message_cache": {
    "enabled": true,
    "data_path": "./data/message_cache",
    "max_age_days": 7,
    "max_events": 10000
  },
  "event_logger": {
    "enabled": true,
    "targets": ["file", "sqlite"],
    "file_dir": "./data/events",
    "table_name": "event_logs"
  },
  "log_level": "info"
}

Advanced Features

Media Handling Modes

WhatsHooked supports three media delivery modes:

1. Link Mode (default, recommended)

{
  "media": {
    "mode": "link",
    "base_url": "https://your-domain.com"
  }
}
  • Downloads media and stores locally
  • Webhooks receive URL: https://your-domain.com/api/media/business/filename.jpg
  • Efficient for large media files

2. Base64 Mode

{
  "media": {
    "mode": "base64"
  }
}
  • Encodes media as base64 in webhook payload
  • No separate download needed
  • Good for small files, increases payload size

3. Both Mode

{
  "media": {
    "mode": "both"
  }
}
  • Provides both URL and base64
  • Maximum flexibility, largest payloads

Event Logger

Track all events to file and/or database:

{
  "event_logger": {
    "enabled": true,
    "targets": ["file", "sqlite", "postgres"],
    "file_dir": "./data/events",
    "table_name": "event_logs"
  },
  "database": {
    "type": "postgres",
    "host": "localhost",
    "port": 5432,
    "username": "whatshooked",
    "password": "password",
    "database": "whatshooked"
  }
}

Logged events include:

  • All message events
  • Connection status changes
  • Hook success/failure
  • Webhook triggers

Two-Way Communication

Your webhooks can respond to trigger outgoing messages:

Webhook Response Format:

{
  "send_message": true,
  "to": "1234567890",
  "text": "Thanks for your message!",
  "account_id": "business"
}

This sends a reply immediately when your webhook receives an event.

Production Deployment Checklist

Before going live:

  • Use a System User token (not personal user token)
  • Set verify_token to a secure random string (32+ characters)
  • Configure webhooks in Meta Developer Console
  • Subscribe to required webhook fields (messages, etc.)
  • Test webhook verification succeeds
  • Enable HTTPS for production (required by Meta)
  • Set up firewall rules to allow Meta's webhook IPs
  • Configure authentication (username/password or auth_key)
  • Enable message cache for reliability
  • Set up event logging for audit trail
  • Test sending and receiving messages
  • Monitor logs for errors
  • Set up log rotation for production
  • Document your webhook endpoints
  • Set up monitoring/alerts for webhook failures

Troubleshooting Common Issues

Error: "Object with ID does not exist" (error_subcode: 33)

Cause: One of the following:

  • Incorrect Phone Number ID
  • Access token lacks permissions
  • Access token expired

Fix:

  1. Verify token permissions (see Step 4)
  2. Double-check Phone Number ID (see Step 5)
  3. Generate a new token if needed

Error: "Invalid OAuth access token"

Cause: Token is invalid or expired

Fix: Generate a new access token (Step 4)

Error: "Application does not have permission"

Cause: App not added to WhatsApp Business Account

Fix: Complete Step 3 to assign System User to WhatsApp

Token Expires Too Quickly

Issue: Using a User Access Token instead of System User token

Fix:

  • Use a System User (Step 2) for permanent tokens
  • User Access Tokens expire in 60 days
  • System User tokens can be set to "Never expire"

Security Best Practices

  1. Never commit tokens to version control

    • Add config.json to .gitignore
    • Use environment variables for sensitive data
  2. Rotate tokens regularly

    • Even "permanent" tokens should be rotated periodically
    • Revoke old tokens when generating new ones
  3. Use System Users for production

    • Don't use personal User Access Tokens
    • System Users provide better security and permanence
  4. Limit token permissions

    • Only grant the minimum required permissions
    • For WhatsHooked, you only need:
      • whatsapp_business_management
      • whatsapp_business_messaging
  5. Monitor token usage

    • Check token status regularly via debug_token endpoint
    • Watch for unexpected API calls

Additional Resources

Support

If you continue to have issues:

  1. Verify your Meta Business Account has WhatsApp API access
  2. Check that your phone number is verified in WhatsApp Manager
  3. Ensure you're using Graph API v21.0 or later
  4. Review the WhatsApp Business API changelog for updates