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_codesandbox_allowance_used). Apps made withPOST /v1/sandbox/keysmay 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 (itsaddress.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).
POST /v1/conversations/{conversation_id}/messages).
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.