Skip to main content
Let someone text your product the way they’d text a person: a short request, another detail, then “actually, make that tomorrow.” They shouldn’t have to learn a command language or wait for a slow lookup before they can say anything else. Flow runs that conversation: it reads incoming messages, calls your tools and replies using each channel’s formatting and delivery rules. Its turn handling accounts for double texts, corrections and work that takes longer than a quick reply. Connect through Flow Messaging or bring your own provider.

Choose a channel

WhatsApp

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
Restart after 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.
The trial uses shared test numbers. A join code binds your phone to your app; another app’s code moves it. Use your own channel for real customer data.

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.
Define the channel using callbacks to your provider. In this fragment, replace waClient and smsClient with the API clients you use:
Channel definitions
Pass these definitions in 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

Quick messages fold into one turn; a new message during a tool call lets the agent respond while that tool finishes in the background.
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
A tap is delivered as 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

SMS and WhatsApp from the same phone, and an app identity resolved to the same account, reach one conversation.
SMS and WhatsApp numbers are normalized to E.164. For example, +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.
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 in profile. 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
A tool you define with the same 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’s timeZone, 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
This returns { 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.