# 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](https://business.facebook.com/)
2. Select your business account
3. Navigate to **Business Settings** (gear icon)

## Step 2: Create a System User (Recommended for Production)

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

1. In Business Settings, go to **Users** → **System 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:

```bash
# 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:

```json
{
  "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](https://business.facebook.com/wa/manage/home/)
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

```bash
# 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:

```json
{
  "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)

```bash
# 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](https://business.facebook.com/wa/manage/home/)
- Select your phone number
- Go to **Settings** → **Two-step verification**
- Note or reset your PIN

**2. Register the phone number:**

```bash
# 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:**

```json
{
  "success": true
}
```

**Error Response (if already registered):**

```json
{
  "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:

```bash
# 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:

```json
{
  "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:

```json
{
  "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:

```bash
# 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

```bash
./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](https://developers.facebook.com/)
2. Select your app
3. Navigate to **WhatsApp** → **Configuration**
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
```

3. Your webhook should receive the payload

### Webhook Payload Example

```json
{
  "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`:

```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`:

```json
{
  "message_cache": {
    "enabled": true,
    "storage": "database",
    "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:**

```bash
curl -u username:password http://localhost:8825/api/cache
```

**Get cache statistics:**

```bash
curl -u username:password http://localhost:8825/api/cache/stats
```

**Replay all cached events:**

```bash
curl -X POST -u username:password http://localhost:8825/api/cache/replay
```

**Replay specific event:**

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

**Delete cached event:**

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

**Clear all cache:**

```bash
curl -X DELETE -u username:password \
  "http://localhost:8825/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**:
   ```bash
   # 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:8825/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:

```json
{
  "log_level": "debug"
}
```

## Webhook Payload Examples

### Text Message

```json
{
  "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)

```json
{
  "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:8825/api/media/business/wamid.xxx_a1b2c3d4.jpg",
    "base64": "..." // Only if media.mode is "base64" or "both"
  }
}
```

### Location Message

```json
{
  "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)

```json
{
  "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

```json
{
  "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:

```json
{
  "server": {
    "host": "0.0.0.0",
    "port": 8825,
    "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,
    "storage": "database",
    "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)

```json
{
  "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**

```json
{
  "media": {
    "mode": "base64"
  }
}
```

- Encodes media as base64 in webhook payload
- No separate download needed
- Good for small files, increases payload size

**3. Both Mode**

```json
{
  "media": {
    "mode": "both"
  }
}
```

- Provides both URL and base64
- Maximum flexibility, largest payloads

### Event Logger

Track all events to file and/or database:

```json
{
  "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:**

```json
{
  "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

- [WhatsApp Business Platform Documentation](https://developers.facebook.com/docs/whatsapp)
- [Graph API Reference](https://developers.facebook.com/docs/graph-api)
- [System Users Guide](https://www.facebook.com/business/help/503306463479099)
- [WhatsApp Business API Getting Started](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started)

## 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](https://developers.facebook.com/docs/whatsapp/changelog) for updates
