> ## 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.

# permission

> The key may not do this.

# permission

HTTP 403. The key may not do this.

## What it means

The key is valid but this action is not allowed for it.

## Why it happens

* A test key (`fk_test_`) used a dedicated sender, or a live key (`fk_live_`) used a shared sandbox sender.
* On the sandbox, the contact has not joined your app (or has since joined another app by sending its join code).
* The sender is pending, flagged or banned. A Telegram bot is flagged when Telegram rejected its token (revoked in @BotFather), and banned once disconnected or connected to another app.
* A dedicated sender was requested or disconnected with a test key, or the Telegram bot you connected is one of Flow's sandbox senders.
* On iMessage, you started a new conversation from a line that may only reply. The contact must message the line first.
* The app's sandbox allowance is used up (`channel_code` `sandbox_allowance_used`). Apps made with `POST /v1/sandbox/keys` may send 50 messages in total to 1 contact; people who signed in, 100 messages to each of 3 contacts, one allowance per person shared by every app they own or claim. Only messages your agent sends count.
* The allowance has no room for this contact (`sandbox_contact_limit`), or the send used a sandbox channel the allowance does not cover, such as iMessage (`sandbox_channel_not_included`; iMessage lines are arranged with the Flow team and used with a live key).
* An app made without an account tried something that needs a person signed in, such as connecting its own Telegram bot, or uploading more than 20 files in a day (`sign_in_required`).

## How to fix it

Use the key of the right mode. On the sandbox, have the contact join your app: on Telegram they open the sandbox sender's link (its `address.link`) and tap **Start**, which joins them; if the link can't be used, they send the bot its `join_code` (for example `join wild-otter-04508705`). The MCP tool `sandbox_join` gives the link (and a QR code with `include_qr: true`). Then send again.

If Telegram rejected your bot's token, get a new token from @BotFather (`/mybots`, API Token) and connect the bot again with `POST /v1/senders` and your live key: the same sender becomes `active`, and its queued messages go out (those queued for more than 72 hours fail with `outside_window`, `channel_code` `queued_too_long`, instead of going out late).

When the sandbox allowance is used up, or the app was made without an account, have a person sign in with GitHub to claim the app: run `npx @flow-engineer/messaging login` (it uses the `claim_token` saved by `init`), or start the device flow yourself with `POST /v1/device/authorizations` and your `claim_token`, show the person the link, and poll `POST /v1/device/token` for the new key. Claiming keeps the app and moves it under the person's signed-in allowance: 3 contacts and 100 messages each, one allowance per person shared by all their apps (a person may claim up to 10 apps). `GET /v1/app` shows what is left (`allowance`).

```bash theme={null}
curl -X POST https://api.flow.engineer/v1/device/authorizations \
  -H "Content-Type: application/json" -d '{"claim_token": "'"$FLOW_CLAIM_TOKEN"'"}'
# Show verification_uri_complete to the person, then poll every `interval` seconds:
curl -X POST https://api.flow.engineer/v1/device/token \
  -H "Content-Type: application/json" -d '{"device_code": "fdc_..."}'
```

On an iMessage line that may only reply, wait for the contact to message it, then reply in that conversation (`POST /v1/conversations/{conversation_id}/messages`).

```bash theme={null}
# Your join code (app.sandbox_join_code) and the sandbox senders (address.link, join_code)
curl https://api.flow.engineer/v1/app -H "Authorization: Bearer $FLOW_MESSAGING_KEY"
curl https://api.flow.engineer/v1/senders -H "Authorization: Bearer $FLOW_MESSAGING_KEY"
```

Every error also carries `hint`, one sentence specific to your request, and
`doc_url`, this page. Coding agents connected to Flow's MCP server
(`https://api.flow.engineer/mcp`) can call `explain_error` with the type to read this page.


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