Token导航 LogoToken导航

Agent Skill · 开发

whatsapp-cloud-api-reference(WhatsApp Cloud API reference 问题管理)

用于辅助 API 设计、接口文档、请求响应结构和服务集成说明。它适合让 Agent 梳理 endpoint、生成 OpenAPI 草稿、检查字段命名、整理错误码或辅助前后端联调。使用时需要确认真实业务语义、鉴权方式、分页和错误处理规则;涉及生成接口文档时,应避免凭空补字段,最好从现有代码、schema 或接口样例中提取事实。

whatsapp-cloud-api-reference 是开发类 Agent Skill,可用宿主为 OpenClaw,通过ClawHub获取,权限为接触敏感数据;许可证为 MIT-0,第三方扫描有提醒项,按安装量在开发类的 21,746 个 Skill 里排第 3,079。

总安装 12,854周安装 520GitHub Stars 公开资料未说明下载 4,035核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT-0
版本
1.0.0

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:whatsapp-cloud-api-reference(WhatsApp Cloud API reference 问题管理)
来源仓库:https://github.com/romanbaz/whatsapp-cloud-api-reference
安装命令:
openclaw skills install whatsapp-cloud-api-reference
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install whatsapp-cloud-api-reference

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

用于辅助 API 设计、接口文档和请求响应结构梳理。whatsapp-cloud-api-reference 属于开发类 Skill,可作为该场景下的辅助能力补充。适用于 Meta Cloud API 实施和故障排查的开发场景。支持诊断消息未送达、模板拒绝等问题,整理错误码规则。

使用时需确认真实业务语义和鉴权方式,避免凭空补字段。建议从现有代码或接口样例中提取事实,确保文档准确性。

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 OpenClaw;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 openclaw skills install whatsapp-cloud-api-reference;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要接触敏感数据,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

whatsapp-cloud-api-reference 怎么安装?

在终端执行 openclaw skills install whatsapp-cloud-api-reference。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

whatsapp-cloud-api-reference 支持哪些 AI 工具?

公开资料标注支持 OpenClaw。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

whatsapp-cloud-api-reference 需要什么权限?

权限级别是接触敏感数据。该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

whatsapp-cloud-api-reference 是免费的吗?

来源标注的许可证是 MIT-0。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

whatsapp-cloud-api-reference 用的人多吗?

公开数据是累计安装 12,854 次,近一周安装 520 次,下载 4,035 次。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

whatsapp-cloud-api-reference 安全吗?

第三方扫描有提醒项,说明存在需要人工确认的行为。安装前请打开源码看清它读了什么、联网到哪里。

值得选的地方和要先想清楚的地方

值得选的地方

  • 专注 OpenClaw 一种宿主,触发方式和使用习惯不用来回切换。
  • 许可证明确为 MIT-0,商用和分发边界好判断。

先想清楚的地方

  • 需要接触敏感数据,装到常用环境前要先评估影响面。
  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 OpenClaw,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 环境里有大量密钥、凭证,不方便给第三方 Skill 读取的人。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
whatsapp-cloud-api-reference

WhatsApp Cloud API reference 问题管理 · 本页

12,854—接触敏感数据MIT-0有提醒

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

name
whatsapp-cloud-api-reference
description
Use when implementing WhatsApp messaging via Meta Cloud API, or diagnosing failures like message not delivered, template rejected, webhook issues, phone not registered, token errors, rate limiting, 24-hour window violations, quality rating drops, or setup mistakes on the WhatsApp Business API.

WhatsApp Messaging via Meta Cloud API

Overview

The Meta WhatsApp Cloud API is the official, fully hosted path for programmatic WhatsApp messaging. No server management needed. First 1,000 service conversations per month are free.

Key rules:

  • Your app can NEVER send a free-form text message first. The very first message to any user must always be a pre-approved template. Free-form text is only unlocked after the user replies, and only within the 24-hour window that reply opens.
  • Business-initiated messages outside a 24h reply window must use a pre-approved template
  • Phone numbers must be registered in your WABA before sending
  • Always use a System User token — user tokens expire in 24 hours

