Build an agentiMessage API6 min read
iMessage agent with the Claude Agent SDK
Run a Claude agent on iMessage with Flow and stream its answer as chat bubbles. Includes a TypeScript example, session handling and safe tool setup.

The short answer
With Flow, call query() for each incoming message and pass the iterator to reply() for natural chat bubbles. Keep one Claude session per conversation, turn built-in tools off for customer-facing agents and reuse the same handler on every channel.
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.
The code
This is the TypeScript example from Flow’s Claude Agent SDK guide. Your agent keeps its tools. Flow handles the channel connection.
// npm install @flow-engineer/messaging @anthropic-ai/claude-agent-sdk
import { query } from "@anthropic-ai/claude-agent-sdk";
import { FlowMessaging, contentText } from "@flow-engineer/messaging";
const flow = new FlowMessaging();
for await (const event of flow.events.stream({ types: ["message.received"] })) {
const run = query({
prompt: contentText(event.data.message.content),
options: {
systemPrompt: "You are the assistant for Asha's Bakery. Answer customers in short messages.",
tools: [], // no built-in tools (Bash, file access); list the ones your agent may use
includePartialMessages: true, // stream text as it is written
},
});
await event.conversation.reply(run); // sends the agent's text, skips tool and system messages
}
How it fits
- Streaming.
reply()reads thequery()iterator. WithincludePartialMessagesit sends text as it streams. Without it, it sends each assistant message’s text. - Bubbles. Each assistant turn becomes its own bubble, so “Let me check that…” followed by the answer reads like a person texting.
- What the person sees. Tool calls, tool results and system messages are never sent to the person.
Keep the agent safe
The Claude Agent SDK starts from Claude Code’s toolset. An agent that talks to customers should not have a shell or the file system. Two lines in the example do the work:
tools: []turns the built-in tools off.- To allow something, list exactly the tools the agent may use. Flow’s docs warn that
allowedToolsonly pre-approves tools and does not restrict them, so it is not a substitute for the empty list.
Your own actions should go through narrow, typed tools with confirmation before any change. Build an iMessage AI agent covers read before write and confirm before you act.
Sessions and memory
Each message starts a new query(), so you need to carry context. Flow’s guidance is to keep one Claude session per Flow conversation: remember each run’s session_id and pass it as resume next time. The conversation ID is the natural key. On iMessage a contact’s handle can be a phone number or an email address, so key by conversation or contact ID, not by number.
You can also read recent history from the API with GET /v1/conversations/{conversation_id}/messages. Flow keeps message content for 30 days by default, so store long-term memory yourself, and delete a contact’s data through the API when they ask.
Try it
Get a test key with npx @flow-engineer/messaging init, then chat with the agent on the Telegram sandbox bot. Ask the Flow team for an iMessage line when you are ready. The same code answers there, to people who write to the line first. For signed webhooks and retries, see Send iMessage programmatically.
Other frameworks: Vercel AI SDK, OpenAI Agents SDK, LangChain.
Frequently asked questions
Can I build an iMessage agent with the Claude Agent SDK?
Yes. Flow's docs have a Claude Agent SDK guide. You run query() for each incoming message and pass the result to reply(), which sends the streamed answer as chat bubbles.
Should a customer-facing agent have Claude Code's built-in tools?
No. Set tools to an empty list so it has no Bash or file access, and list only the tools your agent needs. Flow's docs note that allowedTools only pre-approves tools and does not restrict them.
How does each assistant turn appear on iMessage?
Each assistant turn becomes its own bubble, which reads naturally in chat. Tool calls, results and system messages are not sent to the person.
How do I keep context between messages?
Keep one Claude session per Flow conversation. Remember each run's session ID and pass it as resume the next time.
Is there a Python version?
Yes. Flow's docs include a Python example that uses the Claude Agent SDK with plain HTTP calls and posts each text block as a message with its own idempotency key.
Does the agent work on WhatsApp and Telegram too?
Yes. The messaging code is the same on each connected channel, and Telegram works in the self-serve sandbox.
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