WhatsApp Business Cloud API 设置

本页的 Python 依赖命令基于一份 PM 准备好的源码 checkout。依赖变更后,重新激活 checkout 并重启 Hermes。

Hermes 可以通过 Meta 官方的 WhatsApp Business Cloud API 接入 WhatsApp。这是生产级路径:无 Node.js 桥接子进程、无二维码、无封号风险。

代价是:

  • 你需要一个 Meta Business 账号(不是个人 WhatsApp)。
  • 机器人运行在一个专用业务电话号码上,不是你的个人号码。
  • Hermes 网关需要一个公网 HTTPS URL,以便 Meta 通过 webhook 投递入站消息。
  • 距用户上一条消息超过 24 小时的回复需要预先审批的模板(这是 Meta 的"客服窗口"规则,不是 Hermes 限制)。

如果这些约束不适合你的场景,Baileys 桥接集成 是替代方案——个人账号、无需公网 URL,但非官方、易封号。

该用哪个?
  • Cloud API(本指南)——跑真正的业务机器人,要稳定性,能接受 Meta 审核 + 模板文书
  • Baileys 桥接——个人项目、快速演示、单用户设置,愿意冒机器人手机号账号的风险

快速上手

hermes whatsapp-cloud

向导会带你过每一个凭据,在你粘贴时逐个校验(抓住头号设置陷阱——把电话号码粘进 Phone Number ID 字段),并为需要在向导外完成的步骤(启动 cloudflared、配置 Meta 的 webhook 仪表盘)打印确切的后续指引。

本页其余部分是手动参考。