Conversation flow:

App → user:  MUST be a template (always, for first contact)
User → app:  reply opens a 24-hour free-form window
App → user:  free-form text allowed within that 24h window
  [24h passes with no user reply]
App → user:  MUST use a template again to re-engage

Setup Checklist

  1. Create Meta Developer App — developers.facebook.com → Create App → Business type
  2. Add WhatsApp product to the app (gives temp test number + 5 test recipient slots)
  3. Create a permanent System User token:

- Meta Business Manager → Settings → System Users → Create Admin user - Assign permissions: whatsapp_business_messaging + whatsapp_business_management - Generate token — this never expires

  1. Register real phone number — number cannot already be active on personal/business WhatsApp
  2. Set up webhook — needs public HTTPS URL (trusted CA cert, no self-signed), must respond in < 10s

Sending Messages

Text Message (Node.js)

// npm install axios
const axios = require('axios');

async function sendMessage(phoneNumber, text) {
    // phoneNumber: E.164 without +, e.g. "14155551234"
    const res = await axios.post(
        `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_NUMBER_ID}/messages`,
        {
            messaging_product: 'whatsapp',
            recipient_type: 'individual',
            to: phoneNumber,
            type: 'text',
            text: { preview_url: false, body: text }
        },
        { headers: { Authorization: `Bearer ${process.env.WA_ACCESS_TOKEN}` } }
    );
    return res.data;
}

Text Message (Python)

# pip install requests
import requests, os

def send_message(phone: str, text: str) -> dict:
    r = requests.post(
        f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_NUMBER_ID']}/messages",
        headers={"Authorization": f"Bearer {os.environ['WA_ACCESS_TOKEN']}"},
        json={
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": phone,        # E.164 without +
            "type": "text",
            "text": {"preview_url": False, "body": text}
        }
    )
    r.raise_for_status()
    return r.json()

Quick test via curl

curl -X POST "https://graph.facebook.com/v21.0/YOUR_PHONE_ID/messages" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"messaging_product":"whatsapp","to":"14155551234","type":"text","text":{"body":"Hello"}}'

Template Message (required when > 24h since last user reply)

const payload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'template',
    template: {
        name: 'hello_world',           // your approved template name
        language: { code: 'en_US' },
        components: [{
            type: 'body',
            parameters: [
                { type: 'text', text: 'John' },        // fills {{1}}
                { type: 'text', text: 'Order #4521' }  // fills {{2}}
            ]
        }]
    }
};

Media Messages (Image, Document, Audio, Video)

Image:

const payload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'image',
    image: {
        link: 'https://your-domain.com/image.jpg'  // must be publicly accessible HTTPS
    }
};

Document:

const payload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'document',
    document: {
        link: 'https://your-domain.com/file.pdf',
        caption: 'Invoice'  // optional
    }
};

Audio:

const payload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'audio',
    audio: {
        link: 'https://your-domain.com/audio.mp3'
    }
};

Video:

const payload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'video',
    video: {
        link: 'https://your-domain.com/video.mp4',
        caption: 'Demo video'  // optional
    }
};

Important constraints:

  • All media URLs must be publicly accessible HTTPS (http:// fails)
  • Max file sizes: Image 16MB, Document 100MB, Audio 16MB, Video 16MB
  • Supported formats: Images (JPEG, PNG), Documents (PDF), Audio (AAC, MP3, OGG, WAV), Video (MP4, 3GPP)
  • Media must not require authentication
  • URLs cannot use shorteners (bit.ly, tinyurl, etc.)

Webhook Handler (Express) — Correct Async Pattern

// GET — Meta calls this to verify your endpoint
app.get('/webhook', (req, res) => {
    const { 'hub.mode': mode, 'hub.verify_token': token, 'hub.challenge': challenge } = req.query;
    if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN)
        return res.status(200).send(challenge);  // raw string only — NOT JSON
    res.sendStatus(403);
});

