CarlyEmail docs

Agent frameworks

Keep your agent framework. Add a real inbox, live email tools, signed inbound events, and thread continuity.

You do not have to rebuild your agent inside CarlyEmail. Keep its model, prompt, memory, tools, and hosting. CarlyEmail adds the email channel around it:

real inbox → signed incoming-mail event → your agent → MCP or SDK → threaded reply

Start with Make your agent reply to email for the framework-neutral version, or with the examples, which were each run for real. Each page below applies that same four-part loop to one runtime.

The portable contract

Your runtime needs CarlyEmail gives it
A callable tool surface Hosted MCP, the toolkits, or generated TypeScript/Python SDKs
A way to receive an event Signed webhook or authenticated WebSocket
A conversation key Stable thread_id on every message and event
A real email identity A CarlyEmail inbox or verified custom domain

MCP is the shortest default because the server publishes the live schemas and tool annotations. When your runtime does not speak MCP, or you want the tool list fixed at build time, carlyemail-toolkit gives the OpenAI Agents SDK, LangChain, LiveKit Agents and the Vercel AI SDK the same tools as native function tools — see Toolkits. Use the SDK directly when you want to keep the email calls outside the model's tool loop.

For LangChain there is also pip install langchain-carlyemail, which adds a loader, a retriever, and a verified inbound router. See the LangChain page.

What is framework-specific

Only two pieces vary: how a runtime registers MCP tools, and how it resumes an agent when the webhook arrives. The business logic should not. In every framework, key memory by CarlyEmail thread_id and reply with the message or thread tool instead of constructing new email headers.

The receiving half does not vary at all, so do not write it per framework:

Python

from carlyemail.inbound import create_email_router

app.include_router(create_email_router(on_email, path="/hooks/carlyemail"))

That verifies the signature over the raw bytes, admits message.received and not the spam, blocked or unauthenticated variants, drops mail the inbox sent itself, checks the sender, ignores redeliveries, and answers the request before your agent starts thinking. decide() is the same logic with no framework in it, for everything that is not FastAPI. See receiving mail.

The security boundary

An email is untrusted model input. A verified webhook proves CarlyEmail delivered the payload; it does not make the sender's instructions safe. Give each agent an inbox-scoped key with the minimum permissions it needs. For early deployments, allow reads and draft_create but omit message_send; sending then returns 403 regardless of the model's reasoning. See human in the loop.

Generating a typed client

The spec is published, and a test fails if it drifts from what the API serves.

npx @hey-api/openapi-ts -i https://docs.carlyemail.com/openapi.json -o ./carlyemail
openapi-generator-cli generate -i https://docs.carlyemail.com/openapi.json -g python -o ./carlyemail