Build an agentiMessage API6 min read
iMessage agent with the OpenAI Agents SDK
Run an OpenAI Agents SDK agent on iMessage with Flow. Receive a message, run the agent and stream the result back as chat bubbles, with TypeScript and Python examples.

The short answer
With Flow, run the agent with stream: true and pass the result to reply() for chat-sized bubbles. The same agent code works across iMessage, WhatsApp and Telegram, so adding a channel does not mean rebuilding your agent.
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.
TypeScript
Flow’s OpenAI Agents SDK guide passes the agent’s stream to reply(). The messaging code is the same on each connected channel.
// npm install @flow-engineer/messaging @openai/agents
import { Agent, run } from "@openai/agents";
import { FlowMessaging, contentText } from "@flow-engineer/messaging";
const flow = new FlowMessaging();
const agent = new Agent({
name: "Bakery assistant",
instructions: "You help customers of Asha's Bakery with orders and delivery. Keep answers short.",
model: "gpt-4.1-mini",
});
for await (const event of flow.events.stream({ types: ["message.received"] })) {
const result = await run(agent, contentText(event.data.message.content), { stream: true });
await event.conversation.reply(result); // reads result.toTextStream()
}
run(agent, input, { stream: true }) returns a streamed result. reply() reads its text stream and sends bubbles while the agent runs.
Python
The Python example in Flow’s docs uses plain HTTP and a FastAPI webhook. It answers {} at once, then sends the agent’s final_output from a background task:
# pip install openai-agents fastapi requests
import os, requests
from fastapi import FastAPI, Request, BackgroundTasks
from agents import Agent, Runner
API = "https://api.flow.engineer"
HEADERS = {"Authorization": f"Bearer {os.environ['FLOW_MESSAGING_KEY']}"}
agent = Agent(name="Bakery assistant", instructions="You help customers of Asha's Bakery. Keep answers short.")
app = FastAPI()
async def answer(event: dict):
text = event["data"]["message"]["content"].get("text", "")
result = await Runner.run(agent, text)
requests.post(
f"{API}/v1/conversations/{event['conversation']['id']}/messages",
headers={**HEADERS, "Idempotency-Key": event["id"]},
json={"content": {"type": "text", "text": result.final_output, "format": "markdown"}, "fallback": "auto"},
timeout=30,
)
@app.post("/flow")
async def flow_webhook(request: Request, tasks: BackgroundTasks):
event = await request.json() # verify Flow-Signature first
if event["type"] == "message.received":
tasks.add_task(answer, event)
return {}
Verify the Flow-Signature header before you trust the body. The check is in Send iMessage programmatically.
How it fits
- The agent’s tools, handoffs and instructions stay in the OpenAI Agents SDK. Flow only moves messages in and out.
- Give the agent the conversation’s history from
GET /v1/conversations/{conversation_id}/messages, or keep your own session keyed by the conversation ID. "fallback": "auto"sends markdown as plain text where a channel has no formatting. On iMessage that is what you want.
Limits to know
- Text length. iMessage takes up to 9,999 characters per message and Telegram 4,096. Longer text is refused.
- Who writes first. By default customers text the line first. A dedicated line with the outbound add-on can write first, within warm-up limits. Test on the Telegram sandbox with the same code.
- No buttons. If your agent uses
buttonscontent, setfallback: "auto"and Flow sends numbered text.
For the design of the agent around this code, see Build an iMessage AI agent. Other frameworks: Vercel AI SDK, Claude Agent SDK, LangChain.
Frequently asked questions
Can the OpenAI Agents SDK run on iMessage?
Yes, through a messaging API. Flow's docs have an OpenAI Agents SDK guide: receive the message, run the agent, and send its stream back as chat bubbles.
Does it work in Python as well as TypeScript?
Yes. The TypeScript SDK passes the streamed result to reply. The Python example answers the webhook with an empty object, runs the agent in a background task, and posts the final output through the HTTP API.
How do I keep conversation history?
Read it from GET /v1/conversations/{conversation_id}/messages, or keep your own session keyed by the conversation ID.
What is the text limit?
iMessage allows up to 9,999 characters in a text message, and Telegram allows 4,096. Text over the limit is refused, so split long answers at paragraph breaks and send each part with its own idempotency key.
How do I avoid duplicate replies?
Use the event ID as the Idempotency-Key. A repeat with the same key returns the first answer and does not send twice.
Can I trace and grade the agent's turns?
Flow's docs describe sending traces from agent frameworks to Flow Observe, which shows each turn's model and tool calls and grades every reply.
Keep reading
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