// POST — CRITICAL: return 200 IMMEDIATELY, process async
app.post('/webhook', express.json(), (req, res) => {
    // Return 200 immediately so Meta doesn't retry
    res.sendStatus(200);

    // Process webhook payload asynchronously (don't block)
    setImmediate(() => {
        processWebhookAsync(req.body).catch(err => {
            logger.error(`Webhook processing failed: ${err.message}`);
        });
    });
});

async function processWebhookAsync(body) {
    body.entry?.forEach(entry =>
        entry.changes?.forEach(change => {
            const value = change.value;

            // Incoming messages
            if (value.messages) {
                value.messages.forEach(msg => {
                    console.log(`Message from ${msg.from}: ${msg.text?.body}`);
                    handleMessage(msg);
                });
            }

            // Delivery status
            if (value.statuses) {
                value.statuses.forEach(status => {
                    console.log(`Message ${status.id} status: ${status.status}`);
                    handleDeliveryStatus(status);
                });
            }
        })
    );
}

Message Status & Delivery Tracking

Webhook Payload: Delivery Status

Meta sends status updates via webhook when a message is delivered, read, or fails:

{
  "object": "whatsapp_business_account",
  "entry": [{
    "changes": [{
      "value": {
        "statuses": [{
          "id": "wamid.xxx",           // Message ID from your send response
          "status": "delivered",       // "sent" | "delivered" | "read" | "failed"
          "timestamp": "1675262308",
          "recipient_id": "14155551234",
          "type": "message"
        }]
      }
    }]
  }]
}

Status values:

  • sent — Message reached Meta servers
  • delivered — Message delivered to user's device
  • read — User opened the message
  • failed — Delivery failed (permanent)

Tracking Message Delivery

// When you send, store the message ID
const sendResult = await sendMessage(phone, text);
const messageId = sendResult.messages[0].id;

// Log it for webhook tracking
db.messages.insert({
    message_id: messageId,
    recipient: phone,
    sent_at: Date.now(),
    status: 'sent',
    body: text
});

// When webhook arrives with status update, match by message_id
function handleDeliveryStatus(statusUpdate) {
    const { id, status, recipient_id } = statusUpdate;

    // Update your database
    db.messages.updateOne(
        { message_id: id },
        { status: status, updated_at: Date.now() }
    );

    // Handle delivery failures
    if (status === 'failed') {
        logger.error(`Message ${id} failed to deliver to ${recipient_id}`);
        // Retry logic here
    }
}

WABA Quality Rating & Account Health

What is Quality Rating?

Your WhatsApp Business Account (WABA) has a quality rating that affects your sending ability:

RatingImpactRecovery
GREENFull functionality, no restrictionsMaintain this (stay green)
YELLOWSlight rate limit reduction, monitor closelyImprove within 7 days or drops to RED
REDSevere restrictions, may lose messaging accessContact Meta Support

How to Check Quality Rating

curl "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID?fields=quality_rating" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Response: { "quality_rating": "GREEN" }

What Causes Quality Rating to Drop?

  • High bounce rate — sending to invalid/inactive numbers
  • Spam reports — users marking your messages as spam
  • High failure rate — messages consistently failing to deliver
  • User blocks — users blocking your number after messages
  • Policy violations — sending prohibited content

How to Improve Quality Rating

  1. Validate phone numbers before sending — use the WhatsApp contacts check (error 131026)
  2. Only message opted-in users — don't send unsolicited messages
  3. Keep template content transactional — avoid marketing spam
  4. Monitor quality metrics — check rating regularly via API
  5. Respect user preferences — remove users who opt out
  6. Don't retry failed numbers aggressively — wait before retrying same number

Group Messages

Note: WhatsApp Business API does NOT support group messaging directly. You can only send to individual recipients (1:1 conversations).

