Skip to main content
POST

Authorizations

Authorization
string
header
required

An API key of one app, sent as Authorization: Bearer <key>. Keys start with fk_test_ (test mode: sandbox senders and test data only) or fk_live_ (live mode). Keep live keys on your server; never ship them in an app or page.

Headers

Idempotency-Key
string

A unique string (up to 255 characters) that makes this request safe to retry. A repeat with the same key within 24 hours returns the first answer instead of acting again. See "Idempotency" in the introduction.

Required string length: 1 - 255
Flow-Version
string<date>

The API version to use, as a date. Without it, the version pinned to your app when it was created is used.

Example:

"2026-11-01"

Path Parameters

conversation_id
string
required

The conversation's ID. A conversation ID, conv_ and a ULID.

Pattern: ^conv_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"

Body

application/json

Text, or one media file with an optional caption, in either direction: the kind every channel takes (POST /v0/conversations/{conversation_id}/basic). Give text, media, or both (then text is the caption). A field not listed here is refused with 400 invalid_request naming it in param; a field or channel block the conversation's channel does not take answers 422 unsupported_content naming it.

Received from a contact: their text and/or one file. Inbound media always has url (a Flow file URL, GET /v0/files/{file_id}) and file_id, on every channel (an SMS contact's MMS files included), and mime_type and size_bytes once Flow kept the file; send a received file again by its file_id. On SMS Flow always keeps its own copy (the provider's is deleted): an MMS file it could not fetch or check arrives as file_blocked instead.

kind
string
default:basic

Always basic. Optional on POST /v0/conversations/{conversation_id}/basic; required where no URL names the kind (webhook replies, the stream, starts, a message's content).

Allowed value: "basic"
text
string

The text, sent exactly as written: no markdown, no stripping. At most 4096 UTF-16 code units on Telegram (an emoji such as 😀 counts as 2), 4096 characters on WhatsApp, 9999 on iMessage, and 10 segments on SMS (see SmsSegments). With media it is the caption, at most 1024 (UTF-16 code units on Telegram). Optional when media is given. Longer text is refused with 400 invalid_request.

Required string length: 1 - 9999
media
object

One image, video, document or audio file. When sending, give exactly one of url (an HTTPS URL Flow fetches; Flow neither stores nor scans it, and on Telegram a document by URL works for PDF and ZIP files) or file_id (from POST /v0/files). Each channel takes files up to its own size: Telegram 50 MB, WhatsApp and iMessage 100 MB, SMS (MMS) 5 MB; a larger file answers 422 unsupported_content with param media. SMS carries images, audio and video, never documents (param media.type), and only from numbers with MMS (channel_code no_mms).

reply_to
string

Send this as an inline reply to an earlier message in the same conversation (Telegram, WhatsApp and iMessage show it quoted). SMS has no inline replies: there it answers 422 unsupported_content with param reply_to. Refused with 404 not_found (param reply_to) when the target is not a message in this conversation, and with 400 invalid_request (param reply_to) when the target never reached the channel (for example, it failed). If the target fails after the send was accepted, the reply goes out as a normal message.

Pattern: ^msg_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"

Whether links in the text show a rich preview. Left out, the channel's default applies (Telegram, WhatsApp and iMessage show previews). false sends links as plain text. SMS never shows previews, and no channel shows one on a media message: false is accepted there, and true on SMS or with media answers 422 unsupported_content with param link_preview.

telegram
object

Telegram only: how the message looks and notifies on Telegram. On another channel the block answers 422 unsupported_content with param telegram; an unknown key 400 invalid_request.

imessage
object

iMessage only: styles and a subject line. On another channel the block answers 422 unsupported_content with param imessage; an unknown key 400 invalid_request. A message that goes by SMS or RCS (see Message.network) shows neither.

metadata
object

Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.

Response

The message passed the gate and is queued for the channel.

One message in or out of a conversation, with its kind and content and its delivery status. What you send is what the contact gets: Flow never converts a message into another kind.

id
string
required

A message ID, msg_ and a ULID.

Pattern: ^msg_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"

conversation
string
required

A conversation ID, conv_ and a ULID.

Pattern: ^conv_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"

direction
enum<string>
required

in from the contact, out from your app.

Available options:
in,
out
status
enum<string>
required
  • received: inbound from the contact.
  • queued: accepted by the gate, waiting for its turn in the conversation.
  • sent: the channel accepted it.
  • delivered: it reached the contact's device, where the channel reports this.
  • read: the contact read it, where the channel reports this.
  • failed: it could not be sent; see error.
  • unsent: you unsent it.
Available options:
received,
queued,
sent,
delivered,
read,
failed,
unsent
content
object
required

A message's content: one kind object, in either direction, with kind (always present here) saying which. It is exactly what was sent or received, except metadata, which is kept once, at the message's own metadata, and not repeated here.

livemode
boolean
required

Whether the message belongs to live mode.

created_at
string<date-time>
required

When Flow received (inbound) or accepted (outbound) the message.

from
string
read-only

Inbound messages only, on every channel. The contact who wrote it; in a group conversation, which participant. Absent on your own messages.

Pattern: ^ct_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"ct_01JB8ZB2J4K6N8Q0S2V4W6Y8A0"

network
enum<string>
read-only

iMessage channel only: the network that carried the message. imessage, or sms or rcs when the line has the SMS/RCS add-on (Sender.imessage.fallback) and the contact could not get iMessage. The network is chosen for each message when it is sent, never by your app. Set on inbound messages, and on your messages once the channel reports it (in message.sent, message.delivered or message.failed, as data.message.network); absent before that and on other channels. A message that went by sms or rcs showed no imessage.styles, no imessage.subject and no effect, and edits and unsends of it answer 422 unsupported_content with channel_code not_on_sms.

Available options:
imessage,
sms,
rcs
error
object

The details of an error.

channel_message_id
string

The channel's own ID for the message, once the channel accepted it.

sms
object
read-only

SMS text messages only: how the text travels as SMS. Carriers bill and deliver SMS in segments: 160 GSM-7 characters fit one segment (153 each when split), but a single character outside GSM-7 (an emoji, curly quotes, most non-Latin letters) makes the whole text UCS-2, 70 characters in one segment (67 each when split). An SMS send may use at most 10 segments: 1530 GSM-7 characters (fewer when it uses GSM-7's two-unit characters such as { or €), or 670 when it holds an emoji or other character outside GSM-7; a longer text is refused with 400 invalid_request. Absent on MMS and on other channels.

reply_to
string
read-only

The Flow ID of the message this one replies to. For a sent message, the message the send named in reply_to. For a received message, the message it replies to inline (a Telegram or WhatsApp reply; on iMessage, the thread's root message). Absent when the message is not a reply or the quoted message is not known to Flow.

Pattern: ^msg_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"

metadata
object

Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.

updated_at
string<date-time>

When the status last changed.