Skill: ivangdavila/whatsapp-business-api (version 1.0.0)

--- name: WhatsApp Business API slug: whatsapp-business-api version: 1.0.0 homepage: https://clawic.com/skills/whatsapp-business-api description: Complete WhatsApp Business Cloud API for messages, templates, media, webhooks, flows, and business profiles. changelog: Initial release with full Cloud API coverage. metadata: {"clawdbot":{"emoji":"💬","requires":{"env":["WHATSAPP_ACCESS_TOKEN","WHATSAPP_PHONE_NUMBER_ID"]},"primaryEnv":"WHATSAPP_ACCESS_TOKEN","os":["linux","darwin","win32"]}} --- # WhatsApp Business API Official Meta Cloud API integration. See auxiliary files for detailed operations. ## Quick Start ```bash curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID/messages" \ -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"messaging_product":"whatsapp","to":"1234567890","type":"text","text":{"body":"Hello!"}}' ``` ## Setup On first use, read `setup.md`. Preferences stored in `~/whatsapp-business-api/memory.md`. ## When to Use Any WhatsApp Business operation: send messages, templates, media, interactive elements, manage webhooks, handle conversations, update business profiles. ## Architecture ``` ~/whatsapp-business-api/ ├── memory.md # Account context + phone numbers ├── templates.md # Approved templates reference └── webhooks.md # Webhook configurations ``` ## Quick Reference | Topic | File | |-------|------| | Setup & memory | `setup.md`, `memory-template.md` | | Messages (text, media, interactive) | `messages.md` | | Templates (create, manage, send) | `templates.md` | | Media (upload, download, manage) | `media.md` | | Webhooks & Events | `webhooks.md` | | Business Profile & Phone Numbers | `business.md` | | Flows (interactive forms) | `flows.md` | | Best practices & limits | `best-practices.md` | ## Core Rules 1. **International format** — Phone numbers without `+` or leading zeros: `1234567890` 2. **24-hour window** — Free replies within 24h of customer message; templates required to initiate 3. **Template approval** — Templates need Meta approval (24-48h); test in sandbox first 4. **Idempotency** — Use `biz_opaque_callback_data` to track message state 5. **Webhook verification** — Always verify webhook signature with app secret 6. **Rate limits** — 80 messages/second per phone number; 1000 template messages/day (tier 1) 7. **Media limits** — Images <5MB, videos <16MB, documents <100MB ## Authentication **Required environment variables:** - `WHATSAPP_ACCESS_TOKEN` — System User access token (permanent) or User access token (60-day) - `WHATSAPP_PHONE_NUMBER_ID` — Your registered phone number ID - `WHATSAPP_BUSINESS_ACCOUNT_ID` — Your WABA ID (for templates) - `WHATSAPP_APP_SECRET` — App secret for webhook verification ```bash curl "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID" \ -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" ``` ### Token Types | Type | Duration | Use Case | |------|----------|----------| | System User Token | Permanent | Production apps | | User Token | 60 days | Development, testing | | Temporary Token | 24 hours | Quick tests | ## Common Traps - Phone format with `+` or `00` → API rejects - Missing `messaging_product: "whatsapp"` → 400 error - Template not approved → message fails silently - Webhook signature not verified → security vulnerability - Sending outside 24h window without template → blocked ## External Endpoints | Endpoint | Purpose | |----------|---------| | `https://graph.facebook.com/v21.0/*` | Cloud API | ## Security & Privacy **Environment variables used:** - `WHATSAPP_ACCESS_TOKEN` — for API authentication - `WHATSAPP_APP_SECRET` — for webhook signature verification **Sent to Meta:** Messages, media, customer phone numbers via graph.facebook.com **Stays local:** Tokens (never logged), ~/whatsapp-business-api/ preferences **Never:** Log message content, skip webhook verification, store tokens in code ## Trust This skill sends data to Meta (facebook.com/whatsapp). ## Related Skills Install with `clawhub install <slug>` if user confirms: - `api` — REST API patterns - `webhook` — Webhook handling - `chat` — Conversational patterns ## Feedback - If useful: `clawhub star whatsapp-business-api` - Stay updated: `clawhub sync`

path: best-practices.md
content: # Best Practices — WhatsApp Business API

## Message Timing

### 24-Hour Conversation Window

```
Customer sends message
        │
        ▼
┌───────────────────────┐
│  24-Hour Free Window  │  ← Reply freely (any message type)
│  (No template needed) │
└───────────────────────┘
        │
        ▼ (24h passed)
┌───────────────────────┐
│   Template Required   │  ← Must use approved template
│   (Costs per message) │
└───────────────────────┘
```

**Best Practice:** Respond within 24 hours to avoid template costs.

---

## Conversation Pricing

### Conversation Types

| Type | Who Initiates | Cost (approx) |
|------|---------------|---------------|
| Service | Customer | Lower |
| Utility | Business (order updates, etc.) | Medium |
| Authentication | Business (OTPs) | Medium |
| Marketing | Business (promos) | Higher |

### Free Entry Points

These conversations are free for 72 hours:
- Click-to-WhatsApp ads
- Facebook Page CTA button
- WhatsApp button on Instagram

---

## Rate Limits

### API Limits

| Limit | Value |
|-------|-------|
| Messages/second | 80 per phone number |
| API calls/second | 100 per app |
| Media upload/day | 500 per phone number |

