Skip to main content
A conversation is one sender talking with one contact; your agent always replies into a conversation, so the channel is already decided.
TypeScript
curl

The objects

IDs are a prefix plus a ULID, so they sort by creation time.

One channel per conversation

  • There is no cross-channel identity. The same person on Telegram and on WhatsApp is two contacts and two conversations. Flow never guesses that they are the same person, and never moves a conversation to another channel.
  • Never assume a phone number. A Telegram contact has a user ID, a WhatsApp contact may have only a username, and an iMessage handle may be an email address. Read contact.address for what the channel gives.
  • Reply where they wrote. Each event carries conversation.channel, so one agent can serve every channel with the same code.

What a conversation tells you

  • window_open_until (WhatsApp only, once available; absent on Telegram and iMessage): until when free-form messages may be sent. After it, only a template. Every event repeats it in conversation.window_open_until.
  • opted_in: the contact wrote first, joined the sandbox, or you recorded their consent.
  • metadata: up to 20 string pairs of your own (for example your user ID), kept with the conversation.

Starting a conversation

Most conversations start when the person writes first. To write first, use POST /v1/messages with a sender and an address on that sender’s channel. On Telegram that works only for people who already started your bot; Flow’s iMessage lines are reply-only, so they cannot write first.
curl
to takes exactly one of contact, phone, telegram_user_id or handle. Starting a conversation spends the sender’s new-contact budget, and each channel has its own rule for who may be messaged first. See The send gate.