> ## Documentation Index
> Fetch the complete documentation index at: https://flow.engineer/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Go live

> Connect a dedicated sender, use a live key and check production delivery.

Going live means sending from a **dedicated sender** of your own with a **live key**; your agent's code does not change. The examples below assume `FLOW_MESSAGING_KEY` holds your live key (`fk_live_...`).

```bash curl
export TELEGRAM_BOT_TOKEN=...   # the token from @BotFather; never commit it

curl https://api.flow.engineer/v0/senders \
  -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"channel\": \"telegram\", \"telegram_bot_token\": \"$TELEGRAM_BOT_TOKEN\"}"
```

| Channel  | How you go live today                                                                                                                                                                                                                                                                                                                |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Telegram | Self-serve: connect your own bot with a live key (below).                                                                                                                                                                                                                                                                            |
| iMessage | Flow's shared line works with a live key once your account has it (the free trial your first sign-in at flow\.engineer starts opens it, and a paid plan keeps it). For a line of your own, ask the Flow team (Dedicated plan). Lines reply after the person writes first; writing first is available on request for dedicated lines. |
| WhatsApp | Self-serve: connect a number in your own WhatsApp Business Account with a live key (below), or in the [dashboard](https://api.flow.engineer/admin/senders?mode=live). The Flow team can also connect one for you.                                                                                                                    |

## Checklist

**1. Get a live key**

Sign in to the [dashboard](https://api.flow.engineer/admin) with a Flow account, switch to **Live**, and make an `fk_live_...` key on the [Keys page](https://api.flow.engineer/admin/keys?mode=live) (test keys take one call, see [Keys and sign-in](https://flow.engineer/docs/unified-messaging-api/get-a-key.md)). Keep it on your server only, in its own variable (for example `FLOW_MESSAGING_LIVE_KEY`) so it does not replace your test key. Your own Telegram bot and your own WhatsApp number are self-serve; iMessage lines are arranged with the Flow team.

**2. Get a dedicated sender**

Telegram: `POST /v0/senders` with your bot's token; it is `active` at once. WhatsApp: `POST /v0/senders` with your number's IDs, token and app secret (see [WhatsApp](#your-own-number)); it is `active` at once. iMessage: the Flow team connects a line to your app, and it then appears in `GET /v0/senders` with your live key (`POST /v0/senders` with `"channel": "imessage"` answers `501 not_implemented` with any key; build meanwhile on [simulated conversations](https://flow.engineer/docs/unified-messaging-api/concepts/test-and-live-mode.md) of that channel).

**3. Register your webhook with the live key**

Webhook endpoints and their signing secrets belong to one mode: your test endpoint never receives live events. `POST /v0/webhook_endpoints` again with the live key and store the new `whsec_...` secret where your live deployment reads it; the test endpoint keeps its own secret.

**4. Switch the key**

Set `FLOW_MESSAGING_KEY` to the live key and deploy. Watch `message.failed` and `sender.status_changed` for the first days.

## WhatsApp

### Your own number

Connect a number in your own WhatsApp Business Account, under your own Meta app, with a live key: `POST /v0/senders`, or the [Senders page](https://api.flow.engineer/admin/senders?mode=live) of the dashboard. The Flow team can also connect a number for you.

First, in Meta Business Manager:

1. Create a Meta app of type Business with the WhatsApp product, and switch it to Live.
2. Add the phone number to your WhatsApp Business Account, with an approved display name, and add a payment method in WhatsApp Manager (Meta bills its fees to you directly).
3. Create a system user (Admin), assign it your WhatsApp Business Account (full control) and the app, and generate a token that never expires with `whatsapp_business_messaging` and `whatsapp_business_management`.

Then connect the number. Keep the token and the app secret (App settings > Basic) in environment variables, never in code or a chat:

```bash curl
export WHATSAPP_ACCESS_TOKEN=...   # the system user token; never commit it
export WHATSAPP_APP_SECRET=...     # your Meta app's App secret

curl https://api.flow.engineer/v0/senders \
  -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"channel\": \"whatsapp\", \"whatsapp_phone_number_id\": \"106540352242922\", \"whatsapp_business_account_id\": \"102290129340398\", \"whatsapp_access_token\": \"$WHATSAPP_ACCESS_TOKEN\", \"whatsapp_app_secret\": \"$WHATSAPP_APP_SECRET\"}"
```

Use your number's phone number ID (WhatsApp Manager, or App Dashboard > WhatsApp > API Setup), not the phone number itself. Add `whatsapp_registration_pin` (six digits) only when the number is not registered for the Cloud API yet, and `display_name` if you like.

* **Answer:** `200` with the sender `active` and, only in this answer, `setup`: enter its `callback_url` and `verify_token` in your Meta app (App Dashboard > WhatsApp > Configuration) and subscribe the `webhook_fields` there, following its `steps`. Flow has already pointed this number's webhooks at Flow (your other numbers keep theirs); the settings in your app make its field subscriptions apply. The verify token is shown only in this answer.
* **New token:** send the number again the same way. It stays the same sender, with the same verify token.
* **Disconnect:** `DELETE /v0/senders/{sender_id}` deletes the stored token and app secret and retires the sender. Nothing changes in your Meta app: remove Flow's callback URL there yourself.
* **Numbers the Flow team connected** are updated and disconnected by the Flow team (`403 permission` with `channel_code` `whatsapp_number_managed`, and `501 not_implemented` on `DELETE`).

### Numbers, quality and fees

* **Numbers:** a number in your business's own WhatsApp Business Account, or a number Flow provides.
* **Quality and tiers:** the sender shows WhatsApp's `quality_rating` (`green`, `yellow`, `red`) and its messaging tier in `limits.whatsapp_tier`. Replies to people who wrote first are not limited by the tier.
* **Fees:** on a number Flow provides, WhatsApp's own per-message fees (charged by Meta) are passed through. With your own WhatsApp Business Account, Meta bills its fees to your business directly, so add a payment method in WhatsApp Manager. See [Pricing](https://flow.engineer/docs/unified-messaging-api/pricing.md).

### WhatsApp templates

A template is a message WhatsApp approved in advance. On WhatsApp you need one to message someone first, or to write more than 24 hours after their last message. Templates are created on your WhatsApp number, with a realistic sample for each placeholder:

```bash curl
curl https://api.flow.engineer/v0/templates \
  -H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
    "name": "order_ready",
    "language": "en",
    "category": "utility",
    "components": [
      { "type": "body", "text": "Hi {{1}}, your {{2}} is ready for pickup.", "examples": ["Asha", "cake"] },
      { "type": "buttons", "buttons": [
        { "type": "url", "text": "See order", "url": "https://shop.example.com/orders/{{1}}", "example": "A-1042" }
      ] }
    ]
  }'
```

The answer is `201` with the template, `pending` until WhatsApp reviews it, its components as you sent them:

```json
{
  "id": "tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
  "sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
  "name": "order_ready",
  "language": "en",
  "category": "utility",
  "status": "pending",
  "components": [
    { "type": "body", "text": "Hi {{1}}, your {{2}} is ready for pickup.", "examples": ["Asha", "cake"] },
    { "type": "buttons", "buttons": [
      { "type": "url", "text": "See order", "url": "https://shop.example.com/orders/{{1}}", "example": "A-1042" }
    ] }
  ],
  "created_at": "2026-10-24T09:30:00Z"
}
```

* **Always give samples.** WhatsApp reviews a template with them. Put `examples` on the body and on a text header, one value per placeholder in order (`{{1}}` first), and `example` on a `url` button whose link ends in `{{1}}`: the end of the link, which Flow shows WhatsApp in place. Left out, Flow sends placeholder samples (`x1`, `x2`, ...), which WhatsApp often rejects or moves to another category.
* **Sample limits:** exactly one per placeholder; 1 to 60 characters in a header and 1 to 1024 in a body; no line breaks, tabs or more than four spaces in a row. The url sample has no spaces, and the link with it in place is at most 2000 characters.
* **Placeholders** are numbered `{{1}}`, `{{2}}`, ... with none skipped. A header has at most one, a footer none, and a url button's link only `{{1}}`, at its end. A template that breaks these rules or the sample limits gets `400 invalid_request`, and so do samples on a footer, a buttons part, a media header or text without placeholders.
* Names use lowercase letters, digits and underscores. A name and language pair is unique per sender.
* `category` is `utility` (updates about something the person asked for), `marketing` or `authentication`; it sets WhatsApp's fee.
* You receive `template.status_changed` when WhatsApp approves, rejects (with `rejection_reason`) or pauses it.
* In the dashboard, open your WhatsApp number on the [Senders page](https://api.flow.engineer/admin/senders?mode=live), then **Templates**: its New template form always sends its sample values.
* Send it with [`POST /v0/conversations/{conversation_id}/template`](https://flow.engineer/docs/api-reference/messages/send-a-whatsapp-template) (or start a conversation with `POST /v0/messages` and `"kind": "template"`), with values for its placeholders, the url button's by its position (from 0):

```json
{ "template_id": "tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "language": "en", "params": { "body": ["Asha", "cake"], "buttons": [{ "index": 0, "value": "A-1042" }] } }
```

## iMessage: a dedicated line

* **Get one:** ask the Flow team for a line on the Dedicated plan ([Pricing](https://flow.engineer/docs/unified-messaging-api/pricing.md)). They connect it to your app; it is listed in `GET /v0/senders?channel=imessage` with your live key. Flow's shared line needs no setup: once your account has it (the free trial, kept on a paid plan), a live key sees it too, with your app's live join message.
* **The person writes first:** starting a conversation with a new contact is refused with `403 permission`, unless writing first is switched on for the line (`imessage.can_initiate`). Writing first is available on request for dedicated lines, within a warm-up.
* **Add-ons on a dedicated line**, arranged with the Flow team: SMS and RCS from the same number (for contacts who cannot get iMessage), and forwarding of phone calls. Group chats people add the line to reach your app as `group` conversations.
* **Get people to write:** the sender's `address.link` is the line's opt-in link (when it has one): it opens Messages with the line and a prefilled text. Publish it on your site, receipts or QR codes. See [Build an iMessage agent](https://flow.engineer/docs/unified-messaging-api/guides/imessage-agent.md).

## Telegram: your own bot

Create a bot with **@BotFather** and send its token: `POST /v0/senders` with `channel: "telegram"` and `telegram_bot_token`. It is connected and `active` at once. See [Build a Telegram agent](https://flow.engineer/docs/unified-messaging-api/guides/telegram-agent.md#2-your-own-telegram-bot).

* **New token:** after revoking a token in @BotFather, send the new one the same way. The bot stays the same sender; one Telegram rejected meanwhile is `flagged` until you do. Messages queued while it is flagged wait at most 72 hours, then fail with `outside_window` (`channel_code` `queued_too_long`).
* **Disconnect:** `DELETE /v0/senders/{sender_id}` removes the bot's webhook and token and retires the sender.
* **One app per bot:** connecting a bot to another app moves it there and retires the old sender.

## Before you launch

* Handle `outside_window`, `new_contact_limit` and `sender_throttled` (see [Errors](https://flow.engineer/docs/unified-messaging-api/errors/outside_window.md)). Know the [sending limits and abuse signals](https://flow.engineer/docs/unified-messaging-api/concepts/send-gate.md#rule-3-abuse-protection): the same first message to many new contacts throttles a sender for 24 hours.
* Deduplicate webhook deliveries on the event `id`, and use event IDs as idempotency keys.
* Subscribe your webhook only to the event types you use.
* Message content and media are kept for your plan's retention period (30 days by default); store anything you need longer yourself.

## Related

* [Senders](https://flow.engineer/docs/unified-messaging-api/concepts/senders.md) and [Test and live mode](https://flow.engineer/docs/unified-messaging-api/concepts/test-and-live-mode.md)
* API: [Request a dedicated sender](https://flow.engineer/docs/api-reference/senders/request-a-dedicated-sender.md), [Create a template](https://flow.engineer/docs/api-reference/templates/create-a-template.md)