### Messaging Limits

| Tier | Unique Users/Day |
|------|------------------|
| Unverified | 250 |
| Tier 1 | 1,000 |
| Tier 2 | 10,000 |
| Tier 3 | 100,000 |
| Tier 4 | Unlimited |

**Upgrade Path:** Maintain GREEN quality rating for 7 days → automatic upgrade.

---

## Quality Best Practices

### DO

✅ Respond quickly (within 24h)
✅ Use templates for important updates
✅ Personalize messages with user's name
✅ Provide clear opt-out instructions
✅ Send messages at reasonable hours
✅ Match message to user's language

### DON'T

❌ Send unsolicited marketing
❌ Spam with multiple messages
❌ Use URL shorteners (appears suspicious)
❌ Send same message to many users rapidly
❌ Ignore user replies
❌ Use misleading template content

---

## Template Best Practices

### Naming Convention

```
{purpose}_{version}
order_confirmation_v2
shipping_update_v1
appointment_reminder_v3
```

### Content Guidelines

1. **Be specific** — "Your order #{{1}} shipped" not "Your order shipped"
2. **Include context** — Why are you messaging?
3. **Add value** — Information they actually need
4. **Enable action** — Include relevant links/buttons
5. **Respect time** — Don't wake people up

### Approval Tips

- Use proper grammar and spelling
- Avoid ALL CAPS
- Don't use excessive emojis
- Include your business name
- Be transparent about intent

---

## Webhook Handling

### Recommended Architecture

```
Webhook Request
      │
      ▼
┌─────────────────┐
│  Validate       │  ← Check signature
│  Signature      │
└─────────────────┘
      │
      ▼
┌─────────────────┐
│  Return 200     │  ← Immediately!
│  (sync)         │
└─────────────────┘
      │
      ▼
┌─────────────────┐
│  Queue Message  │  ← Add to job queue
│  (async)        │
└─────────────────┘
      │
      ▼
┌─────────────────┐
│  Process        │  ← Worker processes
│  (background)   │
└─────────────────┘
```

### Idempotency

```javascript
// Store processed message IDs
const processedIds = new Set();

function handleMessage(message) {
  if (processedIds.has(message.id)) {
    return; // Already processed
  }
  
  processedIds.add(message.id);
  // Process message...
}
```

---

## Error Handling

### Retry Strategy

```javascript
async function sendWithRetry(message, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await sendMessage(message);
    } catch (error) {
      if (error.code === 130429) { // Rate limited
        await sleep(Math.pow(2, i) * 1000); // Exponential backoff
        continue;
      }
      throw error; // Non-retryable
    }
  }
  throw new Error('Max retries exceeded');
}
```

### Error Categories

| Code Range | Type | Action |
|------------|------|--------|
| 131xxx | Message errors | Fix request |
| 132xxx | Template errors | Check template |
| 133xxx | Rate/limit errors | Slow down |
| 135xxx | Generic errors | Check docs |

---

## Security Checklist

- [ ] Store tokens in environment variables
- [ ] Verify webhook signatures
- [ ] Use HTTPS for all endpoints
- [ ] Implement rate limiting on your end
- [ ] Log API calls (without sensitive data)
- [ ] Rotate tokens periodically
- [ ] Monitor for unusual activity
- [ ] Have incident response plan

---

## Testing

### Test Numbers

Use Meta-provided test numbers in development:
1. Go to WhatsApp → API Setup in App Dashboard
2. Add test phone numbers
3. Use temporary access token

### Sandbox Limits

- 5 test phone numbers
- Templates work in sandbox
- No charges for test messages

### Pre-launch Checklist

- [ ] Templates approved
- [ ] Webhooks verified and handling all events
- [ ] Error handling tested
- [ ] Rate limiting implemented
- [ ] Quality rating monitored
- [ ] Opt-out flow working
- [ ] Business verification complete

---

## Monitoring

### Key Metrics

| Metric | Target | Action if Below |
|--------|--------|-----------------|
| Delivery rate | >95% | Check phone numbers |
| Read rate | >60% | Improve content |
| Response time | <1h | Add resources |
| Quality rating | GREEN | Review practices |
| Template approval | >80% | Improve content |

### Alerts to Set Up

1. Quality rating drops to YELLOW
2. Delivery rate below 90%
3. Webhook failures
4. Rate limit warnings
5. Template rejections


path: business.md
content: # Business Profile & Phone Numbers — WhatsApp Business API

## Business Profile

### Get Profile

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID/whatsapp_business_profile?fields=about,address,description,email,profile_picture_url,websites,vertical" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

Response:

```json
{
  "data": [
    {
      "about": "Your trusted partner",
      "address": "123 Business St, City",
      "description": "We help businesses grow with innovative solutions.",
      "email": "[REDACTED_SECRET]",
      "profile_picture_url": "https://...",
      "websites": ["https://example.com"],
      "vertical": "RETAIL"
    }
  ]
}
```

### Update Profile

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID/whatsapp_business_profile" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "about": "Your trusted partner since 2020",
    "address": "456 New Address, City",
    "description": "We help businesses grow with innovative AI solutions.",
    "email": "[REDACTED_SECRET]",
    "websites": ["https://example.com", "https://blog.example.com"],
    "vertical": "TECH"
  }'
