# WhatsApp Business

> Connect a WhatsApp Business number in one click, let an agent answer customer messages, send templates and media, and track delivery - on Meta's official Cloud API.

Connects a WhatsApp Business number to FlyMyAI on Meta's official Cloud API.
Customers write to the number, your agent answers; the agent can also send
templates, media, buttons and lists, and see whether a message was delivered
and read.

This is the **business** side of WhatsApp: a company number with a bot behind
it, like a support line. It is not a way to drive a personal WhatsApp account -
see [Personal vs business](#personal-vs-business) below.

## What it can do

- **Receive** - every message a customer sends to the number reaches FlyMyAI and
  starts the agent you chose, with the sender, the text and any media id.
- **Reply** - text, approved templates, images/video/audio/documents/stickers,
  reply buttons and lists, locations, contact cards, reactions, read receipts.
- **Track** - sent, delivered, read and failed statuses for every message you send.
- **Manage** - message templates, the numbers on the account, the business profile.

## Connect

1. Open **Tools** in FlyMyAI, find **WhatsApp Business**, press **Connect**.
2. A Meta window opens. Sign in, pick (or create) the WhatsApp Business Account
   and the phone number, and confirm. That is Meta's Embedded Signup - there is
   no developer app to create and no token to copy.
3. The window closes and the tool shows **Configured**. FlyMyAI has subscribed
   the number's webhooks and registered it for messaging.

Each connection is one business number. To use several numbers, add several
connections.

> **Note: Registration pending**
>
> If the tool reports the registration as *pending*, the number already has a
> two-step verification PIN that FlyMyAI cannot know. Ask the agent to run
> `whatsappcloud_register_phone` with that PIN, or reset the PIN in
> **WhatsApp Manager -> Phone numbers -> Two-step verification** and run it with
> a new one. Everything else is already connected.

### Manual setup (no Meta window)

If your team runs its own Meta app, you can paste credentials instead: in the
connection settings set `WHATSAPPCLOUD_API_KEY` (a permanent **System User**
token with `whatsapp_business_messaging` and `whatsapp_business_management`),
`WHATSAPPCLOUD_PHONE_NUMBER_ID` and `WHATSAPPCLOUD_WABA_ID` - all three are on
**App Dashboard -> WhatsApp -> API Setup**. Only the token is mandatory; without
the ids the agent has to pass them in every call. The same fields can be set by
an agent through `set_tool_config`.

A number set up manually still needs FlyMyAI's webhook to receive messages: on
the Meta app's **WhatsApp -> Configuration** page set the callback URL to
`https://backend.flymy.ai/api/v1/agents/whatsapp/webhook/` with the verify
token your FlyMyAI operator gives you, and subscribe to the `messages` field.
FlyMyAI binds a number to a connection only after reading the number back from
Meta with that connection's token - a phone number id alone, which is in every
webhook, never redirects anyone's messages.

## Receiving messages

A customer message starts **the receiving agent**:

- the agent you named with `whatsappcloud_set_inbound_agent` (by id or uuid), or
- otherwise the oldest active agent that has this WhatsApp connection attached.

The agent must be **frozen** once (`freeze_agent`) - inbound messages run its
frozen version, exactly like a schedule does, and each customer number gets its
own thread. The run gets the version's usual inputs (its schedule defaults) plus
a reserved `trigger` input - the same shape every event source in FlyMyAI uses:

| Input | Meaning |
| --- | --- |
| `trigger.kind` | Always `whatsapp` |
| `trigger.event_id` | FlyMyAI's id of the inbound event |
| `trigger.whatsapp.from` | Customer number, e.g. `15551234567` - pass it as `to` when replying |
| `trigger.whatsapp.from_name` | The customer's WhatsApp profile name |
| `trigger.whatsapp.text` | The message as text; for media, the caption with the kind in brackets |
| `trigger.whatsapp.message_type` | `text`, `image`, `video`, `audio`, `document`, `sticker`, `location`, `interactive`, `button`, `reaction`, `contacts` |
| `trigger.whatsapp.message_id` | The WAMID - pass it as `reply_to_message_id` to quote the message |
| `trigger.whatsapp.media_id` | For media messages, the id to fetch with `whatsappcloud_get_media` |
| `trigger.whatsapp.reply_to_message_id` | Set when the customer replied to one of your messages |
| `trigger.whatsapp.timestamp`, `.to_phone_number_id`, `.to_display_phone_number`, `.raw` | When it arrived, which number it hit, and Meta's full message object |

The inputs arrive verbatim in the run's `Inputs:` line, and an instruction can
reference them as `{{ trigger.whatsapp.text }}`. An input of your own named
`trigger` is ignored - the event data always wins.

A typical receiving agent's pipeline: read `trigger.whatsapp.text`, decide, call
`whatsappcloud_send_text` with `to = trigger.whatsapp.from`.
`whatsappcloud_list_inbound_messages` shows what arrived recently, per customer
if you pass `from_number`, with what happened to each message: `dispatched` and
the run that handled it, or `gated`, `filtered`, `rate_limited`, `dropped` with
the reason.

### Who may start the agent, and how often

Every inbound message is a billed agent run, so the number has a policy, set
with `whatsappcloud_set_inbound_policy`:

- **Paused** - messages are kept but start nothing, until you resume.
- **Allowed senders** - only these customer numbers start the agent; others are
  recorded as `filtered`.
- **Runs per hour** - 60 by default. Over the cap a message is recorded as
  `rate_limited`; a sustained burst above three times the cap pauses inbound
  automatically (`auto_paused`) until you resume it.

A message on an empty FlyMyAI balance is recorded as `gated` and starts nothing.
Inbound messages and receipts are kept for 30 days.

### The 24-hour rule

Meta lets a business answer **freely for 24 hours after the customer's last
message**. Outside that window only an approved **template** is delivered, and
templates are what you use to write first. Templates are created with
`whatsappcloud_create_template` and approved by Meta, usually within minutes for
utility templates.

## Delivery statuses

Every send action returns a message id. `whatsappcloud_get_message_status`
returns its history - `sent`, `delivered`, `read`, or `failed` with Meta's
reason - as the receipts arrive.

## Actions

| Action | What it does |
| --- | --- |
| `whatsappcloud_send_text` | Text, optional link preview and reply context. |
| `whatsappcloud_send_template` | An approved template with variable components. |
| `whatsappcloud_send_media` | Image, video, audio, document or sticker by media id or public link. |
| `whatsappcloud_send_interactive` | Reply buttons (up to 3) or a list (up to 10 rows). |
| `whatsappcloud_send_location` | A location pin. |
| `whatsappcloud_send_reaction` | React to a message with an emoji. |
| `whatsappcloud_send_contacts` | One or more contact cards. |
| `whatsappcloud_mark_read` | Blue ticks, optionally with a typing indicator. |
| `whatsappcloud_upload_media` / `get_media` / `delete_media` | Media objects. |
| `whatsappcloud_list_templates` / `create_template` / `delete_template` | Message templates. |
| `whatsappcloud_list_phone_numbers` / `get_phone_number` | The account's numbers. |
| `whatsappcloud_get_business_profile` / `update_business_profile` | The business profile. |
| `whatsappcloud_set_inbound_agent` | Which agent answers customer messages. |
| `whatsappcloud_set_inbound_policy` | Pause/resume inbound, allowed senders, runs per hour. |
| `whatsappcloud_list_inbound_messages` | Recent inbound messages, optionally per customer. |
| `whatsappcloud_get_message_status` | Delivery history of a sent message. |
| `whatsappcloud_register_phone` / `deregister_phone` | Register the number for the API, or release it back to an app. |

The sender number and the business account come from the connection; pass
`phone_number_id` or `waba_id` in a call only to override them.

## Personal vs business

Meta has no API for a personal WhatsApp account, so no connector - here or
anywhere - can read your personal chats or write as you. What exists:

| You want | What it is |
| --- | --- |
| A company number that customers write to and an agent answers | **This connector.** |
| Your own number in the WhatsApp Business *app* on your phone, plus an agent on the same number | Meta's *coexistence* mode: the app keeps working and the API sees new messages. FlyMyAI's Connect flow does not offer it yet - today a number moves to the API entirely. |
| An agent inside your personal WhatsApp | Not supported by Meta. Third-party bridges exist (they mirror WhatsApp Web) but are unofficial and can get a number banned when used for bulk messaging. |

### Moving a number to the API and back

A phone number lives on exactly one of WhatsApp Messenger, the WhatsApp Business
app or the Cloud API at a time. Moving is free in both directions; Meta bills
nothing for the move itself.

**Personal number to this connector.** Meta requires the personal WhatsApp
account on that number to be deleted first (Settings, Account, Delete my
account). Personal chats, groups and backups do not come along, and your
contacts see a business account from then on. Use a spare or company SIM
unless you really mean to give the number up.

**WhatsApp Business app number to this connector.** The same rule: delete the
account in the app first, or wait for coexistence. Either way the app's chat
history is not available to the agent.

**Back to the app.** Run `whatsappcloud_deregister_phone` (or remove the number
in WhatsApp Manager), then register the number in WhatsApp or WhatsApp Business
on the phone with the SMS code. If WhatsApp asks for a two-step verification
PIN, it is the one set when the number was registered on the API - FlyMyAI
keeps it as `WHATSAPPCLOUD_TWO_STEP_PIN` in the connection; a lost PIN is reset
in WhatsApp Manager, Phone numbers, Two-step verification, before you
deregister. Messages exchanged while the number was on the API stay in FlyMyAI
(`whatsappcloud_list_inbound_messages`), not in the app.

**What costs money.** Meta charges per message, not for the API: incoming
messages and your replies inside the 24-hour window are free; template
messages are billed by Meta per message at the recipient country's rate,
directly to the Meta business account. FlyMyAI charges only its normal agent
run and tool-call usage.

## Troubleshooting

- **The agent did not answer a message** - `whatsappcloud_list_inbound_messages`
  shows each message's status and reason: no receiving agent (attach the
  connection or `whatsappcloud_set_inbound_agent`), agent not frozen, inbound
  paused or auto-paused (`whatsappcloud_set_inbound_policy` resumes it), sender
  not allowed, empty balance.
