Choose a channel
Buttons, lists, templates and your own sender.
iMessage
Text, photos and tapbacks on an iMessage line.
Telegram
Try a bot on your phone, then connect your own.
SMS
Plain-text conversations through your sender.
Web & app chat
Keep your UI and connect Flow’s conversation runtime.
Try it on your phone
1
Connect a channel
From the starter project, run:
link signs you in, sets up the folder and prints a phone link. If a channel is not open
yet, it tells you which ones are available.2
Start the agent
Terminal
link changes keys, senders or the display name. The starter reads .env at
startup and connects through an outbound event stream, so your laptop needs no public URL
or tunnel.3
Send the first message
Open the phone link and write to the agent. WhatsApp and iMessage trial channels reply after
you write first.Check it worked: ask “What’s on the menu?” and look for the
get_menu call in the local
playground’s Debug view.Choose a provider
The Flow Messaging adapter
covers its senders and connection setup.
Flow Framework 0.7.0 moves the adapter to Flow Messaging’s V0 API (
/v0, one endpoint per
message kind). Upgrade @flow-engineer/flow; your agent code does not change. Earlier versions
call the older /v1 API, which no longer takes sends for most apps. If your own code also calls
Flow Messaging, move it with the
migration skill.Adapt your own provider
Adapt your own provider
Define the channel using callbacks to your provider. In this fragment, replace Pass these definitions in
waClient and
smsClient with the API clients you use:Channel definitions
createAgent({ channels: [wa, text], ... }). Your webhook adapter
supplies incoming messages with agent.message({ channel, from, message, messageId }).Providers can also expose carousels, typing, read receipts and other optional capabilities.
Flow offers the model only the features the connected provider supports.How a turn works
The agent gets a send tool for each channel and answers where the person last wrote. The
playground lets you exercise this behavior without sending to a real provider.
Channel formatting and validation
WhatsApp can use buttons, lists, replies, reactions and declared templates. SMS uses plain text segments. Your custom UI can define additional fields. Content is checked before sending. If a message does not fit the platform’s limits, the agent receives the refusal and can send again. The playground and hosted tests apply the same limits.Reply button ids
Use labels for simple buttons, or{ id, label } when taps must be distinguished.
Button values
choice: { id, title }. Ids are 1–64 bytes and must be unique within
the message and across carousel cards. Labels can repeat. Check the channel’s info.buttonIds
before relying on ids: some providers return only a label.
Use beforeSend for your own final-message checks. Its replacement is what gets validated,
sent and recorded. Hook reference
One person across messaging channels
+1 555 010 0100 and
15550100100 identify the same phone.
For other interfaces, supply resolve to map the verified channel identity to your account
key. The tool receives that key as context.key and a stable identity as context.person.
National phone numbers and identity formats
National phone numbers and identity formats
Set
defaultCountryCode: "+91" if a provider supplies national numbers such as
098765 43210. On phone channels other than WhatsApp, the configured country code is applied
and the national trunk zero is dropped. Without it, bare digits are read as international.context.person is key:<resolved key>, else phone:<E.164>, else <channel>:<address>.
context.key is the exact resolved account key or null. Flow does not guess that an app
user and a phone number are the same account.What the agent knows about the person
Keep account lookups inprofile. Flow exposes them through get_user_profile, which the
agent calls when needed.
In this configuration fragment, members is your application’s account service:
Account profile
get_user_profile name takes precedence. In scenario tests,
the profile is simulated from the test world rather than read from your real backend.
Use context(conversationId) only for small facts at hand: it runs before every turn, so a slow
lookup there delays every reply. Shared facts such as opening hours and policies can live in
the agent’s instructions.
Time zones
The agent sees the person’s time when their zone is known. Otherwise it sees the business’stimeZone, marked as the business’s zone. It never assumes the two are the same.
Set a known person’s zone from your backend:
Set a time zone
{ status: "set" }, or { status: "refused", reason: "unknown_person" } for a
person who has not written yet. An invalid IANA zone throws.
The person can also name their zone in the conversation. A reminder “at 9 tomorrow” requires
one; “in 30 minutes” does not. A no-contact rule “until Friday” uses the latest possible end of
Friday, UTC−12, so it does not lift early.
Reminders and nudges
Reach out with quiet hours, opt-outs and templates.
Keep conversations durable
Store state and handle restarts.