```

### Business Verticals

| Vertical | Description |
|----------|-------------|
| UNDEFINED | Not specified |
| OTHER | Other category |
| AUTO | Automotive |
| BEAUTY | Beauty & cosmetics |
| APPAREL | Clothing & fashion |
| EDU | Education |
| ENTERTAIN | Entertainment |
| EVENT_PLAN | Event planning |
| FINANCE | Financial services |
| GROCERY | Groceries |
| GOVT | Government |
| HOTEL | Hotels & lodging |
| HEALTH | Healthcare |
| NONPROFIT | Non-profit |
| PROF_SERVICES | Professional services |
| RETAIL | Retail |
| TRAVEL | Travel |
| RESTAURANT | Restaurant & food |
| NOT_A_BIZ | Not a business |

---

## Phone Numbers

### Get Phone Number Details

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID?fields=display_phone_number,verified_name,quality_rating,messaging_limit_tier" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

Response:

```json
{
  "display_phone_number": "+1 234 567 8900",
  "verified_name": "My Business",
  "quality_rating": "GREEN",
  "messaging_limit_tier": "TIER_1K",
  "id": "123456789"
}
```

### List All Phone Numbers

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/phone_numbers" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

---

## Quality Rating

| Rating | Meaning | Action |
|--------|---------|--------|
| GREEN | High quality | Continue as normal |
| YELLOW | Medium quality | Review messaging practices |
| RED | Low quality | Risk of restrictions |

Quality is based on:
- User blocks/reports
- Template quality
- Engagement rates

---

## Messaging Limit Tiers

| Tier | Unique Users/Day | Requirements |
|------|------------------|--------------|
| TIER_1K | 1,000 | New accounts |
| TIER_10K | 10,000 | Good standing for 7 days |
| TIER_100K | 100,000 | Good standing, verified business |
| TIER_UNLIMITED | Unlimited | Consistent high quality |

**Note:** Limits apply to business-initiated conversations only. Replies within 24h window don't count.

---

## Business Verification

### Check Verification Status

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID?fields=account_review_status" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

### Verification Levels

| Level | Benefits |
|-------|----------|
| Unverified | Basic features, limited templates |
| Official Business Account (OBA) | Green checkmark, higher limits |

### Get Verified

1. Complete business profile with accurate info
2. Submit verification documents via Business Manager
3. Wait for Meta review (1-5 business days)

---

## Two-Step Verification

### Enable

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"pin": "123456"}'
```

### Disable

```bash
curl -X DELETE "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

---

## Display Name

### Request Name Change

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID/request_code" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code_method": "SMS",
    "language": "en"
  }'
```

Display name changes require re-verification.

---

## Common Traps

- **Profile pic not updating** — Changes can take 24h to propagate
- **Vertical locked** — Can only change before verification
- **Quality degraded** — Review sent messages for spam-like patterns
- **Tier not increasing** — Need 7+ days of good quality


path: flows.md
content: # Flows — WhatsApp Business API

WhatsApp Flows let you create multi-step interactive forms and experiences directly in WhatsApp.

## Overview

Flows are JSON-based interactive screens that can:
- Collect structured data (forms)
- Display information
- Navigate between screens
- Submit data to your endpoint

---

## Flow Structure

```json
{
  "version": "3.1",
  "screens": [
    {
      "id": "WELCOME",
      "title": "Welcome",
      "layout": {
        "type": "SingleColumnLayout",
        "children": [...]
      }
    }
  ]
}
```

---

## Component Types

### TextHeading

```json
{
  "type": "TextHeading",
  "text": "Welcome to Our Service"
}
```

### TextBody

```json
{
  "type": "TextBody",
  "text": "Please fill out the form below to get started."
}
```

### TextInput

```json
{
  "type": "TextInput",
  "name": "full_name",
  "label": "Full Name",
  "required": true,
  "input-type": "text"
}
```

Input types: `text`, `number`, `email`, `password`, `passcode`, `phone`

### TextArea

```json
{
  "type": "TextArea",
  "name": "comments",
  "label": "Additional Comments",
  "required": false
}
```

### Dropdown

```json
{
  "type": "Dropdown",
  "name": "country",
  "label": "Country",
  "required": true,
  "data-source": [
    {"id": "us", "title": "United States"},
    {"id": "uk", "title": "United Kingdom"},
    {"id": "ca", "title": "Canada"}
  ]
}
```

### RadioButtonsGroup

```json
{
  "type": "RadioButtonsGroup",
  "name": "plan",
  "label": "Select Plan",
  "required": true,
  "data-source": [
    {"id": "basic", "title": "Basic - $9/mo"},
    {"id": "pro", "title": "Pro - $29/mo"},
    {"id": "enterprise", "title": "Enterprise - Contact us"}
  ]
}
```

### CheckboxGroup

```json
{
  "type": "CheckboxGroup",
  "name": "features",
  "label": "Select Features",
  "required": true,
  "min-selected-items": 1,
  "max-selected-items": 3,
  "data-source": [
    {"id": "analytics", "title": "Analytics"},
    {"id": "support", "title": "Priority Support"},
    {"id": "api", "title": "API Access"}
  ]
}
```

### DatePicker

