CarlyEmail docs

Add email to any agent

Keep your model and framework. Add a real inbox, email tools, and an event that wakes the right agent thread.

Yes. CarlyEmail is the email layer, not the agent runtime. Keep the agent you already built in LangChain, the Claude Agent SDK, Vercel AI SDK, Mastra, Eve, Cloudflare Agents, or your own loop.

CarlyEmail supplies four things:

What CarlyEmail supplies What your agent supplies
Address A real inbox on carlyemail.com or your domain Who owns it and what it is for
Tools Read, search, draft, send, reply, label, and attachment operations A model or workflow that decides when to call them
Trigger A signed webhook or WebSocket event when mail arrives The function that starts or resumes the agent
Continuity Stable inbox, message, and thread ids Memory keyed by thread_id, when the runtime needs extra state

That produces the same programming model as an email-native agent platform, without choosing the model, memory system, hosting provider, or framework for you:

person sends email
        ↓
CarlyEmail stores and authenticates it
        ↓  signed message.received event
your runtime resumes the agent for this thread
        ↓  MCP or SDK tools
agent reads context, drafts, sends, or replies
        ↓
CarlyEmail sends a correctly threaded email

1. Give the agent an inbox

npx carlyemail signup --human-email you@example.com --username assistant
npx carlyemail verify 123456

The CLI stores the API key in ~/.carlyemail/config.json. Put that key and the inbox it printed in your application's secret store:

export CARLYEMAIL_API_KEY=ce_us_...
export CARLYEMAIL_INBOX=assistant@carlyemail.com

Do not ship the key to a browser. For a deployed agent, issue an inbox-scoped key with only the permissions it needs.

2. Give it email tools

Use MCP when the framework has an MCP client. It discovers the live tool schemas, so you do not maintain wrappers:

URL: https://api.carlyemail.com/mcp
Authorization: Bearer $CARLYEMAIL_API_KEY

LangChain, the Claude Agent SDK, Vercel AI SDK, Mastra, and Eve all support this directly. Use the TypeScript or Python SDK when you want to choose a smaller surface or call CarlyEmail from ordinary application code.

The model does not need every operation. An inbox agent usually needs:

  • list_messages, get_message, and get_thread to understand mail;
  • create_draft for work a person should approve;
  • reply_to_message only when autonomous sending is intentional.

Tool filtering improves model behavior. API-key permissions are the security boundary: if a key lacks message_send, a prompt cannot talk its way around the resulting 403.

3. Wake it when mail arrives

For production, expose an HTTP route and register it once:

npx carlyemail webhook https://your-agent.example/hooks/carlyemail \
  --events message.received

Save the whsec_... secret it prints. Your route must verify the unmodified raw body, deduplicate on event_id, and return a non-2xx response when the agent run fails so CarlyEmail retries it.

Use the event's thread_id as the agent-runtime conversation key. A reply then has two kinds of continuity:

  • CarlyEmail preserves the real email thread through In-Reply-To and References headers.
  • Your runtime can preserve private working memory under the same thread_id.

Use a WebSocket instead while developing locally or when the agent is already a long-running process. Use a webhook for serverless and durable runtimes because it wakes them and retries without an open connection.

4. Pick your framework

If your framework is not listed, it needs only one of these two interfaces:

  • an MCP client or function-tool API for actions;
  • an HTTP handler or WebSocket client for incoming events.

That is the entire adapter. CarlyEmail does not require a particular model provider, prompt format, memory database, or deployment platform.

Before you let it send unattended

Email content is untrusted model input. Verify the CarlyEmail webhook, then still treat the sender's text and attachments as untrusted. Start with a key that can read and create drafts but cannot send. Add autonomous sending only after you have sender allowlists, loop protection, idempotency, and an escalation path. See human in the loop.