Products

Ships to

Developer guideiMessage API7 min read

iMessage API for Python

Send and receive iMessage from Python with Flow. Install the SDK, stream inbound events, verify signed webhooks in FastAPI and reply with idempotency keys.

The short answer

With Flow, pip install one package, get a test key and loop over message.received events. Verify the signed webhook in FastAPI, reply with the event ID as the idempotency key and use the same agent 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.

Install and get a key

pip install flow-messaging
npx @flow-engineer/messaging init

You need Python 3.10 or later. init creates a test key with no account and writes FLOW_MESSAGING_KEY to .env. Without Node, call the endpoint yourself:

import requests
print(requests.post("https://api.flow.engineer/v1/sandbox/keys").json())

Save the key it returns as FLOW_MESSAGING_KEY. It is shown once. The test key runs on the Telegram sandbox. The same handler answers on iMessage, on Flow’s shared or dedicated US numbers.

Stream and reply

This is the Python example from Flow’s quickstart:

from flow_messaging import Flow, content_text

flow = Flow()
for event in flow.events.stream(types=["message.received"]):
    said = content_text(event.data.message.content)
    flow.messages.send(
        event.conversation.id,
        f"You said: {said}",
        idempotency_key=event.id,
    )

Replace the echo with a call to your model. A send is queued first, so watch message.sent or message.failed to learn the outcome. Flow has Python examples for the OpenAI Agents SDK and LangChain.

A FastAPI webhook

In production, register an HTTPS endpoint and verify every delivery. This is Flow’s FastAPI example:

from fastapi import FastAPI, Request, Response
from flow_messaging import WebhookSignatureError, construct_event, content_text, webhook_reply

app = FastAPI()

@app.post("/flow")
async def flow_webhook(request: Request):
    body = await request.body()  # the raw body: a parsed and re-serialized body will not match
    try:
        # The secret defaults to FLOW_MESSAGING_WEBHOOK_SECRET.
        event = construct_event(body, request.headers.get("Flow-Signature"))
    except WebhookSignatureError:
        return Response(status_code=400)
    if event.type == "message.received":
        return webhook_reply(f"You said: {content_text(event.data.message.content)}")
    return {}

Three rules: use the raw body, answer within 10 seconds or return {} and send later, and deduplicate on the event id, because deliveries are at least once. iMessage webhooks: signing, retries and ordering covers the details.

The raw WebSocket stream

In any language, you can read the live stream with a WebSocket client that can set a header. This is the example from Flow’s local development guide:

# pip install websockets
import asyncio, json, os, websockets

async def main():
    url = "wss://api.flow.engineer/v1/stream?type=message.received"
    headers = {"Authorization": f"Bearer {os.environ['FLOW_MESSAGING_KEY']}"}
    async with websockets.connect(url, additional_headers=headers) as ws:
        async for raw in ws:
            frame = json.loads(raw)
            print(frame)  # an "event" frame; reply with POST /v1/conversations/{id}/messages

asyncio.run(main())

Keep the id of the last event you handled. When you reconnect, pass it as after and the stream replays what you missed, then goes live.

Other languages: Node.js and TypeScript and Go.

Frequently asked questions

Is there an iMessage API for Python?

Yes. Flow publishes flow-messaging on PyPI. The underlying API is HTTP, so requests or httpx also work.

Which Python version does the Flow SDK need?

Python 3.10 or later, per Flow's docs. Import it with from flow_messaging import Flow. Flow() reads FLOW_MESSAGING_KEY from the environment.

How do I get a test key from Python?

Run npx @flow-engineer/messaging init, or call POST https://api.flow.engineer/v1/sandbox/keys with requests. The key allows one contact and 50 messages on the Telegram sandbox and expires after seven days.

How do I verify a webhook in FastAPI?

Read the raw body with await request.body(), then call construct_event with the body and the Flow-Signature header. The secret defaults to FLOW_MESSAGING_WEBHOOK_SECRET. Return a 400 if it raises WebhookSignatureError.

How do I reply with an AI model in Python?

Receive the event, call your model, and send the answer with flow.messages.send or answer in the webhook response. Flow has examples for the OpenAI Agents SDK, the Claude Agent SDK and LangChain.

Can I use the stream without a public URL?

Yes. flow.events.stream connects to the live event stream, so a laptop script can receive messages with no tunnel.

Start hereiMessage API: a developer’s guideThe overview of how iMessage APIs work, and what to ask a provider. Developer guideSend iMessage programmaticallycurl, TypeScript and Python: get a key, receive a signed webhook, reply once and handle retries.Developer guideiMessage API without a MacWhy a hosted iMessage API removes the Mac, what the DIY relay costs you, and how to choose.Developer guideiMessage MCP serverTwo things people mean by it: a messaging MCP for your coding agent, and your product as an MCP server.Developer guideiMessage API for Node.js and TypeScriptInstall the SDK, stream inbound messages, verify signed webhooks in Express and reply safely.Developer guideiMessage API for GoPoll events, reply and verify signed webhooks with the standard library, no SDK needed.Developer guideiMessage webhooks: signing, retries and orderingWhat a reliable webhook does, and how Flow, Sendblue and Linq differ on retries and replay.

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