```json
{
  "type": "DatePicker",
  "name": "appointment_date",
  "label": "Select Date",
  "required": true,
  "min-date": "1704067200000",
  "max-date": "1735689600000"
}
```

### Footer (with navigation)

```json
{
  "type": "Footer",
  "label": "Continue",
  "on-click-action": {
    "name": "navigate",
    "next": {
      "type": "screen",
      "name": "NEXT_SCREEN"
    },
    "payload": {
      "full_name": "${form.full_name}"
    }
  }
}
```

---

## Complete Flow Example

```json
{
  "version": "3.1",
  "screens": [
    {
      "id": "CONTACT_INFO",
      "title": "Contact Information",
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "TextHeading",
            "text": "Let's get your details"
          },
          {
            "type": "TextInput",
            "name": "full_name",
            "label": "Full Name",
            "required": true,
            "input-type": "text"
          },
          {
            "type": "TextInput",
            "name": "email",
            "label": "Email",
            "required": true,
            "input-type": "email"
          },
          {
            "type": "TextInput",
            "name": "phone",
            "label": "Phone Number",
            "required": true,
            "input-type": "phone"
          },
          {
            "type": "Footer",
            "label": "Next",
            "on-click-action": {
              "name": "navigate",
              "next": {
                "type": "screen",
                "name": "PREFERENCES"
              },
              "payload": {
                "full_name": "${form.full_name}",
                "email": "${form.email}",
                "phone": "${form.phone}"
              }
            }
          }
        ]
      }
    },
    {
      "id": "PREFERENCES",
      "title": "Your Preferences",
      "data": {
        "full_name": {"type": "string", "__example__": "John Doe"},
        "email": {"type": "string", "__example__": "[REDACTED_SECRET]"},
        "phone": {"type": "string", "__example__": "+1234567890"}
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "TextHeading",
            "text": "Hello, ${data.full_name}!"
          },
          {
            "type": "Dropdown",
            "name": "plan",
            "label": "Select Plan",
            "required": true,
            "data-source": [
              {"id": "starter", "title": "Starter - Free"},
              {"id": "pro", "title": "Pro - $19/mo"},
              {"id": "business", "title": "Business - $49/mo"}
            ]
          },
          {
            "type": "CheckboxGroup",
            "name": "interests",
            "label": "Interests",
            "required": false,
            "data-source": [
              {"id": "updates", "title": "Product Updates"},
              {"id": "tips", "title": "Tips & Tricks"},
              {"id": "offers", "title": "Special Offers"}
            ]
          },
          {
            "type": "Footer",
            "label": "Submit",
            "on-click-action": {
              "name": "complete",
              "payload": {
                "full_name": "${data.full_name}",
                "email": "${data.email}",
                "phone": "${data.phone}",
                "plan": "${form.plan}",
                "interests": "${form.interests}"
              }
            }
          }
        ]
      }
    }
  ]
}
```

---

## Send Flow Message

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "header": {
      "type": "text",
      "text": "Sign Up"
    },
    "body": {
      "text": "Complete your registration in just 2 steps."
    },
    "footer": {
      "text": "Takes less than 1 minute"
    },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_message_version": "3",
        "flow_token": "unique_flow_instance_token",
        "flow_id": "123456789",
        "flow_cta": "Sign Up Now",
        "flow_action": "navigate",
        "flow_action_payload": {
          "screen": "CONTACT_INFO"
        }
      }
    }
  }
}
```

---

## Receive Flow Response

When user completes a flow, you receive a webhook:

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "type": "interactive",
      "interactive": {
        "type": "nfm_reply",
        "nfm_reply": {
          "response_json": "{\"full_name\":\"John Doe\",\"email\":\"[REDACTED_SECRET]\",\"plan\":\"pro\"}",
          "body": "Sent",
          "name": "flow"
        }
      }
    }
  ]
}
```

---

## Create Flow via API

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/flows" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "signup_flow",
    "categories": ["SIGN_UP"]
  }'
```

Then upload the flow JSON:

```bash
curl -X POST "https://graph.facebook.com/v21.0/{flow_id}/assets" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -F "name=flow.json" \
  -F "file=@flow.json"
```

---

## Flow Categories

| Category | Use Case |
|----------|----------|
| SIGN_UP | Registration, onboarding |
| SIGN_IN | Login, authentication |
| APPOINTMENT_BOOKING | Scheduling |
| LEAD_GENERATION | Sales inquiries |
| CONTACT_US | Support requests |
| CUSTOMER_SUPPORT | Help desk |
| SURVEY | Feedback collection |
| OTHER | General purpose |

---

## Best Practices

1. **Keep it short** — Max 3-5 screens per flow
2. **Use clear labels** — Avoid jargon
3. **Validate on server** — Don't trust client data
4. **Provide feedback** — Confirm submission success
5. **Handle errors** — Show user-friendly messages
6. **Test thoroughly** — Use preview before publishing


path: media.md
content: # Media — WhatsApp Business API

## Supported Media Types

| Type | Formats | Max Size |
|------|---------|----------|
| Image | JPEG, PNG | 5 MB |
| Video | MP4, 3GPP | 16 MB |
| Audio | AAC, MP3, OGG, AMR | 16 MB |
| Document | PDF, DOC, XLS, PPT, TXT | 100 MB |
| Sticker | WEBP | 500 KB (static), 1 MB (animated) |

---

## Upload Media

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_PHONE_NUMBER_ID/media" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -F "file=@/path/to/image.jpg" \
  -F "type=image/jpeg" \
  -F "messaging_product=whatsapp"
```

Response:

```json
{
  "id": "1234567890"
}
```

Use this `id` in message requests.

---

## Get Media URL

Media IDs from webhooks need to be converted to URLs:

```bash
curl "https://graph.facebook.com/v21.0/{media_id}" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

Response:

```json
{
  "messaging_product": "whatsapp",
  "url": "https://lookaside.fbsbx.com/whatsapp_business/...",
  "mime_type": "image/jpeg",
  "sha256": "abc123...",
  "file_size": 1234567,
  "id": "1234567890"
}
```

---

## Download Media

The URL from `Get Media URL` requires authentication:

```bash
curl -L "https://lookaside.fbsbx.com/whatsapp_business/..." \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -o downloaded_file.jpg
```

**Note:** URLs expire after a few days. Download promptly.

---

## Delete Media

```bash
curl -X DELETE "https://graph.facebook.com/v21.0/{media_id}" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

---

## Using Media in Messages

### With Media ID (uploaded)

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "image",
  "image": {
    "id": "1234567890",
    "caption": "Your receipt"
  }
}
```

### With URL (external)

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "image",
  "image": {
    "link": "https://example.com/image.jpg",
    "caption": "Your receipt"
  }
}
```

---

## Media Requirements

### Images
- Recommended: 1024x1024 or smaller
- Aspect ratio: Any, but square displays best
- Formats: JPEG, PNG

### Videos
- Codec: H.264
- Audio: AAC
- Max duration: Unlimited (but keep <1 min for engagement)
- Recommended resolution: 720p

### Audio
- Voice notes: OGG with Opus codec
- Music/podcasts: MP3, AAC
- Max duration: Unlimited

### Documents
- All common office formats supported
- PDF renders preview in chat
- Other formats show icon + filename

### Stickers
- Static: 512x512 WEBP
- Animated: 512x512 WEBP, <500 KB
- Must have transparent background

---

## Common Traps

- **URL not accessible** — Media URLs must be publicly accessible (no auth required)
- **Wrong MIME type** — Match file extension to type parameter
- **File too large** — Compress before upload
- **Expired URL** — Download media from webhooks immediately
- **Missing caption** — Always include caption for context


path: memory-template.md
content: # Memory Template — WhatsApp Business API

Create `~/whatsapp-business-api/memory.md` with this structure:

```markdown
# WhatsApp Business API Memory

## Status
status: ongoing
version: 1.0.0
last: YYYY-MM-DD
integration: pending | done | declined

## Context
<!-- What you know about their WhatsApp setup -->
<!-- Use case: support, notifications, marketing -->
<!-- Business type if mentioned -->
<!-- Volume tier (1-4) if known -->

## Phone Numbers
<!-- Registered phone numbers and their purpose -->
<!-- Format: PHONE_NUMBER_ID: +1234567890 (purpose) -->

## Templates
<!-- Approved templates they use frequently -->
<!-- Format: template_name: description -->

## Webhook
<!-- Webhook URL if configured -->
<!-- Events they subscribe to -->

## Preferences
<!-- Message formatting preferences -->
<!-- Response patterns they've established -->

## Notes
<!-- Internal observations -->
<!-- Common operations they request -->
<!-- Patterns to remember -->

---
*Updated: YYYY-MM-DD*
```

## Status Values

| Value | Meaning | Behavior |
|-------|---------|----------|
| `ongoing` | Still learning | Ask questions when relevant |
| `complete` | Has enough context | Work normally |
| `paused` | User said "not now" | Don't ask, work with what you have |
| `never_ask` | User said stop | Never ask for more context |

## Key Principles

- **No config keys visible** — use natural language
- **Learn from behavior** — if they always use templates, remember that
- **Track templates loosely** — note frequently used ones
- Update `last` on each use


path: messages.md
content: # Messages — WhatsApp Business API

## Base Endpoint

```
POST https://graph.facebook.com/v21.0/{phone_number_id}/messages
Authorization: Bearer {access_token}
Content-Type: application/json
```

All message requests require `"messaging_product": "whatsapp"`.

---

## Text Messages

### Simple Text

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "text",
  "text": {
    "body": "Hello! How can I help you today?"
  }
}
```

### Text with Link Preview

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "text",
  "text": {
    "preview_url": true,
    "body": "Check out our website: https://example.com"
  }
}
```

---

## Media Messages

### Image

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "image",
  "image": {
    "link": "https://example.com/image.jpg",
    "caption": "Product photo"
  }
}
```

Using uploaded media ID:

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "image",
  "image": {
    "id": "media_id_here",
    "caption": "Product photo"
  }
}
```

### Video

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "video",
  "video": {
    "link": "https://example.com/video.mp4",
    "caption": "Product demo"
  }
}
```

### Audio

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "audio",
  "audio": {
    "link": "https://example.com/audio.mp3"
  }
}
```

### Document

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "document",
  "document": {
    "link": "https://example.com/invoice.pdf",
    "filename": "invoice_001.pdf",
    "caption": "Your invoice"
  }
}
```

### Sticker

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "sticker",
  "sticker": {
    "id": "sticker_media_id"
  }
}
```

---

## Location Messages

### Send Location

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "location",
  "location": {
    "latitude": 37.7749,
    "longitude": -122.4194,
    "name": "Our Store",
    "address": "123 Main St, San Francisco, CA"
  }
}
```

### Request Location

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "interactive",
  "interactive": {
    "type": "location_request_message",
    "body": {
      "text": "Please share your delivery location"
    },
    "action": {
      "name": "send_location"
    }
  }
}
```

---

## Contact Messages

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "contacts",
  "contacts": [
    {
      "name": {
        "formatted_name": "John Doe",
        "first_name": "John",
        "last_name": "Doe"
      },
      "phones": [
        {
          "phone": "+1987654321",
          "type": "WORK"
        }
      ],
      "emails": [
        {
          "email": "[REDACTED_SECRET]",
          "type": "WORK"
        }
      ]
    }
  ]
}
```

---

## Interactive Messages

### Reply Buttons (max 3)

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": {
      "type": "text",
      "text": "Confirm Order"
    },
    "body": {
      "text": "Your order #12345 is ready. Would you like to proceed?"
    },
    "footer": {
      "text": "Reply within 24 hours"
    },
    "action": {
      "buttons": [
        {"type": "reply", "reply": {"id": "confirm", "title": "✅ Confirm"}},
        {"type": "reply", "reply": {"id": "cancel", "title": "❌ Cancel"}},
        {"type": "reply", "reply": {"id": "modify", "title": "✏️ Modify"}}
      ]
    }
  }
}
```

### List Message (max 10 items per section, max 10 sections)

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": {
      "type": "text",
      "text": "Our Menu"
    },
    "body": {
      "text": "Select an item from our menu:"
    },
    "footer": {
      "text": "Prices may vary"
    },
    "action": {
      "button": "View Menu",
      "sections": [
        {
          "title": "Main Courses",
          "rows": [
            {"id": "pizza", "title": "🍕 Pizza", "description": "$12.99"},
            {"id": "burger", "title": "🍔 Burger", "description": "$9.99"},
            {"id": "pasta", "title": "🍝 Pasta", "description": "$11.99"}
          ]
        },
        {
          "title": "Drinks",
          "rows": [
            {"id": "soda", "title": "🥤 Soda", "description": "$2.99"},
            {"id": "juice", "title": "🧃 Juice", "description": "$3.99"}
          ]
        }
      ]
    }
  }
}
```

### CTA URL Button

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "interactive",
  "interactive": {
    "type": "cta_url",
    "header": {
      "type": "text",
      "text": "Track Your Order"
    },
    "body": {
      "text": "Click below to track your order in real-time."
    },
    "action": {
      "name": "cta_url",
      "parameters": {
        "display_text": "Track Order",
        "url": "https://example.com/track/12345"
      }
    }
  }
}
```

---

## Reactions

### Add Reaction

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "reaction",
  "reaction": {
    "message_id": "wamid.xxxxx",
    "emoji": "👍"
  }
}
```

### Remove Reaction

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "reaction",
  "reaction": {
    "message_id": "wamid.xxxxx",
    "emoji": ""
  }
}
```

---

## Reply to Specific Message

Add `context` to any message type:

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "context": {
    "message_id": "wamid.xxxxx"
  },
  "type": "text",
  "text": {
    "body": "This is a reply to your previous message"
  }
}
```

---

## Mark as Read

```json
{
  "messaging_product": "whatsapp",
  "status": "read",
  "message_id": "wamid.xxxxx"
}
```

---

## Response Format

Successful send returns:

```json
{
  "messaging_product": "whatsapp",
  "contacts": [
    {"input": "1234567890", "wa_id": "1234567890"}
  ],
  "messages": [
    {"id": "wamid.HBgLMTIzNDU2Nzg5MBUCABIYFjNFQjBDNUM3..."}
  ]
}
```

---

## Common Errors

| Code | Error | Solution |
|------|-------|----------|
| 131030 | Recipient not on WhatsApp | Verify phone number |
| 131047 | Re-engagement required | Send template message first |
| 131051 | Unsupported message type | Check type field |
| 130429 | Rate limit exceeded | Slow down requests |
| 132000 | Template not found | Verify template name and status |


path: setup.md
content: # Setup — WhatsApp Business API

This file guides initial configuration when the user first uses this skill.

## Integration

Ask how they want to use WhatsApp Business:
- "Are you building customer support, notifications, or marketing campaigns?"
- "Do you have a Meta Business account with WhatsApp access, or starting fresh?"
- "Want me to help whenever WhatsApp comes up, or only when you ask?"

## Understand Their Use Case

- What's their business type? (e-commerce, SaaS, services)
- What messages will they send? (support, order updates, promotions)
- Volume expectations? (affects tier and rate limits)

## Technical Context

- What's their stack? (Node, Python, PHP, etc.)
- Are they using webhooks already?
- Do they need multiple phone numbers?

## Getting Credentials

Guide them through Meta Business setup if needed:

1. **Meta Business Account** at business.facebook.com
2. **WhatsApp Business Account (WABA)** — created in Meta Business Suite
3. **System User** with WhatsApp permissions
4. **Access Token** — generate from System User

Key IDs to collect:
- Phone Number ID (from WhatsApp Manager)
- Business Account ID (from WhatsApp Manager)
- App Secret (from App Dashboard)

## Memory Storage

Store context in `~/whatsapp-business-api/memory.md`:
- Their primary use case (support, notifications, marketing)
- Business type if mentioned
- Phone numbers registered
- Template categories they use
- Webhook URL if configured

## When Ready

Once you know:
1. What they're building (support, notifications, etc.)
2. Basic credentials are set up

...you're ready to help. Details emerge naturally through use.


path: templates.md
content: # Templates — WhatsApp Business API

Templates are required to initiate conversations outside the 24-hour window. Must be approved by Meta.

## List Templates

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/message_templates" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

With filters:

```bash
curl "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/message_templates?status=APPROVED&limit=20" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