If you need group functionality:

  • Users must add your business number to a group manually
  • Messages sent to the group are treated as individual 1:1 messages
  • You cannot initiate group conversations programmatically

API Versioning Strategy

All examples use v21.0 (current as of February 2026). Meta deprecates API versions annually.

Checking Your Current Version

# List all available versions
curl "https://graph.facebook.com/versions" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Current version recommendations
# - v21.0 (current, recommended)
# - v20.0 (previous, will deprecate in 6 months)

Version Update Strategy

// Store version in config, not hardcoded
const API_VERSION = process.env.WHATSAPP_API_VERSION || 'v21.0';

const url = `https://graph.facebook.com/${API_VERSION}/${PHONE_NUMBER_ID}/messages`;

// When Meta deprecates a version, update .env:
// WHATSAPP_API_VERSION=v22.0

What Changes Between Versions?

  • New message types or features added
  • Deprecated fields removed
  • Error codes may change slightly
  • Response payload structure may change

Always test before upgrading — make requests against the new version in your dev environment first.


Message Constraints & Limits

Text Message Limits

ConstraintLimit
Text body max length4,096 characters
Link previewEnabled by default, disable with preview_url: false
Carriage returns / newlinesSupported (use `\
`)

Interactive Message Limits

TypeItemsCharacter Limit
Button1-3Title: 20 chars
List1-10Title: 24 chars per row

