Skip to main content
AgentMail

AgentMail's agent-native email → the Naive email primitive

AgentMail gives an AI agent a real email account through one API — create an inbox, send and receive messages, group them into threads, and drive it all from webhooks or WebSockets. It does that job well, and the API is clean. But it is also a separate vendor account:
  • Inboxes live behind their own API key, dashboard, and inbox_id namespace.
  • That account scopes email and nothing else — it knows the agent’s inboxes and threads, but not its virtual card, its vault secrets, its connected apps, or its KYC status.
  • “Who let this agent email a customer, and what else can it touch?” is answered in AgentMail for mail, and somewhere else for everything else. There is no shared accountability.
Naive’s email primitive gives the agent the same capability — provision an inbox on your domain, send mail, read replies — but rooted in one identity:
  • The tenant user that owns the inbox is the same user that owns its cards, its vault secrets, its connections, and its KYC.
  • Whether the agent may send mail at all — and whether a send freezes for human approval — is decided by that user’s Account Kit at execution time.
  • Every inbox, send, and inbound reply lands in the same per-user activity log as everything else the agent does.
This guide maps AgentMail’s API to Naive’s, shows the smallest working swap, and is explicit about what does not map yet.
AgentMail is a trademark of its owner, used here for identification only. No endorsement or affiliation is implied.
Tested against: AgentMail Node SDK agentmail (AgentMailClient, API base https://api.agentmail.to/v0, docs snapshot June 2026) and the Naive Node SDK @usenaive-sdk/server against the Naive API (base https://api.usenaive.ai/v1, docs snapshot June 2026).Version notes:
  • AgentMail’s REST API is still on the v0 path — treat method and field names as pre-1.0 and verify against your installed SDK version.
  • AgentMail keys an inbox by its full address (agent@agentmail.to) and uses that as the inbox_id. Naive returns an inbox UUID but the SDK’s from_inbox accepts either the id or the address.
  • AgentMail sends a separate text and html body. Naive takes a single body and sends it as HTML if it contains tags, otherwise plain text.

Concept map

Before / after: the core path

The path that matters for almost every agent is create an inbox, then send a message from it. Here it is on both platforms.
The create-and-send shape lines up closely. The real differences to plan for:
  • One body, not two. AgentMail takes text and html. Naive takes a single body and sends it as HTML when it contains tags, otherwise plain text.
  • Inbox id is a UUID (or the address). AgentMail uses the address itself as inbox_id. Naive returns a UUID id plus the address; from_inbox accepts either.
  • The id is your identity, not a separate account. In AgentMail the API key scopes email. In Naive the same forUser(id) handle also owns the agent’s cards, vault, connections, and KYC.

Receiving replies

Both platforms expose a list-the-inbox call and an inbound webhook. AgentMail’s event is message.received; Naive’s is email.received.
  • On Naive, an inbound delivery fires a signed, retried email.received webhook. When present, data.tenant_user_id identifies which tenant user the reply belongs to (company-wide subscriptions may omit it — see Webhooks).
  • Naive returns a snippet in the list and the full body via getEmail — there is no separate quoted-history-stripped field (see gaps).

Minimal viable migration

The smallest swap that keeps a working agent running is just create inbox + send + read inbound. You do not need tenant-user fan-out, Account Kits, or webhooks to make your first call.
1

Install the SDK and set your key

Set NAIVE_API_KEY (a server-side key from the dashboard).
2

Confirm you have an active domain

Naive auto-provisions a system domain on registration. Check GET /v1/domains; if it shows pending_dns, run POST /v1/domains/:id/verify. Bringing your own domain is the Domains flow.
3

Swap inbox creation

Replace client.inboxes.create({ username }) with client.email.createInbox({ local_part }). Reuse your AgentMail username as the local_part. Keep the returned inbox.id.
4

Swap send

Replace client.inboxes.messages.send(inboxId, { to, subject, text, html }) with client.email.send({ from_inbox, to, subject, body }). Collapse text/html into one body.
5

Swap inbound

Replace messages.list(inboxId) / the message.received webhook with client.email.inbox({ inboxId }) / the email.received webhook.
6

Ship it

At this point you are off AgentMail for the core send/receive path. Everything below is upside, not a requirement.

Consolidate further once you’re on Naive

This is where the migration pays for itself. In AgentMail, the API key isolates email and nothing else. On Naive, the unit of isolation is a tenant user, and it isolates the agent’s entire footprint.

Gain #1 — one identity across primitives

  • With AgentMail, the agent’s inboxes are an island. With Naive, naive.forUser(acme.id) is a single handle to email and cards and vault and connections and KYC.
  • You provision a customer’s whole agent footprint from one identity, and tear it down from one place. Sibling tenants can never read each other’s inboxes, cards, or secrets.

Gain #2 — execution-time permission enforcement

  • Whether an agent may send mail — and whether a send freezes for human review — is policy on the Account Kit, not a check you hand-write and not a separate draft-review service.
  • The agent’s code is identical for every tier — client.email.send({ ... }). Whether the call runs is decided by the caller’s kit at execution time:
    • primitives_config.email.enabled: false makes the exact same line return forbidden, with no code change on your side.
    • requiresApproval: true freezes the send until a human approves — the API replays it only after approval. This is Naive’s answer to AgentMail’s human-in-the-loop drafts: the review gate is policy, applied to the live call, not a separate object you build a UI around.

Gain #3 — unified accountability

  • Every inbox, send, and inbound reply for a customer lands in one per-user activity log — alongside their card, vault, and connection events, not in a separate mail dashboard:
  • That is the question that is hard to answer when mail lives in AgentMail, cards live in Stripe, and secrets live somewhere else. Under Naive it is a single query.

What does not map yet

A migration guide that hides gaps is worse than none. The core path (create inbox → send → read inbound → webhook) maps cleanly, but the following AgentMail features have no direct equivalent on Naive’s email primitive today. Check this list against your app before you commit.
If your agent depends on threads/replies as first-class objects, labels, per-recipient allow/block lists, full-text search, or attachments, those are the gaps most likely to matter. The create-inbox → send → receive loop with Account-Kit governance — the most common agent email pattern — maps cleanly today. Human-in-the-loop maps, but as an approval gate rather than a draft object.

Where to go next