- **Inbound stopped after a flood of messages** - the number auto-paused on a
  burst; resume it with `whatsappcloud_set_inbound_policy` and raise
  `rate_limit_per_hour` if the volume is real.
- **`131047` re-engagement** - more than 24 hours since the customer's last
  message; send a template.
- **`131030` recipient not in allowed list** - a Meta test number can only
  message the numbers added on the API Setup page; add yours or use a real number.
- **`190` invalid token** - a temporary API Setup token expired; reconnect, or
  paste a permanent System User token.
- **`133005`/`133016` registration** - the number has a two-step PIN; run
  `whatsappcloud_register_phone` with it.

## For operators

Connect with Meta needs FlyMyAI's own Meta app on the backend: `META_APP_ID`,
`META_APP_SECRET`, `WHATSAPP_EMBEDDED_SIGNUP_CONFIG_ID` (the Embedded Signup
configuration from **Facebook Login for Business**) and
`WHATSAPP_WEBHOOK_VERIFY_TOKEN`. The app's webhook callback URL is
`{PUBLIC_BACKEND_URL}/api/v1/agents/whatsapp/webhook/`, subscribed to
`messages`. Until the app passes Meta's App Review for
`whatsapp_business_messaging` and `whatsapp_business_management`, only users
with a role on the app can complete the signup; without the variables the
connector shows the manual token setup instead of the Meta button.

## Links

- [Cloud API reference](https://developers.facebook.com/docs/whatsapp/cloud-api)
- [Message templates](https://developers.facebook.com/docs/whatsapp/message-templates)
- [Conversation pricing](https://developers.facebook.com/docs/whatsapp/pricing)
