iMessage
iMessage · iMessage API for AI agents · iMessage AI agent · send iMessage from API · iMessage chatbot API · AI agent SMS iMessage · iMessage business API for developers · blue bubble APIReceive and answer iMessage, one-to-one and in group chats, on Flow's shared line or a dedicated line.
Let people text your agent on an iMessage line. The person writes first; your agent replies with the same code you use on Telegram. iMessage has no 24-hour window: once someone has texted your line, your agent can message them any time.
// npm install @flow-engineer/messagingimport { FlowMessaging, contentText } from "@flow-engineer/messaging";import { askMyAgent } from "./agent"; // returns a string, or any LLM stream
const flow = new FlowMessaging();
for await (const event of flow.events.stream({ types: ["message.received"] })) { const text = contentText(event.data.message.content); if (event.conversation.type !== "group") { await event.conversation.react(event.data.message.id, "👍"); // shows as the Like tapback; groups take no reactions } await event.conversation.reply(await askMyAgent(text));}1. Get a line
Section titled “1. Get a line”- Sign in to the dashboard with your Flow account. Your first sign-in (or the first key you make there) starts the account’s free trial, which opens Flow’s shared iMessage line to every app of the account, with test and live keys. The Flow CLI’s
npx @flow-engineer/flow setupstarts the same trial. GET /v0/senders?channel=imessagelists the line (kindshared) with your app’sjoin_code, the message a person sends to join your app.address.link, when set, opens Messages with the line and that message filled in.- Run the code above, send the join message to the line from an iPhone or Mac, and your agent answers. A person’s messages go to your app until they send another app’s join message.
The trial is a free trial for 25 people on the shared iMessage line (see Pricing); an account that moves to a paid plan keeps the line, with that plan’s limits. The shared line ignores group chats and never writes first.
- Sign in to the dashboard with a Flow account and make a live key (
fk_live_...) under Developers › API keys, switched to Live. See Going live. - Buy Dedicated (SMS/RCS fallback and call forwarding included), and, if you want your agent to write first, the $40 Outbound add-on, in the dashboard (Settings → Usage and billing → Choose a plan); Flow reviews the line, then sets it up, usually within a day. A new line takes up to 250 different people a day and ramps to 1,000 over its first 14 days. Lines are not made through the API:
POST /v0/senderswith"channel": "imessage"answers501 not_implemented. - With the live key,
GET /v0/senders?channel=imessagelists the line. Itsaddress.link, when set, is the line’s opt-in link: it opens Messages with the line and a prefilled text the person sends to start. - Run the code above, text the line from an iPhone or Mac, and your agent answers.
What a line can do beyond replying is on the sender, in imessage (read-only; it follows the line and its add-on): fallback (SMS/RCS fallback, included on every line, section 6), call_forwarding (section 8) and can_initiate (writing first, section 5).
Every message type you can send, with its API call and a screenshot from the contact’s iPhone, is on iMessage message types.
2. Bubbles fit iMessage
Section titled “2. Bubbles fit iMessage”People read iMessage as short lines. reply() uses shorter bubbles here (about 400 characters before it cuts at a sentence end) than on Telegram. See Streaming replies.
3. Tapbacks and effects
Section titled “3. Tapbacks and effects”- Reactions (
POST .../reaction): ❤️ 👍 👎 😂 ‼️ ❓ show as tapbacks, and any other emoji as an emoji reaction. Groups take none (422 unsupported_content,channel_codegroup_unsupported). - Effects (
POST .../effect) send text with an iMessage effect:
import { effect } from "@flow-engineer/messaging";
await event.conversation.send(effect("Your order is confirmed!", "confetti"));Effects are slam, loud, gentle, invisible_ink, echo, spotlight, balloons, confetti, love, lasers, fireworks and celebration. Which ones a contact’s device shows may vary, and a message that goes by SMS or RCS shows the text without the effect. Effects work on iMessage only: send the same text as a basic message on other channels.
4. No buttons: list the choices in the text
Section titled “4. No buttons: list the choices in the text”iMessage has no buttons, lists or carousel cards: they answer 422 unsupported_content with param kind, and nothing is sent. Flow converts nothing, so send a basic message whose text lists the choices, and read the person’s answer, which arrives in words as a basic message. To show several photos or videos together, send one basic message with imessage.media_group (photo carousel).
await event.conversation.send("Which time works, 10:00 or 14:00?", { idempotencyKey: `${event.id}:time` });If your code sends buttons on every channel, catch the 422 and send the text instead with a new Idempotency-Key: a 422 is kept under its key, so add what the substitute is, such as evt_123:time:basic. See unsupported_content.
5. Who writes first
Section titled “5. Who writes first”iMessage lines are personal-style numbers, and iMessage watches closely for unwanted messages. Flow protects your line:
- The person writes first. A line replies only, unless writing first is switched on for it (
imessage.can_initiate). From a reply-only line, starting a conversation with a new contact (POST /v0/messages) is refused with403 permission. Someone who has written to the line before is an open conversation: replies to them spend no new-contact budget, but each person still counts toward your plan’s people for the day and month (Pricing). - No 24-hour window. iMessage has no 24-hour window: once someone has texted your line, your agent can message them any time. Only typing and read receipts are timed: they work for 5 minutes after your line’s last message to the person, so send your reply first.
- Writing first comes with the Outbound add-on, for dedicated lines only (Pricing). Buy it ($40 a month) in the dashboard with Dedicated, or later on the same card. The line then writes first within a warm-up: it starts 2 new conversations a day, growing to 40 a day over its first 21 days (
limits), separately from its daily limit of different people. If the channel still refuses a start, the send ends inmessage.failedwithoutside_window. The shared line never writes first. - Get people to write with the opt-in link. When the line has one, the sender’s
address.linkopens Messages with the line and a prefilled text the person sends to start. Put it on your site, receipts or a QR code. - No cold outreach. Sending the same text to many people, or messages that get no reply, can throttle the line (
sender_throttled).
See The send gate.
6. SMS and RCS from the same number
Section titled “6. SMS and RCS from the same number”SMS/RCS fallback is included on every iMessage line: Flow’s shared line (on the free trial and the Shared plan) and dedicated lines alike, with nothing to buy. With it (imessage.fallback on the sender), a message to someone who cannot get iMessage goes by SMS or RCS from the same number. On a line without it, a message to someone not on iMessage fails (message.failed).
Your app never picks the network: it is chosen per message when it is sent. Message.network says which way a message went: imessage, sms or rcs. It is set on inbound messages, and on yours once the channel reports it (data.message.network in message.delivered or message.failed).
The network is chosen after Flow sends, so Flow sends every message as for iMessage: a message that goes by SMS or RCS shows no imessage.styles, no imessage.subject and no effect, and leaves out an imessage.line_contact_card card, so it arrives as its text alone. Message.network tells which way it went, and is the only sign the card was left out. Keep text readable without them. Edits and unsends of a message that went by SMS or RCS are refused with 422 unsupported_content, channel_code not_on_sms: send a new message instead.
7. Group chats
Section titled “7. Group chats”Only dedicated lines take group chats, and only ones people add the line to. Flow’s shared line ignores group chats: they never reach your app from it.
- A group is one conversation with
typegroup. It has nocontact;groupholds the group’sname, when it has one, and itsparticipants(each a contact on the channel, made the first time Flow sees them). Every inbound message carriesfrom, the contact who wrote it. - People who join or leave change the list without an event. Read the conversation (
GET /v0/conversations/{conversation_id}) for the current list; events inline onlygroup_name. - Writers count toward your plan’s people; members who never write do not. A writer past a limit is held: their messages wait and arrive when they are let in, and the rest of the group’s messages are not held up. See Pricing.
- Reply with the normal sends (
POST /v0/conversations/{conversation_id}/basicand the other kinds’ endpoints): basic messages (withimessage.styles,imessage.subjectandlink_preview), media, voice notes, effects, threaded replies (reply_to), edits and unsends work in groups. - No reactions, typing or read receipts from your line. Each answers
422 unsupported_contentwithparamkindandchannel_codegroup_unsupported, and nothing is sent: leave them out in groups. - Flow cannot create a group, rename one, or add or remove people. A start (
POST /v0/messages) always makes a direct conversation.
Test groups before you have a line: POST /v0/test/conversations with "channel": "imessage" and a group (name, participants) makes a simulated group conversation under the same rules. See Test and live mode.
8. Calls
Section titled “8. Calls”- Phone calls to a dedicated line’s number can be forwarded to a phone you choose; call forwarding is included with Dedicated and set up with the Flow team (
imessage.call_forwarding.phoneshows where). Forwarded calls send no event. The shared line does not forward calls. - FaceTime calls are never forwarded: the line cannot answer them, and each attempt arrives as a
call.receivedevent (data.call.contact,data.call.networkfacetime) in the direct conversation with the caller, made if it is new. A webhook endpoint receives it only when it listscall.receivedinevents. - A call is not a message: it does not count toward your plan’s people, and it does not let a reply-only line write to the caller first.
9. Text styles, subjects, link previews and your contact card
Section titled “9. Text styles, subjects, link previews and your contact card”text is sent exactly as written: Flow reads no markdown, and text that looks like markup shows as written. Style a basic message with imessage.styles, ranges of the text (start and length in UTF-16 code units) with a style: bold, italic, underline, strikethrough, big, small, or the animations shake, nod, explode, ripple, bloom and jitter. imessage.subject (up to 200 characters) shows as the message’s bold subject line, and link_preview: false sends links without a rich preview.
await flow.messages.basic(event.conversation.id, { text: "Your order is on its way. Track it: https://example.com/t/4821", imessage: { subject: "Order 4821", styles: [{ start: 14, length: 10, style: "big" }] }, link_preview: false,});imessage.line_contact_card: true attaches your line’s own contact card: a card bubble with the line’s name, photo and number, which the contact taps to save. Send it when a contact asks for your card, or once early in a conversation, not on every message.
{ "text": "Save my contact card so you can find me", "imessage": { "line_contact_card": true } }- Dedicated lines only. On a shared line it answers
422 unsupported_contentwithparamimessage.line_contact_cardandchannel_codeline_contact_card_dedicated_only. - It needs
textand cannot go withmedia. Either answers400 invalid_requestwithparamimessage.line_contact_card. - By SMS or RCS the card is left out. The message arrives as its text alone, and only its
network(smsorrcs) tells you.
The imessage block works on iMessage only: on another channel it answers 422 unsupported_content with param imessage. See Send a basic message.
10. What iMessage supports
Section titled “10. What iMessage supports”| Kind | iMessage |
|---|---|
Text (basic) | yes, up to 9999 characters, sent as written; styles, a subject and, on a dedicated line, your line’s contact card through the imessage block (section 9) |
| Images, video, audio | yes |
| Documents | yes, sent and received; malware-scanned before Flow keeps them (an upload that fails answers 422 file_blocked) |
| Reactions | yes: tapbacks for ❤️ 👍 👎 😂 ‼️ ❓, emoji reactions for others; not in groups |
| Typing | yes, once the contact wrote, for 5 minutes after your line’s last message to them, so send your reply first (otherwise 409 outside_window, safe to ignore); not in groups (422, group_unsupported) |
| Read receipts | yes, for the whole conversation (up_to answers 422 unsupported_content), once the contact wrote, for 5 minutes after your line’s last message to them, so send your reply first (otherwise 409 outside_window); not in groups (422, group_unsupported) |
| Effects | yes (may be lost on a message that goes by SMS or RCS) |
| Photo carousel | yes: 2 to 10 images and videos in one message with imessage.media_group on a basic message |
Buttons, lists, carousel cards (/carousel), templates | no (422 unsupported_content): list the choices in a basic message’s text; for photos, use imessage.media_group |
| Edit | yes, within 15 minutes of sending (otherwise 422 unsupported_content, edit_window_closed); not the message that started the conversation (first_message_not_editable), nor one that went by SMS or RCS |
| Unsend | yes, within 2 minutes of sending (otherwise 422 unsupported_content, unsend_window_closed); not one that went by SMS or RCS |
| Voice notes | yes, from an mp3, wav, m4a, caf or aac file |
| Contact cards | yes, as a basic message with a vCard (.vcf) as media of type document; your own dedicated line’s card with imessage.line_contact_card (section 9). The /contact-card endpoint answers 422 unsupported_content |
| Locations | no (422 unsupported_content): send a basic message with a maps link |
Each endpoint’s page opens with the channels it works on and their limits. Each type’s request and how it looks on an iPhone: iMessage message types.
11. Contacts
Section titled “11. Contacts”An iMessage contact’s address.handle is a phone number or an email address. Do not assume a phone number. Reply into the conversation the person started rather than addressing them by handle.
Related
Section titled “Related”- Going live: live keys and iMessage lines.
- Pricing: plans and how many people each reaches.
- iMessage message types: every type with a screenshot.
- Content types
Thanks, we read every comment.