Products

Ships to

Build an agentiMessage API8 min read

One agent on iMessage, WhatsApp and Telegram

Run one AI agent across iMessage, WhatsApp and Telegram with Flow. Keep one codebase while Flow handles each channel's windows, buttons, limits and identity.

The short answer

Flow lets one agent answer on iMessage, WhatsApp and Telegram from one codebase. Your tools and logic stay the same while Flow enforces each channel's rules around first messages, buttons, text length and identity.

Building on the iMessage API? Build on iMessage with Flow: one agent for iMessage, WhatsApp and Telegram. New to it? Read the iMessage API guide.

One handler, three channels

The promise of a unified messaging layer is that the code that answers a customer does not care where the customer is. This is the whole loop on Flow, from the quickstart:

import { FlowMessaging, contentText } from "@flow-engineer/messaging";

const flow = new FlowMessaging();
for await (const event of flow.events.stream({ types: ["message.received"] })) {
  const said = contentText(event.data.message.content);
  await event.conversation.reply("You said: " + said, { idempotencyKey: event.id });
}

The same loop receives a message from a Telegram bot, a WhatsApp number or an iMessage line, and replies into the same conversation. event.conversation.channel tells you which one if you want to change the tone.

The differences are not in the code. They are in the rules each channel enforces, and a messaging layer should apply them for you so a refused send comes back as a clear error.

Who can write first

Channel You may send free-form content when… Otherwise
WhatsApp The contact’s last message is less than 24 hours old Only an approved template
iMessage (Flow) The contact has written to this line before Customers text first. A dedicated line with the outbound add-on can write first, within warm-up limits
Telegram The contact has started your bot Telegram refuses the message

This is from Flow’s sending rules and its iMessage plans. The practical lesson is the same on all three: agents that reply to people who wrote first rarely meet these rules. Agents that broadcast do.

Flow keeps the channel rules at the messaging boundary: customers text the shared iMessage line first, while a dedicated line can support writing first subject to availability and warm-up limits. Your agent logic stays the same across channels.

What each channel can show

From Flow’s content types docs:

Content Telegram WhatsApp iMessage
Text Yes, up to 4,096 characters Yes, up to 4,096 Yes, up to 9,999
Images, video, audio, documents Yes Yes Yes
Buttons Inline keyboard Up to 3 buttons, else a list No
Templates No Yes No
Reactions Yes Yes Tapbacks, a fixed set
Typing indicator Yes Within 24 hours of the contact’s last message Within 5 minutes
Edit a sent message Yes No Within 15 minutes
Unsend Within 48 hours No Within 2 minutes
Message effects No No Yes

Nothing is converted silently. Without a fallback, a send that the channel cannot show fails with unsupported_content. With fallback: "auto", Flow sends the documented default, such as numbered text for buttons on iMessage. The message then carries a delivered_as field that says what the person actually saw.

import { buttons } from "@flow-engineer/messaging";

await conversation.send({
  content: buttons("When should we deliver?", [
    { id: "today", label: "Today" },
    { id: "tomorrow", label: "Tomorrow" },
  ]),
  fallback: "auto", // iMessage has no buttons: send numbered text instead
});

When the person answers “2” on iMessage, you still receive a button_reply with the second button’s id, so your code handles taps the same way everywhere.

Ask before you send

You do not have to memorise the table. GET /v1/capabilities?conversation=conv_... returns, per content type, native, fallback or unsupported, plus the channel’s size limits and whether the window is open. Use it to choose content before sending, instead of handling unsupported_content and outside_window afterwards.

For retries, send an Idempotency-Key with every POST. Retry only what clears by itself: rate_limited and sender_throttled carry a retry_after, and outside_window will not clear until the person writes again.

One person, many channels

Customers do not think in channels. The same person may text on iMessage and message on WhatsApp. Joining those identities is a security decision, so do it deliberately.

Flow’s framework docs say phone numbers on SMS and WhatsApp are normalised to E.164, so +1 555 010 0100 and 15550100100 identify the same phone. For other interfaces you supply a resolve function that maps the verified channel identity to your own account key. Flow does not guess that an app user and a phone number are the same account.

An iMessage handle can be a phone number or an email address, so do not assume a number. Never link identities by a matching display name.

Where this fits with Flow

Flow is the framework and messaging API this post is based on. You write your product’s tools once in TypeScript and the same agent answers on iMessage, WhatsApp and Telegram, and works as an MCP server and CLI for ChatGPT, Claude and coding agents. iMessage starts with a free shared line for up to 25 people a month, a $30 a month shared line for roughly 200 to 300 people and dedicated lines from $100 a month. See iMessage API pricing.

For Flow’s iMessage plans and limits, see Flow on iMessage: features, pricing and limits. For the full agent design, read Build an iMessage AI agent, or start with a test key on the Telegram sandbox.

Frequently asked questions

Can one AI agent work on iMessage, WhatsApp and Telegram?

Yes. With a messaging layer that normalises events and applies each channel's rules, one handler receives messages and replies on all three. Flow's docs say the same code answers on Telegram, WhatsApp and iMessage.

What is the biggest difference between the channels?

Who can write first. WhatsApp allows free-form replies for 24 hours after the contact writes, and then only approved templates. On Flow's iMessage line, customers text first unless a dedicated line has the outbound add-on. A Telegram bot can only message people who started it.

Do buttons work on iMessage?

No. iMessage has no buttons. With fallback set to auto, Flow sends numbered text, and when the person answers with the number you still receive a button reply with the button's ID.

What are the text length limits?

4,096 characters on Telegram and WhatsApp, and 9,999 on iMessage. Longer text is refused, so check the limit or let a reply helper split the answer.

How do I know what a channel supports?

Ask the API. GET /v1/capabilities for a conversation returns, for each content type, whether the channel shows it natively, through a fallback or not at all, plus size limits and whether the window is open.

How do I match the same person across channels?

Only through a verified mapping. Phone numbers on SMS and WhatsApp are normalised to E.164. For other interfaces such as iMessage, supply a resolve function that maps the verified identity to your own account key. Flow does not guess that two identities are the same person.

Start hereiMessage API: a developer’s guideThe overview of how iMessage APIs work, and what to ask a provider. Build an agentBuild an iMessage AI agentWebhook in, model call, tools, reply out: the Flow loop, memory, testing and production safeguards.Build an agentiMessage agent with the Vercel AI SDKTake a Vercel AI SDK agent and answer on iMessage with streamed, chat-sized bubbles.Build an agentiMessage agent with the OpenAI Agents SDKReceive an iMessage, run an OpenAI Agents SDK agent and send the result back as bubbles.Build an agentiMessage agent with the Claude Agent SDKRun a Claude agent for each iMessage and deliver its streamed answer.Build an agentLangChain iMessage agentConnect a LangChain model or chain to an iMessage conversation.Build an agentiMessage API for coding agentsLet Claude Code, Cursor or Codex set up and test a messaging integration for you.

All posts: the Flow blog. The Flow docs have the reference.

From reading to building

Your agent, on iMessage and everywhere else.

Flow gives your agent an iMessage line, and the same code answers on WhatsApp and Telegram and works as an MCP server and CLI. See how it works on iMessage.

Build on iMessage with Flow