Media URL Requirements

  • Must be HTTPS only (http:// rejected)
  • Must be publicly accessible (no authentication required)
  • Must NOT use shorteners (bit.ly, tinyurl rejected)
  • Must have correct Content-Type header
  • Max file sizes:

- Image: 16 MB - Audio: 16 MB - Video: 16 MB - Document: 100 MB

Rate Limits (Per WABA)

LimitValue
Default throughput80 messages/second
Burst capacity1,000 messages/second (request increase)
Requests per minute60 API calls/minute

Template Approval Process

Template Submission Workflow

Create template in Meta Business Manager
        ↓
Submit for review (human review by Meta)
        ↓
Status: PENDING (24-72 hours typical)
        ↓
Status: APPROVED (can now use in messages)
    OR
Status: REJECTED (reason provided in dashboard)

How Long Does Approval Take?

  • Typical: 24-72 hours
  • Peak times (weekends, holidays): up to 7 days
  • Fast-track: Available for high-volume WABAs (request in Meta Support)

Common Approval Issues

IssueWhy Rejected
Variable formatMust use {{1}}, {{2}} format
Template starts/ends with variableMust have text before first variable
URL shortenersUse full domain URLs only
Placeholder qualityPlaceholder values must be realistic examples
Sensitive data requestNever ask for SSN, card numbers, passwords
Unclear purposePurpose field must clearly state intent
Warm language in utilityUse formal wording; warmth triggers "marketing" category (costs more)
Duplicate templateName/wording too similar to existing template

Check rejection reason in Meta Business Manager → Business Support → Rejected Template Messages.


Common Setup Mistakes

1. Phone Number Already on Personal WhatsApp

Symptom: Registration fails with error 133010, status stays PENDING

Cause: Phone number is already active on a personal WhatsApp account

Fix:

1. Remove phone from personal WhatsApp (go to Settings → Devices → Remove phone)
2. Wait 24 hours
3. Re-run registration API call

2. Mixing Phone Number ID with WABA ID

Symptom: API returns "Invalid parameters" for phone operations

How to tell them apart:

PHONE_NUMBER_ID: 120######## (11-12 digits, starts with 120)
WABA_ID: ######### (9-10 digits, higher number)

# Correct endpoint
POST /v21.0/PHONE_NUMBER_ID/messages  ✅

# Wrong endpoint
POST /v21.0/WABA_ID/messages  ❌

3. Token Validation Passes but Scopes Missing

Symptom: Token debug shows valid, but messaging fails with error 3/10

# Token is "valid" but missing scopes
curl "https://graph.facebook.com/debug_token?input_token=TOKEN&access_token=TOKEN"
# Response: { "is_valid": true, "scopes": ["manage_pages"] }  ← NO whatsapp_business_messaging

# Fix: Regenerate System User token with correct permissions

4. Webhook Returns JSON Instead of Raw Challenge

Symptom: Webhook verification fails silently in Meta dashboard

Wrong:

return res.json({ challenge });  // ❌ returns JSON

Correct:

return res.status(200).send(challenge);  // ✅ returns raw string

5. WABA Not Subscribed to App

Symptom: Webhooks never arrive (silent failure since 2025 Meta UI change)

Fix:

curl -X POST \
  "https://graph.facebook.com/v21.0/WABA_ID/subscribed_apps" \
  -H "Authorization: Bearer YOUR_SYSTEM_USER_TOKEN"

6. Sending Template Too Early in Approval Process

Symptom: Error 132001 "Template Unavailable"

Cause: Template still in PENDING status, not yet APPROVED

Fix: Check status in Meta Business Manager → Message Templates → wait for APPROVED status

7. Phone Number Not in WABA

Symptom: Error 131009 when trying to send

How to verify:

# Check which phone numbers are in your WABA
curl "https://graph.facebook.com/v21.0/WABA_ID/phone_numbers?fields=id,display_phone_number" \
  -H "Authorization: Bearer YOUR_TOKEN"

8. Sending Free-Form Text as First Message

Symptom: API returns 200, message ID issued, but user never receives it

Root cause: Only templates allowed as first message to any number

Fix: Always use a template for first contact


Phone Number Status Reference

Check the status field via API. Each status blocks different operations:

StatusMeaningAction
PENDINGNumber is registered but not verifiedSet up 2FA (either manual or API), then run register call
REGISTEREDNumber is verified and readyCheck code_verification_status — should be VERIFIED
FLAGGEDAccount or number under review for policy violationContact Meta Support
BANNEDNumber permanently disabledContact Meta Support

Error Code Reference

CodeNameCauseFix
190Token ExpiredUser token (24h lifetime) used in productionSwitch to System User token; debug at developers.facebook.com/tools/debug/accesstoken
3 / 10Permission DeniedToken missing required scopesRegenerate System User token with whatsapp_business_messaging + whatsapp_business_management
100Invalid ParameterMisspelled field or wrong valueCheck request body against API docs; verify phone number format (E.164, no +)
130429Rate Limit (MPS)Exceeded 80 messages/sec defaultAdd send queue + exponential backoff (see below)
13104724h Window Expired> 24h since customer last repliedReplace free-form text with a pre-approved template message
131026UndeliverableRecipient blocked you, no WhatsApp, or outdated appVerify recipient number; confirm they have WhatsApp installed and accepted Meta terms
131048Spam Rate LimitMessages flagged as spamCheck Quality Rating in WhatsApp Manager; review message content and opt-in practices
131056Pair Rate LimitToo many messages to same recipient too fastWait before retrying the same number
131009Invalid Parameter ValuePhone number not in WABA, or wrong parameterVerify number is registered in your WABA under Phone Numbers
131021Same Sender/Recipientfrom and to are the same numberUse a different recipient
131031Account LockedPolicy violation or wrong 2-step PINContact Meta Support
132001Template UnavailableWrong template name, wrong language code, or not yet approvedCheck WhatsApp Manager → Message Templates for exact name, language, and status
133010Phone Not RegisteredSender number not registered in Cloud APIRun the registration API call (see below)
368Policy ViolationAccount restrictedContact Meta Support
1 / 2API Service ErrorMeta outage or server errorCheck metastatus.com; retry with exponential backoff

Fix: Token Problems (Error 190, 3, 10)

Diagnose first:

curl "https://graph.facebook.com/debug_token?input_token=YOUR_TOKEN&access_token=YOUR_APP_ID|YOUR_APP_SECRET"

Check is_valid, expires_at (0 = never expires), and scopes in the response.

Fix — create a non-expiring System User token:

  1. Meta Business Manager → Settings → System Users
  2. Create Admin system user
  3. Add whatsapp_business_messaging + whatsapp_business_management permissions
  4. Generate token — never set an expiry

Fix: Phone Number Not Registered (Error 133010)

Step 1 — check phone number status:

curl "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID?fields=verified_name,code_verification_status,quality_rating,status" \
  -H "Authorization: Bearer YOUR_TOKEN"

If status is PENDING, the number is waiting for verification. Continue below.

Step 2 — set up Two-Step Verification (choose ONE method):

Option A: Manual setup (easy)

  1. Meta Business Manager → WhatsApp Settings → Phone Numbers → Your Number
  2. Click "Two-Step Verification" → set a PIN

Option B: API setup (easier for automation)

# Set 2FA PIN via API
curl -X POST \
  "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID/two_step_verification" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"pin": "123456"}'  # any 6-digit PIN you choose