前置条件

  1. 一个 Meta Business 账号。在 business.facebook.com 创建。
  2. 一个启用了 WhatsApp 的 Meta 应用。见下文"创建 Meta 应用"。
  3. 一种把本地端口暴露到公网并带 HTTPS 的方式。推荐 Cloudflare Tunnel(`cloudflared)——免费、无需端口转发、无需域名。ngrok、你自己带反向代理 + TLS 的域名,或直接把网关绑到公网 IP 的 VPS 也行。
  4. 可选但推荐:PATH 上有 ffmpeg,这样出站语音消息渲染为原生 WhatsApp 语音气泡(绿色波形),而非 MP3 音频附件。缺失时 Hermes 优雅降级。

创建 Meta 应用

  1. 前往 developers.facebook.com/apps → Create App。
  2. 选择用例:"Connect with customers through WhatsApp" → Next。
  3. 选择或创建一个业务作品集。查看发布要求。确认 → Create app。
  4. 创建后你会落到 Customize use case → Connect on WhatsApp → Quickstart。点击 Start using the API——你现在在 API Setup 页。
  5. 确认已链接一个 WhatsApp Business Account(WABA)。如果你在第 3 步新建了作品集,会自动创建一个。在 API Setup 页核验。

你需要从仪表盘拿到这些值——向导按此顺序提示:

值仪表盘位置字段形态说明
Phone Number IDApp Dashboard → WhatsApp → API Setup → "From" 下拉下方数字,15-17 位不是电话号码本身。头号设置错误就是把真实电话号码粘到这里。
Access TokenApp Dashboard → WhatsApp → API Setup → "Generate access token"以 EAA 开头,100+ 字符临时 token 24 小时有效——生产环境见下文"永久 token"。
App SecretApp Dashboard → Settings → Basic → App secret 旁点 "Show"32 字符小写 hex用于校验入站 webhook 签名。没有它,入站投递以 503 拒绝。
App ID(可选)App Dashboard → Settings → Basic数字,15-16 位发消息不要求,便于分析。
WABA ID(可选)App Dashboard → WhatsApp → API Setup → 顶部附近数字,15+ 位发消息不要求,便于分析。

永久 token(生产)

临时 access token 24 小时后过期,意味着今天生成的 token 明天就失效。生产部署请用系统用户永久 token:

  1. 前往 business.facebook.com/latest/settings → System users(左侧边栏)。
  2. Add → 名字(如 hermes-bot)→ 角色:Admin。
  3. 选中新用户 → Assign Assets:
    • 选你的应用 → 在 Full control 下打开 Manage app。
    • 选你的 WhatsApp 账号 → 在 Full control 下打开 Manage WhatsApp Business Accounts。
    • 点 Assign assets。
  4. 用以下权限生成 token:
    • business_management
    • whatsapp_business_messaging
    • whatsapp_business_management
  5. 设 token 过期:Never。
  6. 复制 token → 更新 ~/.hermes/.env 中的 WHATSAPP_CLOUD_ACCESS_TOKEN → 重启网关。

系统用户 token 不过期,除非你显式吊销。


把 Hermes 暴露到公网

Cloud API 通过 HTTPS POST 把入站消息投递到你的 webhook URL——这意味着 Hermes 网关必须能被 Meta 的服务器访问。三种常见方式:

Cloudflare Tunnel(推荐)

免费、无需端口转发,Windows / macOS / Linux 通用。作为独立进程与网关并行运行。

安装:

# Windows
winget install Cloudflare.cloudflared

# macOS
brew install cloudflared

# Linux
# 从 https://github.com/cloudflare/cloudflared/releases 下载二进制

运行快速隧道(无需 Cloudflare 账号——给你一个 https://<random>.trycloudflare.com URL):

cloudflared tunnel --url http://localhost:8090

记下打印出的 URL——这就是你要给 Meta 的。

快速隧道会轮换

免费快速隧道 URL 每次重启 cloudflared 都会变。要稳定 URL,用 cloudflared tunnel login 登录并创建命名隧道。免费 Cloudflare 账号有无限命名隧道——命名隧道工作流见 Cloudflare 文档。

ngrok

ngrok http 8090

免费档每次重启 URL 都不同。付费档给你稳定子域名。

你自己的域名 + 反向代理

如果你已有带 TLS 证书的服务器(Caddy、nginx 等),把一条路由指向 localhost:8090。这是生产最稳的选择,但需要现有基础设施。


在 Meta 侧配置 webhook

隧道跑起来后:

  1. 记下隧道打印的公网 URL——假设是 https://abc123.trycloudflare.com。
  2. 生成一个 Verify Token——向导用 secrets.token_urlsafe(32) 替你做;手动配置时运行:
    python -c "import secrets; print(secrets.token_urlsafe(32))"
    

    把它存为 ~/.hermes/.env 中的 WHATSAPP_CLOUD_VERIFY_TOKEN。

  3. 启动 Hermes 网关:hermes gateway。
  4. 在 Meta App Dashboard → WhatsApp → Configuration(或按 UI 版本 Use cases → Customize → Configuration)→ Webhook 区段点 Edit。
  5. 填写:
    • Callback URL:https://abc123.trycloudflare.com/whatsapp/webhook
    • Verify Token:第 2 步的字符串(必须完全一致)
  6. 点 Verify and save。Meta 用 GET 请求访问你的 URL,网关回显 challenge,Meta 把 webhook 标记为已验证。
  7. 在 Webhook fields 下点 Manage → 订阅 messages 字段。这才是告诉 Meta 真正把入站消息投到你 webhook 的开关。

手动验证回路(从第三个终端):

TUNNEL="https://abc123.trycloudflare.com"
VERIFY="<your verify token>"

# 应打印 HTTP 200,正文 "hello"
curl -i "$TUNNEL/whatsapp/webhook?hub.mode=subscribe&hub.verify_token=$VERIFY&hub.challenge=hello"

# 健康端点——应显示 verify_token_configured: true 和 app_secret_configured: true
curl "$TUNNEL/health"

收件人白名单(Meta 侧)

在开发模式(你的应用过 App Review 之前),Meta 限制机器人能给哪些号码发消息:

  1. App Dashboard → WhatsApp → API Setup → To 下拉。
  2. 点 Manage phone number list。
  3. 添加你想发消息的号码(你的、团队的、友好测试者)。Meta 通过短信或 WhatsApp 给每个号码发一个 6 位验证码。

开发模式最多 5 个号码。进入 App Review 后移除此限制。


允许列表(Hermes 侧)

除 Meta 的收件人白名单外,Hermes 有自己的按平台允许列表,控制agent 处理哪些入站消息。加到 ~/.hermes/.env:

# 逗号分隔的电话号码,带国家码,无 '+' / 空格 / 短横线
WHATSAPP_CLOUD_ALLOWED_USERS=15551234567,15557654321

# 或允许所有人(仅在与 Meta 收件人白名单组合时安全)
# WHATSAPP_CLOUD_ALLOW_ALL_USERS=true

向导在第 6 步设置它。没有允许列表时,每条入站消息都被拒绝——这是有意为之,这样即使收件人白名单哪天松了,机器人也不会被随机号码调用。


打磨机器人的 WhatsApp 资料

WhatsApp 在聊天头部和联系人列表为你的机器人显示名字和头像。这些不能通过 Cloud API 设置——它们在 Meta 的 Business Manager 里。

机器人跑起来后,前往 business.facebook.com/wa/manage/phone-numbers,点你的电话号码,你会找到:

内容位置说明
Display name电话号码页顶部改名走 Meta 的名称审核流程(约 24–48 小时)。
Profile picture电话号码页顶部方形图,建议 ≥640×640px。立即更新。
About / 描述 / 网站 / 邮箱 / 营业时间 / 类别"Edit profile" 按钮用户点机器人名字时出现在信息面板。装饰性。
Verified 徽章(绿色对勾)Business Manager → Security Center → Start Verification需要 Meta 单独的企业认证流程。

hermes whatsapp-cloud 向导在设置末尾打印这些链接。这些对机器人工作都不是必需的——纯粹是机器人在用户面前的门面。


配置参考

所有设置都在 ~/.hermes/.env。必填值用粗体。

变量默认值说明
WHATSAPP_CLOUD_PHONE_NUMBER_ID—API Setup 中的 15-17 位 ID。不是电话号码。
WHATSAPP_CLOUD_ACCESS_TOKEN—Meta access token(以 EAA 开头)。临时 24h 或系统用户永久。
WHATSAPP_CLOUD_APP_SECRET—Settings → Basic 中的 32 字符 hex。没有它,入站以 503 拒绝。
WHATSAPP_CLOUD_VERIFY_TOKEN—GET 握手的共享密钥。向导自动生成。
WHATSAPP_CLOUD_ALLOWED_USERS—逗号分隔、允许给机器人发消息的 wa_id。
WHATSAPP_CLOUD_ALLOW_ALL_USERSfalse设为 true 绕过允许列表。
WHATSAPP_CLOUD_APP_ID—可选,供未来分析集成。
WHATSAPP_CLOUD_WABA_ID—可选,供未来分析集成。
WHATSAPP_CLOUD_WEBHOOK_HOST未设置(双栈:所有接口,IPv4+IPv6)webhook 服务器绑定的接口。
WHATSAPP_CLOUD_WEBHOOK_PORT8090webhook 服务器绑定端口。必须与你隧道转发的端口一致。
WHATSAPP_CLOUD_WEBHOOK_PATH/whatsapp/webhookMeta POST 的 URL 路径。
WHATSAPP_CLOUD_API_VERSIONv20.0Meta Graph API 版本。仅当 Meta 文档推荐更新版本时才覆盖。
WHATSAPP_CLOUD_HOME_CHANNEL—用作机器人主频道的 wa_id(cron 任务等用)。

你可以同时启用 Baileys(whatsapp)和 Cloud(whatsapp_cloud)适配器,指向不同号码。


功能

入站

  • 文本消息——直传给 agent。
  • 图片——自动下载并附加到 agent 输入。原生视觉模型(Claude、GPT-4o、Gemini 等)直接读图;非视觉模型收到自动生成的文字描述。
  • 语音消息——自动下载为 .ogg,经你配置的 STT 提供商转写(本地 faster-whisper、OpenAI/Nous、Groq 等),再作为文本交给 agent。
  • 文档——自动下载。小的、文本可读文件(.txt、.md、.json、.py、.csv 等,≤100KB)内联进 agent 输入,让它无需工具调用即可读。更大的文件本地缓存,供 agent 其他工具访问。
  • 按钮点击——用户点机器人之前发的按钮时(澄清选择、命令审批、斜杠命令确认),点击直接路由到对应处理器。过期点击回退为普通文本输入。
  • 回复上下文——用户回复某条旧消息时,agent 看到原文作为上下文。引用一张图片、语音、视频或文档(你或机器人发的,例如 cron 投递的图表)也会把该文件附到这一轮,因此引用图片下问"这是什么?"可行。Meta 的 webhook 只带被引用消息 id,因此从本地最近收发索引解析(每网关最近 1000 条);更早的引用不带附件到达。

出站

  • 文本——markdown 自动转成 WhatsApp 风味语法(**bold** → *bold*、~~strike~~ → ~strike~、标题 → 粗体、[link](url) → link (url))。长消息按 4096 字符一块拆分。
  • 图片——agent 生成的图片和本地图片文件都支持,作为原生照片附件投递。
  • 语音消息——TTS 输出经 ffmpeg 转成原生 WhatsApp 语音气泡(绿色波形)。没装 ffmpeg 时回退为 MP3 音频附件。见下文"语音消息"。
  • 视频 / 文档——都支持,作为原生附件发送。

交互式 UX

当 agent 触发这些流程时,Hermes 用 WhatsApp 原生交互消息——可点的按钮,而非"回复编号"提示:

  • clarify 工具——多选题渲染为快速回复按钮(1–3 项)或可点开的列表面板(4+ 项)。选"✏️ Other"让用户输入自由文本,agent 收到作为结果。
  • 危险命令审批——当 agent 的终端/代码执行碰到门控命令时,用户看到 ✅ Approve / ❌ Deny 按钮,而不必打 /approve 或 /deny。
  • 斜杠命令确认——/reload-mcp 这类特权命令显示 ✅ Approve Once / 🔒 Always / ❌ Cancel 按钮。

按钮渲染失败时(例如旧版 WhatsApp 客户端),所有交互提示优雅降级为纯文本。

已读回执与正在输入指示

Hermes 立即确认入站消息:

  • 网关一收到你的消息,它就显示蓝色双勾。
  • agent 准备回复时,你 WhatsApp 聊天里机器人名字显示 "typing…"。
  • 机器人首条回复到达时,正在输入指示自动消失。

这让"机器人看到你的消息了"与"它还在生成回复"一目了然。

语音消息

WhatsApp 区分"语音消息"(绿色波形气泡)和普通音频文件附件。区别纯粹在编解码:语音消息必须是 audio/ogg、opus 编码。

Hermes TTS 产出 MP3。两条路径:

  • PATH 上有 ffmpeg(推荐)——出站 TTS 被转换,作为正经语音消息到达。安装:
    • Windows:winget install Gyan.FFmpeg
    • macOS:brew install ffmpeg
    • Linux:包管理器
  • 没有 ffmpeg——出站 TTS 作为 MP3 音频附件到达。能正常播放,只是不像语音消息。网关日志会一次性警告,让你知道。

你可以通过健康端点检查网关是否找到 ffmpeg:

curl http://localhost:8090/health
# 找 "ffmpeg_present": true

已知限制

24 小时对话窗口

Meta 只允许在用户最后一条入站消息后 24 小时窗口内发自由格式消息。窗口之外,Meta API 只接受预先审批的消息模板。

实际含义:

  • 被动聊天(用户私信 → 机器人 24h 内回 → 用户再回 → …)永久可用。覆盖 >95% 的正常机器人使用。
  • 间隔 >24h 后投递到 WhatsApp 的 cron 任务会以 Graph 错误码 131047("Re-engagement message")失败。
  • 耗时超过 24h 的长时 delegate_task 异步结果同样失败。
  • 把外部事件路由到 WhatsApp 的 webhook 订阅者在用户最近没私信机器人时失败。

Hermes 在系统提示中就这个窗口警告 agent,因此模型排程延迟消息时知道要提一句。

消息模板支持(窗口外发送的 workaround)尚未在 Hermes 实现。如果你需要,请开 issue——已计划,但在等明确的需求信号。

群聊

Cloud API 的群组支持有限(按 Meta 的能力层级门控)。Hermes 的 whatsapp_cloud 适配器在 v1 中目前只处理私信。需要群聊请用 Baileys 桥接。

出站限流

Meta 默认吞吐是每个业务号码 80 条消息/秒,可升级。Hermes 目前不在客户端强制——极高量发送可能撞到 Meta 限制。


故障排查

Meta 仪表盘里设置校验失败("URL couldn't be validated")

几乎总是以下之一:

  • 隧道 URL 错了或过期——cloudflared 快速隧道会轮换。拿新 URL,同时更新 .env 和 Meta 仪表盘。
  • Verify token 不匹配——~/.hermes/.env 中 WHATSAPP_CLOUD_VERIFY_TOKEN 必须与你敲进 Meta 仪表盘的完全一致。先跑上面的 curl 探针,确认网关本地的 verify 握手正常。
  • 网关没在跑——检查 hermes gateway 已起。
  • App Secret 没设——没有它,Hermes 以 503 拒绝入站 POST。Meta 解读为"无法校验"。

graph error 100: Object with ID '...' does not exist

你把电话号码(10-11 位)粘进了 WHATSAPP_CLOUD_PHONE_NUMBER_ID,而不是 Phone Number ID(Meta 的 15-17 位内部 ID)。重看 API Setup 页——Phone Number ID 显示在 "From" 下拉下方。

向导现在用校验器抓住这个,但手动配置时值得知道。

graph error 190: Authentication Error

你的 access token 无效。子码:

  • subcode 463——token 过期。临时 token 24h。重新生成,或切到系统用户永久 token(见上)。
  • subcode 467——token 被作废(吊销或改密码)。
  • 其他 190——生成 token 时没带所需权限。确认三个(business_management、whatsapp_business_messaging、whatsapp_business_management)都选了。

graph error 131047: Re-engagement message

24 小时对话窗口过期了(见"已知限制")。要么:

  • 让用户先私信机器人重开窗口。
  • 等 Hermes 支持模板。

入站消息:media metadata fetch failed (status=401)

与出站(graph error 190)同一个 401 根因——access token 无效或过期。修 token。

机器人回复显示为原始 JSON / 工具调用泄漏

常见原因:为 whatsapp_cloud 配置的工具集缺了 agent 想调的工具。查 hermes tools list,确认平台用的是 hermes-whatsapp(默认 Cloud 适配器工具集,与 Baileys 相同)。

如果模型发出工具调用形态的文本而非结构化调用,通常意味着工具集实际上是空的。平台 → 默认工具集映射见 hermes_cli/platforms.py。

STT(语音转写)返回空 / "could not transcribe"

默认 stt.provider: local 需要 python -c "import pm; pm.sync_venv(['stt-whisper'], explicit=True)"。如果你是 Nous 订阅者,可以改走托管网关做 STT——在 hermes tools 里给语音转文本选 Nous Subscription,或直接设置:

hermes config set stt.provider nous
hermes gateway restart

这用你的 Nous Portal access token,无需单独 OpenAI key。(旧文档建议 stt.use_gateway true——那个标志是遗留;现在只由 provider 选择控制路由。)


安全说明

  • 把 App Secret 当密码对待——任何人拿到它都能伪造 Hermes 会当作真实的 webhook 载荷。
  • verify token 是共享密钥——泄露风险较低(最坏情况有人把 Meta webhook 重新订阅到他自己的另一个 URL),但仍避免提交它。
  • access token 是你机器人的身份——系统用户 token 等价于长期 API key。部署被攻陷时立即轮换。
  • 设了 WHATSAPP_CLOUD_APP_SECRET 时 webhook 端点只接受签名请求——即使开发环境也保持设置。没有它,网关以 HTTP 503 拒绝入站投递。
  • /health 端点无认证——暴露它是安全的,因为它只报告配置是否存在的布尔值,不报告值本身。但如果你不想暴露它,在反向代理/隧道层限制访问。

与 Baileys 桥接对比

Baileys(hermes whatsapp)Cloud API(hermes whatsapp-cloud)
账号类型个人业务
设置扫二维码Meta 应用 + WABA + token
依赖Node.js + npm纯 Python(httpx + aiohttp)
进程托管 Node 子进程aiohttp webhook 服务器
需要公网 URL?否是
封号风险有(非官方 API)无(官方支持)
入站轮询 Node 桥接Meta webhook POST
出站本地桥接 → BaileysHTTPS 到 graph.facebook.com
群组完整支持仅私信(v1)
24h 窗口无限制硬规则——之后需模板
语音消息(出)原生有 ffmpeg 原生,否则 MP3
已读回执无有(蓝色双勾)
正在输入指示无有(回复时自动消失)
交互按钮仅文本回退原生(澄清、审批、斜杠确认)
生产使用有风险(Meta 可封)为此设计

跑个人项目的用户大多偏好 Baileys。跑面向客户机器人的用户大多偏好 Cloud API。


另见