> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flow.engineer/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Flow Messaging is one HTTP API (https://api.flow.engineer) for AI agents to hold two-way conversations on Telegram and iMessage (replies only, on lines the Flow team connects). WhatsApp is coming and not available yet.
> Build the app on the REST API. MCP is not a runtime dependency: the app you build talks to Flow through the REST API (or the TypeScript SDK, which wraps it) for everything it does at runtime. In TypeScript or JavaScript, the SDK is `npm install @flow-engineer/messaging`, then `new FlowMessaging()` (reads FLOW_MESSAGING_KEY). Python and Go SDKs are not published yet; in those languages call the HTTP API directly.
> No key yet? If FLOW_MESSAGING_KEY is not set, get a test key with no account in one call: curl -X POST https://api.flow.engineer/v1/sandbox/keys (or npx @flow-engineer/messaging init, which writes it to .env). Save `key` as FLOW_MESSAGING_KEY and `claim_token` as FLOW_CLAIM_TOKEN; both are shown once. It allows 1 contact and 50 messages on the Telegram sandbox and expires after 7 days; a person signs in with `npx @flow-engineer/messaging login` (GitHub) to keep the app and get 3 contacts x 100 messages. Signed in, live keys (fk_live_...) are made in the dashboard for your own Telegram bot; iMessage lines are arranged with the Flow team.
> The hosted MCP server (https://api.flow.engineer/mcp) is optional, for testing during development. Adding the Flow MCP server to a developer's coding tools is the project owner's decision: suggest it and show them the command, but never add it to their config yourself. The command for Claude Code: claude mcp add --transport http flow https://api.flow.engineer/mcp --header "Authorization: Bearer $FLOW_MESSAGING_KEY". If its tools are already available to you, use them for sandbox testing.
> Authenticate with `Authorization: Bearer fk_test_...` (sandbox) or `fk_live_...` (real contacts). Never put a live key in client-side code.
> Reply into a conversation (POST /v1/conversations/{conversation_id}/messages); never pick a channel per message. Switch on `error.type`, not the message text.
> Full documentation for agents: https://docs.flow.engineer/llms-full.txt. OpenAPI spec: https://raw.githubusercontent.com/flow-engineer/sdk/main/openapi/openapi.yaml.

# Build an iMessage AI agent with the Flow Messaging API

> Let your AI agent answer people on iMessage from a line the Flow team connects to your app: receive iMessages by webhook or stream, reply with streamed LLM answers, tapbacks and effects, and use fallbacks for buttons. Replies only: the person writes first.

This guide builds an AI agent that people text on iMessage, using the same code as on Telegram, plus the iMessage-specific parts: tapbacks, effects and the replies-only rule.

<Note>
  iMessage is live for **replies only**, on lines the **Flow team** connects to your app: the person always writes first, and your agent answers. It is not part of the sandbox. To get a line, ask the Flow team; build on the [Telegram sandbox bot](/guides/telegram-agent) in the meantime, with the same code.
</Note>

```ts TypeScript theme={null}
// npm install @flow-engineer/messaging
import { 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);
  await event.conversation.react(event.data.message.id, "👍"); // shows as the Like tapback
  await event.conversation.reply(await askMyAgent(text));
}
```

## 1. Get a line

1. Sign in to the [dashboard](https://api.flow.engineer/admin) with GitHub and make a live key (`fk_live_...`) on the [Keys page](https://api.flow.engineer/admin/keys?mode=live). See [Going live](/guides/going-live).
2. Ask the Flow team to connect an iMessage line to your app. iMessage lines are not self-serve: `POST /v1/senders` with `"channel": "imessage"` answers `501 not_implemented`.
3. With the live key, `GET /v1/senders?channel=imessage` lists the line. Its `address.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.
4. Run the code above, text the line from an iPhone or Mac, and your agent answers.

## 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](/concepts/streaming-replies).

## 3. Tapbacks and effects

* **Reactions** become tapbacks. iMessage has a fixed set, so with `fallback: "auto"` (which `react()` sets) Flow uses the closest tapback, or skips the reaction and says so in `delivered_as`.
* **Effects** send text with an iMessage effect:

```ts TypeScript theme={null}
import { effect } from "@flow-engineer/messaging";

await event.conversation.send({ content: effect("Your order is confirmed!", "confetti"), fallback: "auto" });
```

Effects are `slam`, `loud`, `gentle`, `invisible_ink`, `echo`, `spotlight`, `balloons`, `confetti`, `love`, `lasers`, `fireworks` and `celebration`. Which ones a line can send may vary, so keep `fallback: "auto"` (plain text) on.

## 4. No buttons: use the fallback

iMessage has no buttons. Send `buttons` content with `fallback: "auto"` and Flow sends numbered text instead. When the person answers "2", you still receive a `button_reply` with the second button's `id`, so your code is the same on every channel.

```ts TypeScript theme={null}
import { buttons } from "@flow-engineer/messaging";

await event.conversation.send({
  content: buttons("Which time works?", [{ id: "t10", label: "10:00" }, { id: "t14", label: "14:00" }]),
  fallback: "auto",
});
```

## 5. Replies only

iMessage lines are personal-style numbers, and iMessage watches closely for unwanted messages. Flow protects your line:

* **The person writes first.** Flow's iMessage lines are reply-only: starting a conversation with a new contact (`POST /v1/messages`) is refused with `403 permission`. Someone who has written to the line before is an open conversation, and replies to them are not budgeted.
* **Get people to write with the opt-in link.** When the line has one, the sender's `address.link` opens 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](/concepts/send-gate).

## 6. What iMessage supports

| Content | iMessage |
| - | - |
| Text | yes, up to 9999 characters (markdown becomes plain text with `fallback: "auto"`) |
| Images, video, audio | yes |
| Documents | received (not malware-scanned yet); sending uploaded documents is refused with `422 unsupported_content` until scanning is on |
| Tapbacks | yes, fixed set |
| Typing | yes, within 5 minutes of the contact's last message (otherwise `409 outside_window`, safe to ignore) |
| Read receipts | yes, for the whole conversation (`up_to` has no effect) |
| Effects | yes |
| Buttons | no (numbered-text fallback) |
| Edit | yes, within 15 minutes of sending (not the message that started the conversation) |
| Unsend | yes, within 2 minutes of sending |
| Voice notes | yes, from an mp3, wav, m4a, caf or aac file |
| Locations, contact cards | no: `fallback: "auto"` sends a maps link or text |

Use `GET /v1/capabilities?conversation=conv_...` for the live answer for a conversation.

## 7. 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

* [Going live](/guides/going-live): live keys and iMessage lines.
* [Content types](/concepts/content-types)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.