Step 3 — register the number with the 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": "123456"}'  # same PIN from step 2

Step 4 — wait 5 minutes, then verify registration:

curl "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID?fields=verified_name,code_verification_status,quality_rating,status" \
  -H "Authorization: Bearer YOUR_TOKEN"

Look for status: REGISTERED and code_verification_status: VERIFIED.


Fix: Webhook Not Verifying

Diagnose in this order:

  1. Not returning raw challenge — endpoint must return the hub.challenge string value only, not JSON
  2. Token mismatch — hub.verify_token Meta sends must match exactly what you set in the dashboard (case-sensitive)
  3. SSL issue — Meta requires a valid cert from a trusted CA; self-signed certs are rejected
  4. Timeout — your server must respond within 10 seconds
  5. WABA not subscribed to App — common silent failure since 2025 Meta UI change:
# Subscribe your WABA to your App (run once)
curl -X POST \
  "https://graph.facebook.com/v21.0/WABA_ID/subscribed_apps" \
  -H "Authorization: Bearer YOUR_SYSTEM_USER_TOKEN"

# Verify the subscription exists
curl "https://graph.facebook.com/v21.0/WABA_ID/subscribed_apps" \
  -H "Authorization: Bearer YOUR_SYSTEM_USER_TOKEN"

Local development — expose localhost with a tunnel:

# Using ngrok
ngrok http 3000
# Use the https:// URL ngrok provides as your webhook callback URL in Meta

Fix: Rate Limiting (Error 130429)

Default limit is 80 messages per second. Fix with a queue and exponential backoff:

// npm install limiter
const { RateLimiter } = require('limiter');
const limiter = new RateLimiter({ tokensPerInterval: 70, interval: 'second' });

async function sendWithRetry(phone, message, attempt = 0) {
    await limiter.removeTokens(1);
    try {
        return await sendMessage(phone, message);
    } catch (err) {
        const code = err.response?.data?.error?.code;
        if (code === 130429 && attempt < 5) {
            const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s, 16s
            await new Promise(r => setTimeout(r, delay));
            return sendWithRetry(phone, message, attempt + 1);
        }
        throw err;
    }
}

To increase throughput beyond 80 MPS, apply in Meta Business Manager → WhatsApp → Phone Numbers → Request Increased Messaging Limit.


Fix: 24-Hour Window (Error 131047)

You cannot send free-form text to a user more than 24 hours after their last message. You must use a template.

// Instead of free-form text, send an approved template
await sendTemplate(phoneNumber, 'order_update', 'en_US', [
    { type: 'text', text: 'John' },
    { type: 'text', text: '#4521' }
]);

Create and submit templates at: Meta Business Manager → WhatsApp → Message Templates.


Fix: Template Rejected (Error 132001 or rejection in WhatsApp Manager)

