Skip to main content
Commands, package names, and image names on this page come from the open-source project that Mibyan Desktop is built on, and can differ from the Mibyan Desktop installer. For the supported Mibyan install and update path, see Install and update.
Python dependency commands on this page use a PM-prepared source checkout. After a dependency change, reactivate the checkout and restart Mibyan. Mibyan can connect to WhatsApp through Meta’s official WhatsApp Business Cloud API. This is the production-grade path: no Node.js bridge subprocess, no QR codes, no account-ban risk. In exchange:
  • You need a Meta Business account (not personal WhatsApp).
  • The bot operates on a dedicated business phone number, not your personal number.
  • The Mibyan gateway needs a public HTTPS URL so Meta can deliver inbound messages via webhook.
  • Replies more than 24 hours after the user’s last message require a pre-approved template (this is Meta’s “customer service window” rule, not a Mibyan limit).
If those constraints don’t work for your use case, the Baileys bridge integration is the alternative — personal account, no public URL needed, but unofficial and ban-prone.
Which one should I use?
  • Cloud API (this guide) — running a real business bot, want stability, fine with the Meta verification + template paperwork
  • Baileys bridge — personal projects, quick demos, single-user setups, willing to risk the bot phone number’s account

Quick start

The wizard walks you through every credential, validates each one as you paste it (catches the #1 setup trap — pasting a phone number into the Phone Number ID field), and prints exact follow-up instructions for the parts that need to happen outside the wizard (starting cloudflared, configuring Meta’s webhook dashboard). The rest of this page is the manual reference.

Prerequisites

  1. A Meta Business account. Create one at business.facebook.com.
  2. A Meta app with WhatsApp enabled. See “Creating the Meta app” below.
  3. A way to expose a local port to the public internet with HTTPS. Cloudflare Tunnel (cloudflared) is recommended — free, no port forwarding, no domain required. ngrok, your own domain with a reverse proxy + TLS, or a VPS with the gateway directly bound to a public IP all work too.
  4. Optional but recommended: ffmpeg on PATH so outbound voice messages render as native WhatsApp voice-note bubbles (green waveform) instead of MP3 audio attachments. Mibyan degrades gracefully if absent.

Creating the Meta app

  1. Go to developers.facebook.com/apps → Create App.
  2. Choose use case: “Connect with customers through WhatsApp” → Next.
  3. Pick or create a business portfolio. Review the publishing requirements. Confirm → Create app.
  4. After creation you’ll land on Customize use case → Connect on WhatsApp → Quickstart. Click Start using the API → you’re now on the API Setup page.
  5. Make sure a WhatsApp Business Account (WABA) is linked. If you created a new portfolio in step 3, one was auto-created. Verify in the API Setup page.
You’ll need these values from the dashboard — the wizard prompts for them in this order:

Permanent token (production)

Temporary access tokens expire after 24 hours, which means a token generated today stops working tomorrow. For production deployments use a System User permanent token:
  1. Go to business.facebook.com/latest/settings → System users (left sidebar).
  2. Add → name (e.g. mibyan-bot) → role: Admin.
  3. Select the new user → Assign Assets:
    • Select your app → toggle Manage app under Full control.
    • Select your WhatsApp account → toggle Manage WhatsApp Business Accounts under Full control.
    • Click Assign assets.
  4. Generate token with these permissions:
    • business_management
    • whatsapp_business_messaging
    • whatsapp_business_management
  5. Set token expiration: Never.
  6. Copy the token → update WHATSAPP_CLOUD_ACCESS_TOKEN in ~/.mibyan/.env → restart the gateway.
System User tokens don’t expire unless you explicitly revoke them.

Exposing Mibyan to the internet

The Cloud API delivers inbound messages by HTTPS POST to your webhook URL — that means the Mibyan gateway has to be reachable from Meta’s servers. Three common ways: Free, no port forwarding, works on Windows / macOS / Linux. Runs as a separate process alongside the gateway. Install:
Run a quick tunnel (no Cloudflare account needed — gives you a https://<random>.trycloudflare.com URL):
Note the printed URL — that’s what you’ll give Meta.
Quick tunnels rotateThe free quick-tunnel URL changes every time you restart cloudflared. For a stable URL, log in with cloudflared tunnel login and create a named tunnel. Free Cloudflare accounts get unlimited named tunnels — see Cloudflare’s docs for the named-tunnel workflow.

ngrok

Free tier shows a different URL on each restart. Paid tier gives you a stable subdomain.

Your own domain + reverse proxy

If you already have a server with a TLS cert (Caddy, nginx, etc.), point a route at localhost:8090. This is the most stable option for production but requires existing infrastructure.

Configuring the webhook on Meta’s side

Once your tunnel is running:
  1. Note the public URL printed by your tunnel — say https://abc123.trycloudflare.com.
  2. Generate a Verify Token — the wizard does this for you with secrets.token_urlsafe(32); if you’re configuring manually, run:
    Save it as WHATSAPP_CLOUD_VERIFY_TOKEN in ~/.mibyan/.env.
  3. Start the Mibyan gateway: mibyan gateway.
  4. In the Meta App Dashboard → WhatsApp → Configuration (or Use cases → Customize → Configuration depending on UI version) → click Edit on the Webhook section.
  5. Fill in:
    • Callback URL: https://abc123.trycloudflare.com/whatsapp/webhook
    • Verify Token: the string from step 2 (must match exactly)
  6. Click Verify and save. Meta hits your URL with a GET request, the gateway echoes back the challenge, and Meta marks the webhook as verified.
  7. Under Webhook fields, click Manage → subscribe to the messages field. This is what tells Meta to actually deliver inbound messages to your webhook.
To verify the loop manually (from a third terminal):

Recipient whitelist (Meta-side)

In development mode (before your app goes through App Review), Meta restricts which numbers your bot can message:
  1. App Dashboard → WhatsApp → API Setup → To dropdown.
  2. Click Manage phone number list.
  3. Add the phone numbers you want to message (yours, your team’s, friendly testers). Meta sends each one a 6-digit verification code via SMS or WhatsApp.
Up to 5 numbers in dev mode. Going to App Review removes this limit.

Allowlist (Mibyan-side)

In addition to Meta’s recipient whitelist, Mibyan has its own per-platform allowlist that controls which incoming messages the agent processes. Add to ~/.mibyan/.env:
The wizard sets this in step 6. Without an allowlist, every inbound message is denied — this is intentional, so the bot can’t be invoked by random numbers if the recipient whitelist is ever loosened.

Polishing your bot’s WhatsApp profile

WhatsApp displays a name and profile picture for your bot in the chat header and contact list. These can’t be set via the Cloud API — they live in Meta’s Business Manager. Once your bot is working, head to business.facebook.com/wa/manage/phone-numbers, click your phone number, and you’ll find: The mibyan whatsapp-cloud wizard prints these links at the end of setup. None of this is required for the bot to work — it’s pure polish for how your bot appears to users.

Configuration reference

All settings live in ~/.mibyan/.env. Required values are in bold. You can have both the Baileys (whatsapp) and Cloud (whatsapp_cloud) adapters enabled simultaneously, targeting different phone numbers.

Features

Inbound

  • Text messages — passed straight to the agent.
  • Images — auto-downloaded and attached to the agent’s input. Models with native vision (Claude, GPT-4o, Gemini, etc.) read the image directly; non-vision models receive an auto-generated text description.
  • Voice notes — auto-downloaded as .ogg, transcribed via your configured STT provider (local faster-whisper, OpenAI/Nous, Groq, etc.), then handed to the agent as text.
  • Documents — auto-downloaded. Small text-readable files (.txt, .md, .json, .py, .csv, etc.) up to 100KB get inlined into the agent’s input so it can read them without a tool call. Larger files are cached locally for the agent’s other tools to access.
  • Button taps — when the user taps a button the bot sent earlier (clarify choice, command approval, slash-command confirm), the tap is routed directly to the right handler. Stale taps fall back to being treated as regular text input.
  • Reply context — when the user replies to a previous message, the agent sees the original text as context. Quoting an image, voice note, video or document (yours or one the bot sent, e.g. a cron-delivered chart) also attaches that file to the turn, so “what is this?” under a quoted image works. Meta’s webhook carries only the quoted message id, so this resolves from a local index of recent sends/receives (last 1000 messages per gateway); older quotes arrive without the attachment.

Outbound

  • Text — markdown is auto-converted to WhatsApp’s flavored syntax (**bold** → *bold*, ~~strike~~ → ~strike~, headers → bold, [link](https://github.com/NousResearch/hermes-agent/tree/main/website/docs/user-guide/messaging/url) → link (url)). Long messages split at 4096 chars per chunk.
  • Images — agent-generated images and local image files both supported, delivered as native photo attachments.
  • Voice messages — text-to-speech output is converted via ffmpeg into the native WhatsApp voice-note bubble (green waveform). Without ffmpeg installed, falls back to an MP3 audio attachment. See “Voice messages” below.
  • Video / documents — both supported, sent as native attachments.

Interactive UX

When the agent invokes any of these flows, Mibyan uses WhatsApp’s native interactive messages — tap-to-answer buttons instead of “reply with the number” prompts:
  • clarify tool — multi-choice questions render as quick-reply buttons (1–3 choices) or a tap-to-open list sheet (4+ choices). Picking “✏️ Other” lets the user type a free-form answer that the agent receives as the resolution.
  • Dangerous-command approvals — when the agent’s terminal/code execution hits a gated command, the user sees ✅ Approve / ❌ Deny buttons instead of needing to type /approve or /deny.
  • Slash-command confirmations — privileged commands like /reload-mcp show ✅ Approve Once / 🔒 Always / ❌ Cancel buttons.
All interactive prompts gracefully degrade to plain text if the buttons fail to render (e.g. on legacy WhatsApp clients).

Read receipts and typing indicator

Mibyan acknowledges inbound messages immediately:
  • Your message shows blue double-checkmarks as soon as the gateway receives it.
  • The bot’s name in your WhatsApp chat shows “typing…” while the agent is preparing a reply.
  • The typing indicator auto-dismisses when the bot’s first response message arrives.
This makes it obvious when the bot has seen your message versus when it’s still working on a response.

Voice messages

WhatsApp distinguishes between a “voice note” (the green waveform bubble) and a generic audio file attachment. The difference is purely codec: voice notes need to be audio/ogg with opus encoding. Mibyan TTS produces MP3. Two paths:
  • With ffmpeg on PATH (recommended) — outbound TTS is converted and arrives as a proper voice note. Install:
    • Windows: winget install Gyan.FFmpeg
    • macOS: brew install ffmpeg
    • Linux: package manager
  • Without ffmpeg — outbound TTS arrives as an MP3 audio attachment. Plays fine, just doesn’t look like a voice note. A one-time warning fires in the gateway log so you know.
You can check whether the gateway found ffmpeg via the health endpoint:

Known limitations

24-hour conversation window

Meta only allows free-form messages within a 24-hour window after the user’s last inbound message. Outside that window, the only thing Meta’s API accepts is a pre-approved message template. What this means in practice:
  • Reactive chat (user DMs → bot replies within 24h → user replies → …) works forever. This covers >95% of normal bot use.
  • Cron jobs that deliver to WhatsApp after a gap > 24h will fail with Graph error code 131047 (“Re-engagement message”).
  • Long-running delegate_task async results that take longer than 24h fail the same way.
  • Webhook subscribers that route external events to WhatsApp fail when the user hasn’t DM’d the bot recently.
Mibyan warns the agent about this window in its system prompt, so the model knows to mention it when scheduling delayed messages. Message-template support (the workaround for outside-window sends) is not yet implemented in Mibyan. If you need it, please open an issue — it’s planned but waiting on a clear demand signal.

Group chats

The Cloud API has limited group support (capability-tier gated by Meta). Mibyan’s whatsapp_cloud adapter currently handles direct messages only in v1. If you need group chats, use the Baileys bridge.

Outbound rate limit

Meta’s default throughput is 80 messages/second per business phone number, with upgrades available. Mibyan doesn’t currently enforce this client-side — extremely high-volume sends could hit Meta’s limit.

Troubleshooting

Setup verification fails (“URL couldn’t be validated”) in Meta dashboard

Almost always one of:
  • Tunnel URL is wrong or stale — cloudflared quick tunnels rotate. Get a fresh URL and update both .env and Meta’s dashboard.
  • Verify token mismatch — the token in ~/.mibyan/.env’s WHATSAPP_CLOUD_VERIFY_TOKEN must match exactly what you typed into Meta’s dashboard. Run the curl probe above to confirm the gateway’s verify handshake works locally first.
  • Gateway not running — check mibyan gateway is up.
  • App Secret not set — without it, Mibyan refuses inbound POSTs with 503. Meta interprets that as “can’t validate.”

graph error 100: Object with ID ’…’ does not exist

You pasted your phone number (10-11 digits) into WHATSAPP_CLOUD_PHONE_NUMBER_ID instead of the Phone Number ID (Meta’s 15-17 digit internal ID). Re-check the API Setup page — the Phone Number ID is shown below the “From” dropdown. The wizard catches this with a validator now, but it’s worth knowing if you’re configuring manually.

graph error 190: Authentication Error

Your access token is invalid. Subcodes:
  • subcode 463 — token expired. Temp tokens last 24h. Regenerate, or switch to a System User permanent token (see above).
  • subcode 467 — token invalidated (revoked or password changed).
  • Other 190 — token didn’t have the required permissions when generated. Make sure all three (business_management, whatsapp_business_messaging, whatsapp_business_management) were selected.

graph error 131047: Re-engagement message

The 24-hour conversation window expired (see “Known limitations”). Either:
  • Ask the user to DM the bot first to reopen the window.
  • Wait for template support to land in Mibyan.

Inbound message: media metadata fetch failed (status=401)

Same 401 root causes as outbound (graph error 190) — the access token is invalid or expired. Fix the token.

Bot replies appear as raw JSON / tool-call leakage

Common cause: the toolset configured for whatsapp_cloud is missing the tools the agent wants to call. Check mibyan tools list and verify the platform is using mibyan-whatsapp (the default Cloud adapter toolset, same as Baileys). If the model emits tool-call-shaped text instead of a structured call, it usually means the toolset was effectively empty. See mibyan_cli/platforms.py for the platform → default toolset mapping.

STT (voice note transcription) returns empty / “could not transcribe”

The default stt.provider: local requires python -c "import pm; pm.sync_venv(['stt-whisper'], explicit=True)". If you’re a Nous subscriber, you can route STT through the managed gateway instead — select Nous Subscription for speech-to-text in mibyan tools, or set it directly:
This uses your Nous Portal access token instead of needing a separate OpenAI key. (Older docs suggested stt.use_gateway true — that flag is legacy; the provider selection alone controls routing now.)

Security notes

  • Treat the App Secret like a password — anyone with it can forge webhook payloads that Mibyan will accept as authentic.
  • The verify token is a shared secret — leaks are lower-stakes (worst case someone could re-subscribe Meta’s webhook to a different URL of theirs), but still avoid committing it.
  • The access token is your bot’s identity — System User tokens are equivalent to long-lived API keys. Rotate immediately if a deployment is compromised.
  • The webhook endpoint accepts only signed requests when WHATSAPP_CLOUD_APP_SECRET is set — leave it set even in development. Without it, the gateway refuses inbound delivery with HTTP 503.
  • The /health endpoint is unauthenticated — it’s safe to expose because it only reports config-presence booleans, not the values themselves. But if you’d rather not surface it, restrict access at the reverse proxy / tunnel layer.

Comparison to the Baileys bridge

Most users running Mibyan for personal projects prefer Baileys. Most users running customer-facing bots prefer Cloud API.

See also