Skip to content

Ask the docs

Answers come only from these docs and are written by an AI model (Gemini), so check the linked pages. Your question is not kept. Do not include personal details or keys.

Conversations and contacts

Conversations · conversation API for AI agents · WhatsApp conversation API · multi-channel messaging API · omnichannel AI agent

Reply to a conversation without selecting a channel for each message.

A conversation is one sender talking with one contact; your agent always replies into a conversation, so the channel is already decided.

TypeScript
// From an event: the conversation is attached and ready to use.
await event.conversation.reply("Thanks! Your order is on its way.");
// From an ID you stored earlier:
await flow.conversation("conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1").send("Your order has shipped.");
curl
curl https://api.flow.engineer/v0/conversations/conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1/basic \
-H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Your order has shipped."}'
ObjectIDWhat it is
Sendersnd_What your agent talks from: a Telegram bot, an iMessage line or a WhatsApp number. See Senders.
Contactct_A person on one channel: a phone number, a WhatsApp username, a Telegram user or an iMessage handle.
Conversationconv_One sender with one contact (type direct), or with an iMessage group chat (type group). Holds the window state and whether the contact opted in.
Messagemsg_One message in or out, of one kind (content.kind), with a status.
Eventevt_One entry in your app’s ordered log. See Events and webhooks.

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

  • 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.
{
"id": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"type": "direct",
"channel": "telegram",
"sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"contact": "ct_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"livemode": true,
"last_inbound_at": "2026-11-03T10:15:00Z",
"opted_in": true,
"created_at": "2026-11-01T08:00:00Z"
}
  • window_open_until (WhatsApp only; 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.
  • type: direct (one contact, in contact) or group. Read a missing type as direct.
  • group (group conversations only): the group chat’s name, when it has one, and its participants, each a contact. A group has no contact; each inbound message’s from says who wrote. Group chats reach your app only from dedicated iMessage lines people add to them, and the API cannot create a group or change who is in it. See iMessage.
  • opted_in: the contact wrote first, joined the sandbox, or you recorded their consent. Always true on groups.
  • metadata: up to 20 string pairs of your own (for example your user ID), kept with the conversation.

Most conversations start when the person writes first. To write first, use POST /v0/messages: a basic or template message, with kind saying which, plus a sender and an address on that sender’s channel. On Telegram that works only for people who already started your bot. iMessage lines reply only unless writing first is switched on for the line (imessage.can_initiate, available on request for dedicated lines). A start always makes a direct conversation.

curl
curl https://api.flow.engineer/v0/messages \
-H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender": "snd_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"to": { "telegram_user_id": "123456789" },
"kind": "basic",
"text": "Your order has shipped."
}'

Any other kind answers 422 unsupported_content with param kind: start with a basic message or a template, then send the other kinds into the conversation. 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.