1. Get a key in one call
First check whether you already have one: ifFLOW_MESSAGING_KEY is set (in the environment or .env), use it and skip this step.
Example key response
Example key response
The answer (
201, a SandboxKey, shortened here) is a new app of its own:keyandclaim_tokenare shown once. SavekeyasFLOW_MESSAGING_KEYandclaim_tokenasFLOW_CLAIM_TOKEN, in the environment or a git-ignored.env. The claim token (andclaim_url, which holds it) lets a person claim the app, so treat it like the key.- Join from a phone: open a sender’s
address.link(on Telegram, tap Start), or send it thejoin_code(the whole message, as it is). - Don’t call it again when you already have a key. Calls are limited per client address and network; going over answers
429 rate_limitedwithretry_after.
init does all of this: with no key in the environment or .env, it gets one itself, writes FLOW_MESSAGING_KEY and FLOW_CLAIM_TOKEN to .env, and prints the sandbox link and join code, the allowance and the expiry. To use a key you already have, pass init --key fk_test_....
2. The sandbox allowance
Test keys send only on Flow’s shared sandbox senders, and only to people who joined your app there. What an app may send there for free:- Only messages your agent sends count. Inbound messages are free.
- A contact counts once it joins your app, and keeps counting after it leaves.
- iMessage is not part of either allowance. Flow’s shared iMessage line comes with the account’s free trial: your first sign-in at flow.engineer (the dashboard), or the first key you make there, starts it, and the Flow CLI’s
npx @flow-engineer/flow setupstarts the same trial. It has room for up to 25 people per channel, with test and live keys, and your own account’s apps use it instead of the signed-in allowance (see Pricing). Dedicated lines are arranged with the Flow team. - The WhatsApp sandbox is not open yet; when it opens, the allowance covers it.
- When a key from
POST /v0/sandbox/keysexpires, its keys stop working and its contact is removed from the sandbox. A person can still sign in and claim the app. - Going live: signed in, you make
fk_live_keys in the dashboard (Keys, Live) and connect your own Telegram bot or WhatsApp number; dedicated iMessage lines are arranged with the Flow team. See Going live.
GET /v0/app (shortened):
(await flow.app.retrieve()).allowance.
3. Sign in to keep the app
Sign in with your Flow account to claim the app and keep its data. The signed-in allowance is shared across your apps: three contacts, 100 messages each, with up to ten claimed apps.- CLI or device flow: the new key replaces the sandbox key, which stops working immediately.
- Browser claim: no new key is returned. The old key is revoked unless you select Keep my agent’s current key working.
- Kept keys: expiry is removed. An expired key can work again if the app is claimed within 30 days of expiry. For a revoked key, create a replacement in the dashboard.
With the CLI
FLOW_CLAIM_TOKEN from .env, prints a link and a short code (for example WDJB-MJHT), opens the browser and waits. Once the person approves, it replaces FLOW_MESSAGING_KEY in .env with the new key and removes FLOW_CLAIM_TOKEN. --no-browser skips opening the browser.
Coding agents that cannot wait on a command: run npx @flow-engineer/messaging login --no-wait. It prints the link and code and exits. Show them to the person, and once they say they approved, run npx @flow-engineer/messaging login again to collect the key.
With curl
-
Start the sign-in with the claim token.
client_nameis shown to the person on the approval page.Without a claim token, send the app’s test key instead (-H "Authorization: Bearer $FLOW_MESSAGING_KEY"). -
Show the person
verification_urianduser_code: they open the page, sign in and type the code you show them (a link alone never approves;verification_uri_completeopens the same page). Never showdevice_code. -
Poll every
intervalseconds:pending: waitintervalseconds and poll again.429 rate_limitedmeans slow down: waitretry_afterseconds.approved: the answer holdskey(shown once). Save it asFLOW_MESSAGING_KEYin place of the sandbox key, and dropFLOW_CLAIM_TOKEN.denied: the person refused. Stop.expired: the codes ran out (after 15 minutes) or were already used. Start again.
With TypeScript
signIn polls for you, honouring interval and retry_after. For your own loop, use flow.device.authorize({ claimToken, clientName }) and flow.device.poll(deviceCode).
In a browser
claim_url opens a page where the person signs in and claims the app without the CLI.
The dashboard
Dashboard and account linking
Dashboard and account linking
Signed-in people manage their apps and keys at api.flow.engineer/admin (sign in with a Flow account), including creating and revoking test and live keys. A Flow account is the one you use on flow.engineer: sign in there with Google, GitHub or an email link. flow.engineer/login is the only sign-in page: the dashboard and the device flow send you there and bring you back, at once when you are already signed in on flow.engineer. If you used the dashboard with GitHub before Flow accounts, sign in once on flow.engineer with the same email and ask the Flow team to link the two, so you keep your apps and keys; accounts are never linked by email on their own.
Errors
Allowance and sign-in errors
Allowance and sign-in errors
Switch on
error.type and read error.channel_code; each error also carries a hint and a doc_url (permission, authentication, rate_limited).