Rejection ReasonFix
Variable format wrongUse {{1}}, {{2}} — double curly braces, sequential integers only
Template starts/ends with variableAdd plain text before {{1}} and after the last variable
Variables not sequentialMust be {{1}}, {{2}} — no gaps allowed
URL shorteners usedUse full, unshortened URLs to your own domain
Language code mismatchMatch language.code to the actual content language, e.g. en_US, pt_BR
Warm language in utility templateUse formal transactional wording; warm language causes auto-reclassification to marketing category
Sensitive dataNever request SSNs, full card numbers, or passwords
Duplicate of existing templateChange the wording — even minor variation is required
Purpose unclearEach variable must have a descriptive example value in the template submission

Where to find rejection reason: Meta Business Manager → Business Support Home → Your WhatsApp Account → Rejected Template Messages → view policy issue.


Fix: Message Undeliverable (Error 131026)

Run this to check if a number has WhatsApp before sending:

curl "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID/contacts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{"messaging_product": "whatsapp", "contacts": ["+14155551234"]}'
# Response includes "wa_id" if the number has WhatsApp, empty if not

Diagnose: "My message sends but the user never receives it" (first-contact trap)

This is the most common silent failure. The API returns success (messages[0].id) but the user receives nothing.

Root cause: You sent a free-form text message as the first outreach. Meta silently drops it.

How to tell: Check the message status webhook — the message will show failed with error 131047 or show sent but never delivered.

Rule: The very first message your app sends to any number must be a template. No exceptions.

❌ Wrong — app sends free-form text first:
   POST /messages → type: "text", body: "Hello John, your order is ready"
   → API may return 200 but message is silently dropped or returns 131047

✅ Correct — app sends template first:
   POST /messages → type: "template", name: "order_ready"
   → User receives the message and can reply
   → After user replies, free-form text is allowed for 24h

Fix: Create and approve a template for every type of first-contact message you need to send. Submit templates at Meta Business Manager → WhatsApp → Message Templates.



Practical Patterns from Production

Phone Number Validation Before Sending

Always validate phone number format and WhatsApp registration before sending:

def is_valid_phone_format(phone_digits: str) -> bool:
    """
    E.164 format validation:
      - 7-15 digits (not including +)
      - Not all same digit (e.g., 0000000 is invalid)
    """
    if not phone_digits or len(phone_digits) < 7 or len(phone_digits) > 15:
        return False
    if len(set(phone_digits)) == 1:  # all same digit
        return False
    return True

def is_registered_on_whatsapp(phone_digits: str, token: str, phone_id: str) -> bool:
    """
    Check if number is a registered WhatsApp user.
    Returns False only on definitive error 131026 (not on WhatsApp).
    Returns True if successful, uncertain (auth error, etc.), or timeout.
    """
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    data = {
        "messaging_product": "whatsapp",
        "to": phone_digits,
        "type": "text",
        "text": {"body": "_"}  # minimal text to check
    }
    try:
        r = requests.post(
            f"https://graph.facebook.com/v21.0/{phone_id}/messages",
            headers=headers,
            json=data,
            timeout=10
        )
        if r.status_code == 200:
            return True  # number is valid

        error_code = r.json().get("error", {}).get("code")
        if error_code == 131026:
            return False  # NOT on WhatsApp

        return True  # other errors — don't block
    except:
        return True  # network error — don't block

Structured Error Response Handling

def extract_error_code(response_json: dict) -> int:
    """Extract error code from Meta API response."""
    return (
        response_json.get("error", {}).get("code")
        or response_json.get("error", {}).get("error_subcode")
    )

def send_with_error_handling(phone_id: str, recipient: str, message_body: str, token: str):
    """Send message and extract detailed error info."""
    url = f"https://graph.facebook.com/v21.0/{phone_id}/messages"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    data = {
        "messaging_product": "whatsapp",
        "to": recipient,
        "type": "text",
        "text": {"body": message_body}
    }

    try:
        r = requests.post(url, headers=headers, json=data)
        r.raise_for_status()
        return {"success": True, "message_id": r.json().get("messages")[0].get("id")}
    except requests.HTTPError as e:
        error_code = extract_error_code(e.response.json())
        error_msg = e.response.json().get("error", {}).get("message")
        return {
            "success": False,
            "error_code": error_code,
            "error_message": error_msg,
            "response_text": e.response.text
        }