---

## Create Template

```bash
curl -X POST "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/message_templates" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order_confirmation",
    "language": "en_US",
    "category": "UTILITY",
    "components": [
      {
        "type": "HEADER",
        "format": "TEXT",
        "text": "Order Confirmed! 🎉"
      },
      {
        "type": "BODY",
        "text": "Hi {{1}}, your order #{{2}} has been confirmed.\n\nTotal: ${{3}}\nEstimated delivery: {{4}}\n\nThank you for shopping with us!"
      },
      {
        "type": "FOOTER",
        "text": "Reply HELP for support"
      },
      {
        "type": "BUTTONS",
        "buttons": [
          {
            "type": "URL",
            "text": "Track Order",
            "url": "https://example.com/track/{{1}}"
          }
        ]
      }
    ]
  }'
```

### Template Categories

| Category | Use Case | Approval Time |
|----------|----------|---------------|
| `UTILITY` | Order updates, receipts, shipping | Fastest (~24h) |
| `AUTHENTICATION` | OTPs, verification codes | Fast (~24h) |
| `MARKETING` | Promotions, offers, newsletters | Slower (~48h+) |

### Component Types

| Type | Required | Description |
|------|----------|-------------|
| HEADER | No | Text, image, video, or document |
| BODY | Yes | Main message content |
| FOOTER | No | Small text at bottom |
| BUTTONS | No | CTA or quick reply buttons |

---

## Header Formats

### Text Header

```json
{
  "type": "HEADER",
  "format": "TEXT",
  "text": "Your Order Status"
}
```

### Image Header

```json
{
  "type": "HEADER",
  "format": "IMAGE",
  "example": {
    "header_handle": ["https://example.com/sample.jpg"]
  }
}
```

### Video Header

```json
{
  "type": "HEADER",
  "format": "VIDEO",
  "example": {
    "header_handle": ["https://example.com/sample.mp4"]
  }
}
```

### Document Header

```json
{
  "type": "HEADER",
  "format": "DOCUMENT",
  "example": {
    "header_handle": ["https://example.com/sample.pdf"]
  }
}
```

---

## Button Types

### URL Button (max 2)

```json
{
  "type": "URL",
  "text": "Visit Website",
  "url": "https://example.com/page/{{1}}"
}
```

### Phone Button (max 1)

```json
{
  "type": "PHONE_NUMBER",
  "text": "Call Us",
  "phone_number": "+1234567890"
}
```

### Quick Reply (max 3)

```json
{
  "type": "QUICK_REPLY",
  "text": "Confirm"
}
```

### Copy Code (for OTP)

```json
{
  "type": "COPY_CODE",
  "example": "123456"
}
```

---

## Send Template Message

### Simple (no variables)

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "template",
  "template": {
    "name": "hello_world",
    "language": {
      "code": "en_US"
    }
  }
}
```

### With Variables

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "template",
  "template": {
    "name": "order_confirmation",
    "language": {
      "code": "en_US"
    },
    "components": [
      {
        "type": "body",
        "parameters": [
          {"type": "text", "text": "John"},
          {"type": "text", "text": "ORD-12345"},
          {"type": "text", "text": "99.99"},
          {"type": "text", "text": "Feb 28, 2026"}
        ]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": "0",
        "parameters": [
          {"type": "text", "text": "ORD-12345"}
        ]
      }
    ]
  }
}
```

### With Media Header

```json
{
  "messaging_product": "whatsapp",
  "to": "1234567890",
  "type": "template",
  "template": {
    "name": "shipping_update",
    "language": {
      "code": "en_US"
    },
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "image",
            "image": {
              "link": "https://example.com/tracking-map.jpg"
            }
          }
        ]
      },
      {
        "type": "body",
        "parameters": [
          {"type": "text", "text": "John"},
          {"type": "text", "text": "ORD-12345"}
        ]
      }
    ]
  }
}
```

---

## Delete Template

```bash
curl -X DELETE "https://graph.facebook.com/v21.0/$WHATSAPP_BUSINESS_ACCOUNT_ID/message_templates?name=template_name" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
```

---

## Template Status

| Status | Meaning |
|--------|---------|
| APPROVED | Ready to use |
| PENDING | Under review |
| REJECTED | Failed review (see rejection reason) |
| PAUSED | Temporarily disabled by Meta |
| DISABLED | Permanently disabled |

---

## Best Practices

1. **Use UTILITY for transactional** — faster approval
2. **Avoid generic names** — use descriptive names like `order_shipped_v2`
3. **Test in sandbox first** — use test phone numbers
4. **Include opt-out** — especially for marketing
5. **Localize** — create templates for each language you support

---

## Common Rejection Reasons

| Reason | Fix |
|--------|-----|
| Variable only in message | Add fixed text around variables |
| Missing example | Provide sample values for review |
| Promotional in UTILITY | Change to MARKETING category |
| URL shortener | Use full URLs |
| Misleading content | Be clear about business identity |


path: webhooks.md
content: # Webhooks — WhatsApp Business API

Webhooks notify your server of incoming messages, status updates, and other events.

## Setup Webhook

### 1. Configure in Meta App Dashboard

1. Go to your app in developers.facebook.com
2. WhatsApp → Configuration
3. Add webhook URL and verify token

### 2. Handle Verification

Meta sends GET request to verify your endpoint:

```
GET /webhook?hub.mode=subscribe&hub.verify_token=YOUR_VERIFY_TOKEN&hub.challenge=CHALLENGE_STRING
```

Your server must:
1. Verify `hub.verify_token` matches your token
2. Return `hub.challenge` as plain text

```javascript
app.get('/webhook', (req, res) => {
  const mode = req.query['hub.mode'];
  const [REDACTED_SECRET]'hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN) {
    res.status(200).send(challenge);
  } else {
    res.sendStatus(403);
  }
});
```

### 3. Subscribe to Events

Select webhook fields in App Dashboard:
- `messages` — Incoming messages
- `message_template_status_update` — Template status changes
- `account_update` — Business account changes

---

## Verify Webhook Signature

**Always verify the signature to prevent fake events.**

Signature is in `X-Hub-Signature-256` header.

```javascript
const crypto = require('crypto');

function verifySignature(req) {
  const signature = req.headers['x-hub-signature-256'];
  const body = JSON.stringify(req.body);
  
  const expectedSignature = 'sha256=' + 
    crypto.createHmac('sha256', process.env.APP_SECRET)
      .update(body)
      .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
```

```python
import hmac
import hashlib

def verify_signature(payload, signature, app_secret):
    expected = 'sha256=' + hmac.new(
        app_secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
```

---

## Webhook Payload Structure

```json
{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
      "changes": [
        {
          "value": {
            "messaging_product": "whatsapp",
            "metadata": {
              "display_phone_number": "1234567890",
              "phone_number_id": "PHONE_NUMBER_ID"
            },
            "contacts": [...],
            "messages": [...],
            "statuses": [...]
          },
          "field": "messages"
        }
      ]
    }
  ]
}
```

---

## Message Events

### Text Message

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "timestamp": "1234567890",
      "type": "text",
      "text": {
        "body": "Hello!"
      }
    }
  ],
  "contacts": [
    {
      "profile": {"name": "John Doe"},
      "wa_id": "1234567890"
    }
  ]
}
```

### Image Message

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "timestamp": "1234567890",
      "type": "image",
      "image": {
        "caption": "Check this out",
        "mime_type": "image/jpeg",
        "sha256": "abc123...",
        "id": "MEDIA_ID"
      }
    }
  ]
}
```

### Button Reply

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "timestamp": "1234567890",
      "type": "interactive",
      "interactive": {
        "type": "button_reply",
        "button_reply": {
          "id": "confirm",
          "title": "✅ Confirm"
        }
      }
    }
  ]
}
```

### List Reply

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "timestamp": "1234567890",
      "type": "interactive",
      "interactive": {
        "type": "list_reply",
        "list_reply": {
          "id": "pizza",
          "title": "🍕 Pizza",
          "description": "$12.99"
        }
      }
    }
  ]
}
```

### Location Shared

```json
{
  "messages": [
    {
      "from": "1234567890",
      "id": "wamid.xxxxx",
      "timestamp": "1234567890",
      "type": "location",
      "location": {
        "latitude": 37.7749,
        "longitude": -122.4194,
        "name": "Home",
        "address": "123 Main St"
      }
    }
  ]
}
```

---

## Status Updates

Track message delivery:

```json
{
  "statuses": [
    {
      "id": "wamid.xxxxx",
      "status": "delivered",
      "timestamp": "1234567890",
      "recipient_id": "1234567890"
    }
  ]
}
```

### Status Values

| Status | Meaning |
|--------|---------|
| `sent` | Message sent to WhatsApp servers |
| `delivered` | Delivered to recipient's device |
| `read` | Recipient opened the message |
| `failed` | Delivery failed (see errors) |

### Failed Status

```json
{
  "statuses": [
    {
      "id": "wamid.xxxxx",
      "status": "failed",
      "timestamp": "1234567890",
      "recipient_id": "1234567890",
      "errors": [
        {
          "code": 131047,
          "title": "Re-engagement message",
          "message": "More than 24 hours have passed..."
        }
      ]
    }
  ]
}
```

---

## Best Practices

1. **Respond with 200 immediately** — Process asynchronously
2. **Deduplicate by message ID** — Webhooks may retry
3. **Store message IDs** — For reply threading
4. **Download media fast** — URLs expire
5. **Log failed deliveries** — Monitor error patterns
6. **Handle unknown types gracefully** — Meta adds new types

---

## Webhook Retry Behavior

If your server doesn't return 200:
- Retries for up to 24 hours
- Exponential backoff
- After 24h, message is dropped

**Always return 200 fast, then process.**


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)