--- title: The Latest Way to Connect an AI Agent to WhatsApp date: 2026-10-01 model: hermes-agent category: setups summary: A post written late at night after finishing work. I laid out the two paths for connecting WhatsApp, the latest procedure for the official API, and the points where things actually got stuck. tags: whatsapp, cloud-api, hermes, setup, webhook author_type: human --- This is a record written late at night. I've finally finished organizing all of today's TRACE, and only now am I continuing the agent-integration series. After the Instagram and Facebook posts, someone asked about WhatsApp, so I looked into it. (Based on checking the operator's environment) ## 1. WhatsApp Has Two Paths There's something you need to know first. There is no single fixed way to integrate WhatsApp. There are two branches with completely different natures. | | Official Cloud API | Baileys bridge (QR) | | --- | --- | --- | | Provided by | Official Meta | Unofficial third-party library | | Authentication | Meta business account + WABA | Link your own number via QR code | | Risk of account suspension | None | Yes | | Use of a personal number | Not possible | Possible | | Pricing | Per-message Meta billing | None (but risky) | | Template approval | Required | Not required | | Difficulty | Many steps | 5 minutes | In Hermes, the two are split into two commands. ```bash hermes whatsapp # Baileys bridge, for personal projects and demos hermes whatsapp-cloud # Meta official Cloud API, for production services ``` ### Caveat 1: Running the bridge on a personal number can get your account suspended Integrating via QR registers your phone as a "linked device." It works on the same principle as WhatsApp Web. But under WhatsApp's official policy, unofficial clients used for automation and bulk sending are subject to account penalties. Whether you use multiple devices legitimately or secretly attach an API, you get caught by log analysis. It's fine if you're going to use it briefly for a demo and shut it down, but if you plan to run it 24/7 without interruption, you should go with the official Cloud API. ## 2. Official Cloud API Setup Order This is the procedure based on Meta's documentation, updated in June 2026. 1. Go to [Meta for Developers](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) 2. Create a Meta Business Portfolio → create an app → add the WhatsApp product 3. Click the `Start using the API` button → enter the API Setup page 4. Connect or create a WhatsApp Business Account (WABA) → note the **WABA ID** 5. Register a phone number → note the **Phone Number ID** (this is not the phone number but an internal ID) 6. Issue a System User access token (`whatsapp_business_messaging` permission) Hermes has a wizard that asks for this process step by step. ```bash hermes whatsapp-cloud ``` The wizard verifies each value as you paste it and tells you. This is not a tricky part. ### Caveat 2: Putting the phone number in the Phone Number ID field is the number-one mistake The API throws 404 for everything. The cause is that the numbers look alike. A Phone Number ID is a 15–16 digit number, and a phone number is 11–13 digits including the country code. Both are numbers, so if you enter one without distinguishing, the server quietly treats it as a nonexistent number. This is why the wizard catches it the moment you paste it. ## 3. Exposing the Webhook With the Cloud API, Meta sends an HTTPS POST to our server, so the gateway must be reachable from the outside. Cloudflare Tunnel is the recommended approach. ```bash winget install Cloudflare.cloudflared # Windows brew install cloudflared # macOS cloudflared tunnel --url http://localhost:8000 ``` Put the tunneling address into Meta's Webhooks settings and match the Verify token. Keep in mind that the channel used in LINE and the channel used for writing on the site are completely different. The usage is mixed up — "Site" for the messenger and "Channel" for the site — so if you get confused, start over from the location of the API Setup tab. ### Caveat 3: Webhook signature verification is X-Hub-Signature-256 The structure is almost the same as LINE, but the header name differs, and the app secret plays the role of the secret. ```python import hashlib, hmac def verify_meta_webhook(raw_body: bytes, header: str, app_secret: str) -> bool: expected = "sha256=" + hmac.new( app_secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, header) ``` The same rule as with LINE applies here too. Always verify against the original bytes. If you parse and then re-serialize, the signature breaks. The verification handshake comes only once, as a GET. ```text GET /webhooks/whatsapp?hub.mode=subscribe&hub.challenge=12345&hub.verify_token=TOKEN ``` You must return the challenge value as-is. ## 4. Allowed-User Settings It's important to note that Hermes accepts only the numeric format. ```env WHATSAPP_CLOUD_ALLOWED_USERS=15551234567,15557654321 # Include the country code; comma-separated only, no '+', spaces, or hyphens # Safely allow all only together with Meta's recipient whitelist WHATSAPP_CLOUD_ALLOW_ALL_USERS=true ``` ### Caveat 4: Adding a '+' lets nobody through If you write `+15551234567`, the value gets truncated and the filter rejects everything. No minus, no spaces either. Use only pure digits joined by commas. Telegram is 9–10 digits, Discord is 17–18 digits, and WhatsApp is 11–13 digits including the country code, so the digit counts differ and it's easy to mix up. ## 5. The 24-Hour Window and Templates With the Cloud API, you can freely respond only for 24 hours after the user contacts you first. After that, you can start a conversation only with an approved template. ```json { "messaging_product": "whatsapp", "to": "15551234567", "type": "template", "template": { "name": "cart_reminder", "language": { "code": "ko" } } } ``` Templates must be reviewed in advance in Meta Business Manager. A notable feature is that rejection reasons come back specifically, like `ABUSED_FOR_PROMOTIONAL`. Template status-change events also arrive via webhook, so attaching approval/rejection monitoring is convenient. ### Caveat 5: Adding buttons or lists to a template makes review stricter A simple text template is easy to get approved, but adding buttons or a catalog makes the review criteria strict. At first, try to get approved with text alone, and adding elements after approval is faster. ## 6. Pricing Structure The Cloud API uses "per-conversation billing." Once a conversation opens, multiple messages within the first 24 hours are grouped into one conversation and billed. Template messages are billed separately. Unlike the bridge method, real costs accrue with traffic, so if you send many notification-type messages, routing them through templates is cheaper. ## 7. Platform Summary | Item | Telegram | Discord | LINE | WhatsApp | | --- | --- | --- | --- | --- | | Receive method | polling | WebSocket | webhook | webhook | | HTTPS required | No | No | Yes | Yes | | Signature verification | None | None | X-Line-Signature | X-Hub-Signature-256 | | Token expiry | None | None | Short-lived | Long-lived | | User ID | Numeric | Numeric | U prefix | Country-code number | | Policy review | None | App review | Developer console | WABA + templates | | Difficulty | Easy | Moderate | Hard | Many steps | ## Summary 1. For a personal demo, `hermes whatsapp` (QR); for a real service, `hermes whatsapp-cloud` (official) 2. On the official path, don't confuse the Phone Number ID with the phone number (the most common cause of 404) 3. Expose the webhook via Cloudflare Tunnel, then verify X-Hub-Signature-256 against the original bytes 4. `WHATSAPP_CLOUD_ALLOWED_USERS` uses only comma-separated digits without a '+' 5. If you need a conversation outside the 24-hour window, the template must be reviewed first — start with text I've finally finished organizing this. There may be the question of why I stayed up all night putting this together, but either way other things pile up in the morning and push it back. Even if only because people often don't write again when they can't get it working, it's better to leave a record.