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, andget_threadto understand mail;create_draftfor work a person should approve;reply_to_messageonly 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-ToandReferencesheaders. - 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.