Interactive Messages (Smart Buttons vs List)

def send_interactive_message(phone_id: str, recipient: str, text: str, options: list, token: str):
    """
    Intelligently sends:
      - Buttons (up to 3 options)
      - List Menu (4-10 options)

    Each option: {"id": "unique_id", "title": "Text (max 20 chars)"}
    """
    url = f"https://graph.facebook.com/v21.0/{phone_id}/messages"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    if len(options) <= 3:
        # Send as buttons
        button_data = {
            "messaging_product": "whatsapp",
            "to": recipient,
            "type": "interactive",
            "interactive": {
                "type": "button",
                "body": {"text": text},
                "action": {
                    "buttons": [
                        {
                            "type": "reply",
                            "reply": {"id": opt["id"], "title": opt["title"][:20]}
                        }
                        for opt in options
                    ]
                }
            }
        }
        return requests.post(url, headers=headers, json=button_data)
    else:
        # Send as list menu
        list_data = {
            "messaging_product": "whatsapp",
            "to": recipient,
            "type": "interactive",
            "interactive": {
                "type": "list",
                "body": {"text": text},
                "action": {
                    "button": "See options",
                    "sections": [{
                        "title": "Available options",
                        "rows": [
                            {
                                "id": opt["id"],
                                "title": opt["title"][:24]
                            }
                            for opt in options
                        ]
                    }]
                }
            }
        }
        return requests.post(url, headers=headers, json=list_data)

Testing & Debugging Helper Scripts

Token & Phone Verification Script:

# Check if token is valid and phone number is accessible
import requests, os
from dotenv import load_dotenv
load_dotenv()

TOKEN = os.getenv('WHATSAPP_TOKEN')
PHONE_ID = os.getenv('PHONE_NUMBER_ID')

# Debug token
r = requests.get(f"https://graph.facebook.com/debug_token?input_token={TOKEN}&access_token={TOKEN}")
data = r.json()
if 'error' in data:
    print(f"❌ Token Error: {data['error']['message']}")
else:
    print(f"✅ Token Valid")
    print(f"   Expires: {data['data'].get('expires_at')} (0=never)")
    print(f"   Scopes: {data['data'].get('scopes')}")

# Check phone number
r = requests.get(
    f"https://graph.facebook.com/v21.0/{PHONE_ID}",
    headers={"Authorization": f"Bearer {TOKEN}"}
)
if r.status_code == 200:
    print(f"✅ Phone Number Accessible")
    print(f"   Display: {r.json().get('display_phone_number')}")
    print(f"   Quality Rating: {r.json().get('quality_rating')}")
else:
    print(f"❌ Phone Error: {r.json().get('error', {}).get('message')}")

Quick Debug Checklist

When a message fails, check in this order:

  1. Token valid? — curl "https://graph.facebook.com/debug_token?input_token=TOKEN&access_token=APP_ID|APP_SECRET"
  2. Phone number format? — E.164, no +, no spaces: "14155551234"
  3. Number registered in WABA? — check WhatsApp Manager → Phone Numbers
  4. Number registered with Cloud API? — run registration call if error 133010
  5. 24h window? — if > 24h since last user reply, send a template instead
  6. Template approved? — WhatsApp Manager → Message Templates → check status and rejection reason
  7. Webhook subscribed? — verify WABA → App subscription via GET /WABA_ID/subscribed_apps
  8. Rate limited? — check Quality Rating in WhatsApp Manager; implement backoff + queue

平台分布

OpenClaw

92.57%
按下载量换算3,735

安全审计

VirusTotal

通过

ClawScan

可疑

Static analysis

未展示

权限和风险

接触敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills