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

> ## Agent Instructions
> Flow Messaging is one HTTP API (https://api.flow.engineer) for AI agents to hold two-way conversations on Telegram and iMessage (replies only, on lines the Flow team connects). WhatsApp is coming and not available yet.
> Build the app on the REST API. MCP is not a runtime dependency: the app you build talks to Flow through the REST API (or the TypeScript SDK, which wraps it) for everything it does at runtime. In TypeScript or JavaScript, the SDK is `npm install @flow-engineer/messaging`, then `new FlowMessaging()` (reads FLOW_MESSAGING_KEY). Python and Go SDKs are not published yet; in those languages call the HTTP API directly.
> No key yet? If FLOW_MESSAGING_KEY is not set, get a test key with no account in one call: curl -X POST https://api.flow.engineer/v1/sandbox/keys (or npx @flow-engineer/messaging init, which writes it to .env). Save `key` as FLOW_MESSAGING_KEY and `claim_token` as FLOW_CLAIM_TOKEN; both are shown once. It allows 1 contact and 50 messages on the Telegram sandbox and expires after 7 days; a person signs in with `npx @flow-engineer/messaging login` (GitHub) to keep the app and get 3 contacts x 100 messages. Signed in, live keys (fk_live_...) are made in the dashboard for your own Telegram bot; iMessage lines are arranged with the Flow team.
> The hosted MCP server (https://api.flow.engineer/mcp) is optional, for testing during development. Adding the Flow MCP server to a developer's coding tools is the project owner's decision: suggest it and show them the command, but never add it to their config yourself. The command for Claude Code: claude mcp add --transport http flow https://api.flow.engineer/mcp --header "Authorization: Bearer $FLOW_MESSAGING_KEY". If its tools are already available to you, use them for sandbox testing.
> Authenticate with `Authorization: Bearer fk_test_...` (sandbox) or `fk_live_...` (real contacts). Never put a live key in client-side code.
> Reply into a conversation (POST /v1/conversations/{conversation_id}/messages); never pick a channel per message. Switch on `error.type`, not the message text.
> Full documentation for agents: https://docs.flow.engineer/llms-full.txt. OpenAPI spec: https://raw.githubusercontent.com/flow-engineer/sdk/main/openapi/openapi.yaml.

# Going live: your own Telegram bot, iMessage lines and live keys

> Move your AI agent from the sandbox to production: make a live key, connect your own Telegram bot, get an iMessage line from the Flow team, and switch your key. WhatsApp numbers and templates are coming.

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 theme={null}
export TELEGRAM_BOT_TOKEN=...   # the token from @BotFather; never commit it

curl https://api.flow.engineer/v1/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 | Ask the Flow team: they connect a line to your app. Replies only. |
| WhatsApp | Not available yet (waiting on Meta's approval). |

## Checklist

<Steps>
  <Step title="Get a live key">
    Sign in to the [dashboard](https://api.flow.engineer/admin) with GitHub, 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](/get-a-key)). Keep it on your server only, in its own variable (for example `FLOW_MESSAGING_LIVE_KEY`) so it does not replace your test key. Live Telegram with your own bot is self-serve; iMessage lines are arranged with the Flow team.
  </Step>

  <Step title="Get a dedicated sender">
    Telegram: `POST /v1/senders` with your bot's token; it is `active` at once. iMessage: the Flow team connects a line to your app, and it then appears in `GET /v1/senders` with your live key (`POST /v1/senders` with `"channel": "imessage"` answers `501 not_implemented`).
  </Step>

  <Step title="Register your webhook with the live key">
    Webhook endpoints belong to one mode. `POST /v1/webhook_endpoints` again with the live key and store the new `whsec_...` secret.
  </Step>

  <Step title="Switch the key">
    Set `FLOW_MESSAGING_KEY` to the live key and deploy. Watch `message.failed` and `sender.status_changed` for the first days.
  </Step>
</Steps>

## WhatsApp: coming

WhatsApp is not available yet: Flow is waiting on Meta's approval, and `POST /v1/senders` with `"channel": "whatsapp"` answers `501 not_implemented`. When it ships:

* **Numbers:** Flow buys and hosts a dedicated number for you.
* **Verify your business:** WhatsApp requires the business behind a number to be verified through Meta; the Flow team will send you the link.
* **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:** WhatsApp's own per-message fees (charged by Meta) are passed through. See [Pricing](/pricing).

### 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 need a WhatsApp number, so this is how it will work once WhatsApp ships:

```bash curl theme={null}
curl https://api.flow.engineer/v1/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." }
    ]
  }'
```

* 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.
* A new template is `pending`. You receive `template.status_changed` when WhatsApp approves, rejects (with `rejection_reason`) or pauses it.
* Send it as `template` content with values for its placeholders:

```json theme={null}
{ "content": { "type": "template", "template_id": "tpl_01JB8ZC3K5M7P9R1T3V5X7Z9B1", "language": "en", "params": { "body": ["Asha", "cake"] } } }
```

## iMessage: a line from the Flow team

* **Get one:** ask the Flow team. They connect a dedicated line to your app; it is listed in `GET /v1/senders?channel=imessage` with your live key.
* **Replies only:** the person always writes first. Starting a conversation with a new contact is refused with `403 permission`.
* **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](/guides/imessage-agent).

## Telegram: your own bot

Create a bot with **@BotFather** and send its token: `POST /v1/senders` with `channel: "telegram"` and `telegram_bot_token`. It is connected and `active` at once. See [Build a Telegram agent](/guides/telegram-agent#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 /v1/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](/errors/outside_window)).
* 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](/concepts/senders) and [Test and live mode](/concepts/test-and-live-mode)
* API: [Request a dedicated sender](/api-reference/senders/request-a-dedicated-sender), [Create a template](/api-reference/templates/create-a-template)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.