Products

Ships to

Build an agentiMessage API7 min read

iMessage agent with the Vercel AI SDK

Build an iMessage AI agent with the Vercel AI SDK and Next.js. A webhook route, streamed replies as chat-sized bubbles, tools and local testing, with code.

The short answer

Keep your Vercel AI SDK agent as it is. Flow receives the message, you call streamText, and event.conversation.reply sends the streamed answer as chat-sized bubbles. The route answers the webhook at once and does the model call after, so slow models never time out a delivery.

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 route

Flow’s Vercel AI SDK guide keeps channel delivery separate from your model and tools. This is the Next.js route from that guide:

// app/api/flow/route.ts (Next.js)
// npm install @flow-engineer/messaging ai @ai-sdk/openai
import { after } from "next/server";
import { FlowMessaging, contentText } from "@flow-engineer/messaging";
import { streamText, type ModelMessage } from "ai";
import { openai } from "@ai-sdk/openai";

const flow = new FlowMessaging();

export const POST = flow.webhooks.handler({
  onEvent: async (event) => {
    if (event.type !== "message.received") return;

    after(async () => {
      // The last 20 messages of this conversation, oldest first (the new one included).
      const page = await event.conversation.messages({ limit: 20 });
      const messages: ModelMessage[] = page.data
        .reverse()
        .map((m) => ({ role: m.direction === "in" ? "user" : "assistant", content: contentText(m.content) }) as ModelMessage)
        .filter((m) => m.content);

      const result = streamText({
        model: openai("gpt-4.1-mini"),
        system: "You are the assistant for Asha's Bakery. Answer in short messages.",
        messages,
      });
      await event.conversation.reply(result, { idempotencyKey: event.id });
    });
  },
});

flow.webhooks.handler verifies the Flow-Signature header with your webhook secret before it calls onEvent. The idempotency key is the event ID, so a redelivered event cannot make the agent answer twice.

How it fits

  • Streaming into bubbles. reply(result) reads result.textStream, keeps typing on, and sends a bubble at each paragraph break while the model writes. See Flow’s streaming replies docs.
  • History from Flow. The route loads the last 20 messages of the conversation, so you do not need your own store to start.
  • Fast webhook. The handler answers at once and after() runs the model call.
  • One handler for many channels. It serves Telegram and iMessage alike.

Run and test it

Set FLOW_MESSAGING_KEY, FLOW_MESSAGING_WEBHOOK_SECRET and OPENAI_API_KEY. Then register https://<your-app>/api/flow as a webhook endpoint for message.received with POST /v1/webhook_endpoints, and keep the whsec_... secret it returns.

During development you do not need a public URL. Flow’s docs suggest reading events from the live stream, and npx @flow-engineer/messaging listen --forward-to http://localhost:3000/api/flow wraps that and forwards signed deliveries to this route.

Get a test key with no account using npx @flow-engineer/messaging init. The self-serve sandbox is a Telegram bot, so you chat with your agent there. Send iMessage programmatically covers keys, signed webhooks and retries in more detail.

Tools and long tool calls

Tools work as usual with tools and stopWhen. Only the model’s text goes to the person. If a tool takes a while, wrap the work in event.conversation.responding(...) so the person sees typing while they wait.

For the design of the tools themselves, see Build an iMessage AI agent: read before write, confirm before you act, and say it is done only when your backend says so.

Going live on iMessage

Flow’s iMessage numbers are US numbers, shared or dedicated, and the same route answers on iMessage with no code change. By default customers text the line first, and a dedicated line with the outbound add-on can write first. iMessage has no buttons, so Flow sends numbered text, and your code still receives a button_reply.

Other agent frameworks follow the same shape: OpenAI Agents SDK, Claude Agent SDK and LangChain.

Frequently asked questions

Can I use the Vercel AI SDK to build an iMessage agent?

Yes. Flow's docs have a Vercel AI SDK guide. A Next.js route receives the signed webhook, runs streamText, and passes the result to event.conversation.reply, which sends the answer as chat bubbles.

How does streaming work on iMessage?

reply reads the result's text stream, keeps the typing indicator on and sends a bubble at each paragraph break while the model writes. On iMessage the bubbles are shorter, about 400 characters before a cut at a sentence end.

Do tools still work?

Yes. Tools and stopWhen work as usual. Only the model's text is sent to the person, never tool calls or their results. Text the model writes between tool steps is sent too.

Why does the route use after()?

The webhook handler returns at once and after() runs the reply, so a slow model never causes a timed-out webhook delivery.

Do I need a public URL to develop?

No. You can read events from the live stream, or run the npx @flow-engineer/messaging listen command to forward signed deliveries to localhost.

Does the same handler work on other channels?

Yes. One handler serves Telegram and iMessage alike, and event.conversation.channel tells you which one if you want to change the tone.

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 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.Build an agentOne agent on iMessage, WhatsApp and TelegramWhat changes per channel and what stays the same when one codebase answers everywhere.

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