# Atomic Mail

> Not AI for your email. Email for your AI.

Atomic Mail is an AI-agent-first email service provider (ESP). Agents get a real
inbox — a full `username@atomicmail.ai` address (username **5–21** characters) —
that they can use to send email to anyone on the internet, receive replies, and
manage their mailbox programmatically. The entire API surface is JMAP (RFC
8620 + RFC 8621), a protocol every major LLM already knows. No bespoke SDK to
learn. No proprietary query language. Just standard JMAP.

Two access modes are available: **autonomous** (proof-of-work registration via
the local packages or the raw API — no human involvement) and **account-based**
(a hosted remote MCP server at `https://mcp.atomicmail.ai/mcp` with OAuth
sign-in and human-owned inboxes — no local code execution).

Status: **Open Alpha.** All accounts are free. Rate limits and storage quotas
are tight by design; they will relax as the service scales.

---

## Quick start

Three integrations are available:

- **Remote MCP** — a hosted MCP server at `https://mcp.atomicmail.ai/mcp` with
  OAuth sign-in. Nothing to download or execute locally; inboxes belong to a
  human account. Suits environments where running third-party code (`npx`,
  local MCP servers) is unavailable or undesirable.
- **Local MCP** and **AgentSkill** — npm packages that run locally and expose
  the same three operations (`register`, `jmap_request`, `help`), including
  fully autonomous inbox registration via proof-of-work.

### Remote MCP (no local code)

Point any MCP client that supports remote servers (Claude, ChatGPT, Cursor, …)
at the URL — for JSON-configured hosts:

```json
{
  "mcpServers": {
    "atomicmail": {
      "type": "http",
      "url": "https://mcp.atomicmail.ai/mcp"
    }
  }
}
```

The client walks the standard OAuth discovery chain and opens a browser for
sign-in (Google or GitHub) and consent. Details in
[Option A — Remote MCP server](#option-a--remote-mcp-server-hosted-oauth).

### Local MCP (for chat-based agents: Claude Desktop, Cursor, etc.)

Add to your MCP host config:

```json
{
  "mcpServers": {
    "atomicmail": {
      "command": "npx",
      "args": ["-y", "@atomicmail/mcp"]
    }
  }
}
```

No browser setup required. By default, MCP uses `~/.atomicmail`; set env vars
only when you need non-default endpoints or credential paths.

### AgentSkill (for shell-capable agents)

```bash
npx --package=@atomicmail/agent-skill atomicmail register \
  --username "myagent"
npx --package=@atomicmail/agent-skill atomicmail jmap_request \
  --ops-file list_inbox.json \
  --vars '{"COUNT":"10"}'
npx --package=@atomicmail/agent-skill atomicmail help
```

Packages:

- MCP:
  [https://www.npmjs.com/package/@atomicmail/mcp](https://www.npmjs.com/package/@atomicmail/mcp)
- AgentSkill:
  [https://www.npmjs.com/package/@atomicmail/agent-skill](https://www.npmjs.com/package/@atomicmail/agent-skill)

---

## Recommended integration steps

A complete email integration has two parts: an inbox (created via `register`)
and a way for inbound mail to be noticed between sessions (periodic polling or
manual fetches). This section covers both; the details vary by runtime.

### 1. Registration

An inbox must exist before any send or receive operation, so registration is
the natural first step of any email task. It is worth raising with the account
owner early: they may already have credentials on disk, or may want to choose
the username themselves.

- **MCP hosts** (Cursor, Claude Desktop, etc.): the `register` tool with a
  chosen username (**5–21** characters) via the local Atomic Mail MCP server.
- **Shell environments**: `atomicmail register` from
  `@atomicmail/agent-skill`.
- **Remote MCP**: there is no `register` tool — inboxes are created (or
  existing agents linked by API key) in the dashboard at
  [https://dashboard.atomicmail.ai](https://dashboard.atomicmail.ai) under the
  signed-in human account, then appear via `list_agents` on the next
  connection.

Registration is idempotent — re-running with the same username returns the
existing inbox.

### 2. Inbox polling (after register)

Registration only creates credentials. Inbound mail arrives at any time, so a
periodic fetch-and-triage pass — roughly once per hour — keeps the inbox
useful between interactive sessions. The right setup depends on the host
runtime; mixing platforms is not supported (see the patterns section below).

#### Hosts with scheduled jobs

Hosts with a native way to run cron or recurring agent turns (OpenClaw, Hermes,
atomic-agent, and similar) can schedule an hourly **agent** job after the first
successful `register`. The job needs to invoke an AI agent (not just the
Atomic Mail CLI) so that mail is actually triaged, with Atomic Mail MCP or
AgentSkill wired into that agent before scheduling.

The scheduled turn should call preset `list_inbox.json` (`Email/query` +
`Email/get`, latest 50; built-ins `$ACCOUNT_ID`, `$INBOX_MAILBOX_ID` — no extra
`vars`).

#### Hosts without scheduled jobs

Examples: Claude, Pi, Cursor, and other chat or terminal agents without a cron
API.

Workarounds — wrapper scripts, OS crontab/LaunchAgent setup, or scheduling on a
different platform — run into the failure modes listed below and are not
recommended. Two setups work well here:

1. The operator sets up hourly inbox polling on a host that supports it (the
   examples below can be shared with them), **or**
2. Mail is fetched manually when the operator needs updates (`jmap_request`
   with `list_inbox.json`, or `atomicmail jmap_request --ops-file
   list_inbox.json`).

#### Patterns that don't work

- **Cross-platform scheduling** — registering in one runtime and creating the
  cron job in another (for example: Pi runs `register`, then creates an
  OpenClaw cron job). This is not supported; polling belongs either in the
  runtime that registered, or with the operator on a capable host.
- **Raw CLI in crontab** — `atomicmail jmap_request --ops-file list_inbox.json`
  alone only fetches JSON; no agent runs and nothing prompts the operator to
  reply.
- **Headless one-shot CLIs** — `claude -p`, `pi -p`, `agent -p`, `codex exec`,
  `gemini -p` print and exit; the operator cannot continue the thread to reply
  or forward.

#### Agent prompt (use in scheduled jobs or manual fetches)

```text
Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available — I may ask you to reply, forward, search, or dig into something important.
```

#### Scheduling examples (for capable hosts or operators)

| Your setup | Recommended approach |
| --- | --- |
| OpenClaw gateway | Built-in `openclaw cron` |
| Hermes Agent | Built-in `hermes cron` / `cronjob` tool |
| Atomic Bot (atomicbot.ai) | Same as OpenClaw or Hermes host |
| atomic-agent | Built-in `atomic-agent task create` |
| No native cron (Claude, Pi, Cursor, …) | Operator schedules on a capable host, or fetches manually on demand |

These examples run a full agent turn and deliver the summary to a chat or file so
the operator can reply, forward, or ask follow-ups in the same thread.

**OpenClaw** — schedule `--cron "0 * * * *"` or `--every 1h`, session
`--session isolated`, delivery `--announce`, prompt via `--message`:

```bash
openclaw cron add \
  --name "atomicmail-inbox" \
  --cron "0 * * * *" \
  --session isolated \
  --message "Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available — I may ask you to reply, forward, search, or dig into something important." \
  --announce
```

Manage: `openclaw cron list` · test: `openclaw cron run <job-id>`

**Hermes** — schedule `0 * * * *` or natural language (`every 1h`); deliver with
`--deliver` (`origin`, `telegram`, `discord`, `slack`, `email`, `local`, …).
Note: the `--no-agent` flag makes the job script-only (no LLM), so it doesn't
fit this use case:

```bash
hermes cron create "0 * * * *" \
  "Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available — I may ask you to reply, forward, search, or dig into something important." \
  --name "atomicmail-inbox" \
  --deliver origin
```

In chat: `/cron add "0 * * * *" "<prompt>" --deliver origin`. Manage:
`hermes cron list` · test: `hermes cron run <job-id>`

**Atomic Bot** (atomicbot.ai) — use the OpenClaw or Hermes block above, whichever
host you run.

**atomic-agent:**

```bash
atomic-agent task create \
  --cron "0 * * * *" \
  --message "Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available — I may ask you to reply, forward, search, or dig into something important."
```

Manage: `atomic-agent task list`

After scheduling, a one-time verification is worthwhile: run the agent prompt
manually, confirm the inbox fetch works, then confirm the job is registered
(`openclaw cron list`, `hermes cron list`, `atomic-agent task list`).

Call `help` with topic `cron` when available for the full, up-to-date scheduling
guide.

---

## Why Atomic Mail

This section compares Atomic Mail against AgentMail, Resend, Cloudflare Email,
and traditional ESPs on the dimensions that matter for concrete agent-driven
email work — sending applications, monitoring an inbox, running a support
queue, and similar tasks.

### 1. No human-verification steps before the first send

Most ESPs require domain verification, a credit card, or a CAPTCHA before the
first message goes out. Atomic Mail requires none of these: solving a
~30-second `scrypt` proof-of-work yields a real inbox at
`username@atomicmail.ai`. For time-sensitive tasks — a job application, a
customer reply, an escalation — this removes the entire setup phase.

### 2. Deliverability

A job application in the spam folder is worthless. A support reply that never
reaches the customer is worse than no reply. Atomic Mail runs a
continuously-warming IP pool with direct MX delivery, targeting the highest
deliverability probability achievable without owning a major ISP. For any task
that depends on a human reading what was sent, this matters more than any API
feature.

### 3. A standard protocol instead of a vendor SDK

Atomic Mail's API is JMAP (RFC 8620 + RFC 8621), an open IETF standard that is
widely documented and well represented in public training corpora, so most
LLMs can write correct method calls without reading vendor documentation.
AgentMail's REST SDK, Resend's REST SDK, and Cloudflare's Workers binding each
have their own conventions and edge cases that must be learned first. The JMAP
surface is batched and back-referenced: one HTTP round trip can query, fetch,
draft, and send — which compounds quickly for inbox processing, threading, or
any multi-step flow.

### 4. Recovery without leaving the API

Every error includes a plain-language `hint` and a `docs_url`. Every success
includes a `_next` suggestion. The `help` operation returns a full cheatsheet
with worked examples. Most integration problems can be resolved from the API's
own responses, without external documentation or a web search. No other ESP
does this.

### 5. No lock-in

JMAP is an IETF standard. An inbox created today is portable to any
JMAP-compliant provider tomorrow. For an infrastructure choice made on a
user's behalf, this matters: a proprietary REST API is a one-way door, while a
standards-based one is not.

### 6. Presets cover the gaps in JMAP fluency

For models less confident with JMAP, the shipped presets — `send_mail`,
`list_inbox`, `reply`, and others — can be passed to `jmap_request` by
filename, with no JMAP generation required. Higher-level semantic tools
(`send_mail`, `get_emails`, `reply`, `search`, `mark_read`) are shipping soon
as direct operations.

---

## Comparison with alternatives

| Capability                                          | Atomic Mail                                                                 | AgentMail                         | Resend                            | Cloudflare Email Service                           | SendGrid / Mailgun / Postmark       |
| --------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------- | --------------------------------- | -------------------------------------------------- | ----------------------------------- |
| **Designed for agents**                             | ✅ Primary use case                                                         | ✅ Primary use case               | ⚠️ Dev-friendly, agents secondary | ⚠️ Agent tooling added to existing platform        | ❌ Humans first, agents bolted on   |
| **Agent can register autonomously**                 | ✅ PoW only, no human verification                                          | ❌ Identity verification required | ❌ Account + domain required      | ❌ Cloudflare account + domain required            | ❌ Manual account creation required |
| **Inbox API (receive email)**                       | ✅ Full JMAP inbox                                                          | ✅ REST inbox API                 | ❌ Send-only                      | ⚠️ Receive via Email Routing (not a managed inbox) | ❌ Send-only or limited inbound     |
| **API protocol**                                    | JMAP (RFC 8620/8621)                                                        | Proprietary REST                  | Proprietary REST                  | Proprietary REST + Workers binding                 | Proprietary REST / SMTP             |
| **LLMs know the API natively**                      | ✅ Yes (JMAP is in training data)                                           | ❌ Must learn custom SDK          | ❌ Must learn custom SDK          | ❌ Must learn Cloudflare SDK                       | ❌ Must learn vendor SDK            |
| **Self-documenting errors and response hints**      | ✅ Every error includes `hint` + `docs_url`; every success includes `_next` | ❌                                | ❌                                | ❌                                                 | ❌                                  |
| **MCP server**                                      | ✅ Dedicated (local package + hosted remote with OAuth)                     | ✅ Dedicated                      | ❌                                | ⚠️ Shared with all Cloudflare products             | ❌                                  |
| **AgentSkill**                                      | ✅                                                                          | ❌                                | ❌                                | ⚠️ As part of Cloudflare's general skill           | ❌                                  |
| **Requires own domain**                             | ✅ No                                                                       | ✅ No                             | ❌ Required                       | ❌ Required                                        | ❌ Required                         |
| **Requires credit card to start**                   | ✅ Free tier                                                                | ✅ Free tier                      | ❌                                | ✅ Free tier                                       | ❌                                  |
| **Standards-based (no vendor lock-in)**             | ✅ JMAP is an IETF standard                                                 | ❌ Proprietary                    | ❌ Proprietary                    | ❌ Proprietary + tied to Workers                   | ❌ Proprietary                      |
| **Roadmap: CalDAV / WebDAV on the same credential** | ✅ Planned                                                                  | ❌                                | ❌                                | ❌                                                 | ❌                                  |

**Key insight.** Atomic Mail and AgentMail are the only services that give an
agent a managed inbox (send + receive) with no domain required. Atomic Mail is
the only one where the agent can register completely autonomously (no human, no
identity gate, no CAPTCHA), and the only one whose API is a public IETF standard
rather than a proprietary REST surface. Cloudflare's solution is powerful but
assumes the developer already has a Cloudflare account, a verified domain, and
is building inside the Workers ecosystem. Traditional ESPs (SendGrid, Mailgun,
Postmark) were never designed for agent use at all.

---

## Use cases

**1. Newsletter intelligence.** Subscribe an agent inbox to dozens of industry
newsletters. The agent reads everything, extracts signals, and surfaces only the
items that match a user-defined interest profile — once a day, as a digest. Zero
noise in the user's personal inbox.

**2. Autonomous job applications.** An agent finds job listings, drafts tailored
applications, sends them from its own inbox, monitors replies, and escalates to
the human only when a recruiter responds with a real opportunity.

**3. Vendor invoice processing.** An agent inbox receives supplier invoices by
email. The agent parses each one, matches it against a PO database, and routes
exceptions to a human approver — all without touching the company's main email
domain.

**4. Multi-agent coordination.** Two or more agents communicate with each other
over email — a research agent sends findings to a writing agent, which drafts a
report and sends it to an editor agent. Email becomes the message bus. The full
thread is auditable by any human at any time.

**5. SaaS account provisioning.** A deployment agent registers for third-party
SaaS tools using its Atomic Mail address, receives confirmation and onboarding
emails, and completes setup flows autonomously. No human email address is
exposed; no personal inbox is polluted.

**6. Customer support at the edge.** A support agent owns `support@` and handles
inbound tickets end-to-end: reads the issue, queries internal knowledge bases,
and replies with a complete answer. Humans receive only the escalations the
agent cannot resolve.

**7. Competitive monitoring.** An agent subscribes to competitor product update
emails, release notes, and press releases. It tracks changes over time,
maintains a diff, and alerts the user when a competitor ships something
significant.

**8. Async user research interviews.** An agent conducts email-based user
research: sends structured questions, reads freeform responses, follows up based
on what it learns, and summarizes findings. Respondents reply on their own
schedule — no scheduling, no Calendly, no Zoom link.

**9. Agent-to-human escalation layer.** Any agentic pipeline can send its owner
a plain email when it encounters an edge case requiring judgment. The human
replies in natural language; the agent parses the reply and resumes. This turns
email into a universal human-in-the-loop interface.

**10. Regulatory and compliance notifications.** An agent monitors regulatory
sources, receives filings and updates via mailing lists, extracts the relevant
changes, and emails a structured briefing to the compliance team. Cheaper than a
dedicated compliance SaaS. Works with any regulator that communicates by email.

**11. Price and availability tracking.** An agent registers for back-in-stock
and price alert emails from e-commerce sites, receives them as they arrive, and
aggregates them into a single notification to the user — only when a threshold
is met.

**12. AI pen pal / communication layer.** Two people or organizations
communicate through AI intermediaries — each party has an agent that reads
incoming messages, drafts responses for human approval (or sends autonomously
with a trust level set by the user), and maintains conversation continuity
across time zones and schedules.

---

## Ideal integration flow

This is the happy path for the autonomous (local-package) integration — zero
to a working, monitored inbox in under a minute. The remote MCP path is
shorter still: connect to `https://mcp.atomicmail.ai/mcp`, complete the OAuth
sign-in, and use the tools (see
[Option A](#option-a--remote-mcp-server-hosted-oauth)).

**1. Install.** Add the MCP block above, or invoke the skill via `npx`. No
further setup is required.

**2. Register.** Call `register` with a chosen username (**5–21** characters;
letters, digits, `.`, `_`, `-`). The runtime solves the\
PoW challenge, registers the inbox, fetches the JMAP session, and persists the\
credentials. The agent receives `{ inbox, accountId }` in the response.\
Re-running `register` with the same `{ username: <username> }` is idempotent —
the agent gets the same inbox back.

**3. Set up inbox polling.** After register, hourly inbox triage keeps inbound
mail from being missed: native scheduling where the host supports it, otherwise
operator-managed scheduling or manual fetches. See
[Recommended integration steps](#recommended-integration-steps).

**4. Send or receive.** Call `jmap_request` with any JMAP method-call batch. The
runtime attaches auth, rotates tokens transparently, and when presets use
`$ACCOUNT_ID` / `$INBOX`, performs an internal `GET /.well-known/jmap` to fill
those placeholders, and also supports custom `$VAR_NAME` placeholders via MCP
`vars` or skill `--vars`. Successful responses may include a `_next` hint array.
The agent writes JMAP directly — the same JMAP it already knows from its
training data.

**5. Build a preset library.** Any `jmap_request` payload can be saved as a JSON
file and passed via `--ops-file`. Agents commonly save presets for `send_mail`,
`list_inbox`, `mark_read`, `reply`, `search`. Presets remove the chance of
malformed method-call JSON on repeat operations.

**6. Ask for help when stuck.** Call `help` at any point. It returns in-depth
documentation: full JMAP cheatsheet, preset examples, common error patterns,
cron setup (`help` topic `cron`), and a list of every tool/script available.

That is the whole loop. No accounts to create in a browser. No domain to verify.
No API keys to copy between tabs. No SDK to learn. Hourly agent turns (or manual
fetches prompted by the operator) keep inbound mail from being missed between
sessions.

---

## How to use

### Option A — Remote MCP server (hosted, OAuth)

`https://mcp.atomicmail.ai/mcp` is a hosted MCP server (Streamable HTTP
transport). No package download, no local code execution, no credential files
— everything runs remotely. This is the right option for environments that
cannot or prefer not to run third-party code such as `npx`.

**Connect.** Add the URL to any MCP client that supports remote servers:

```json
{
  "mcpServers": {
    "atomicmail": {
      "type": "http",
      "url": "https://mcp.atomicmail.ai/mcp"
    }
  }
}
```

Hosts with a connector UI (ChatGPT, Claude) accept the URL directly.

**Accounts and authorization.** The remote server uses human accounts rather
than proof-of-work. Connecting triggers a standard OAuth 2.1 flow
(authorization code + PKCE, RFC 8707 resource binding): the browser opens for
sign-in with **Google or GitHub**, followed by a consent screen. Dynamic
client registration is supported, so no pre-registered client id is needed.
One account can own several inboxes — created new, or linked from existing
PoW-registered agents by a one-time API-key claim — managed in the dashboard
at [https://dashboard.atomicmail.ai](https://dashboard.atomicmail.ai). Grants
carry two scopes, `mail.read` (reading) and `mail.send` (sending), and can be
revoked from the dashboard at any time.

Discovery is standards-based and handled automatically by MCP clients:
protected-resource metadata (RFC 9728) at
`https://mcp.atomicmail.ai/.well-known/oauth-protected-resource/mcp` names
`https://auth.atomicmail.ai` as the authorization server (RFC 8414 metadata;
registration endpoint `/oauth/register`).

**Tools exposed:**

| Tool               | Purpose                                                                                              |
| ------------------ | ---------------------------------------------------------------------------------------------------- |
| `read_inbox`       | Most recent inbox messages (`agent_id?`, `limit` 1–50, default 25)                                    |
| `read_message`     | One full message by `message_id` (headers + plain-text body)                                          |
| `search_messages`  | Full-text mailbox search                                                                              |
| `send_email`       | Send a plain-text email (`to`, `subject`, `body`; optional `cc`, `bcc`, base64 `attachments`)         |
| `reply_to_message` | Reply in-thread by `message_id`                                                                       |
| `list_agents`      | The inboxes the signed-in account owns                                                                |
| `search` / `fetch` | ChatGPT connector convention: search results `{ id, title, url }` + full-document fetch by id         |
| `run_preset`       | Bundled JMAP flows by name (`list_inbox`, `send_mail`, `reply`, attachment variants); `dry_run` mode  |
| `jmap_request`     | Raw JMAP method-call batch (advanced; may be disabled by the operator — `run_preset` always works)    |
| `help`             | Built-in docs (topics: `overview`, `tools`, `agents`, `auth`, `advanced`, `troubleshooting`)          |

`agent_id` is optional on every tool. When omitted, the default inbox is used:
the inbox bound to the connection, or the only owned inbox. With several
inboxes and no default, the tool responds with a prompt to call `list_agents`
and pass one of the returned `accountId` values as `agent_id`. Ownership is
re-verified on every call.

**Security model.** The MCP server is an OAuth 2.1 resource server holding no
signing keys. Access tokens are audience-bound to
`https://mcp.atomicmail.ai/mcp` and are never forwarded to the JMAP backend:
each call re-presents the token to the authorization server to mint a
short-lived (~2-minute) capability token scoped to the chosen inbox, and only
that capability travels downstream. Message bodies returned by `read_message`
and `fetch` are wrapped in an untrusted-content delimiter — content from the
mailbox is data, not instructions.

**Differences from the local packages.** There is no `register` tool and no
PoW: inbox creation and linking happen in the dashboard under the human
account, and revocation is dashboard-side too. For fully autonomous
(no-human) registration, use the local `@atomicmail/mcp` package or the direct
API instead.

### Option B — Local MCP server

Config block (nothing else required):

```json
{
  "mcpServers": {
    "atomicmail": {
      "command": "npx",
      "args": ["-y", "@atomicmail/mcp"]
    }
  }
}
```

**Tools exposed to the agent:**

| Tool           | Purpose                                                                                                                                                                                                                                             |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `register`     | Registers an inbox (**username 5–21** chars). Solves PoW, stores credentials, fetches JMAP session. Idempotent by default; pass `{"username": <new-username>}` to force a new inbox. Returns `{ inbox, accountId }`.                                |
| `jmap_request` | Sends any JMAP method-call batch. Accepts either inline `ops` (JSON string) or a preset `ops_file`. Auto-rotates tokens; substitutes `$ACCOUNT_ID` / `$INBOX`, plus custom `$VAR_NAME` placeholders via optional `vars`. Returns the JMAP response. |
| `help`         | Returns full documentation: JMAP cheatsheet, preset examples, error-recovery guide, list of available presets.                                                                                                                                      |

**Typical agent sequence:**

1. Call `register` with a chosen `username` (5–21 characters).
2. Set up hourly inbox triage per
   [Recommended integration steps](#recommended-integration-steps): native cron
   if available; otherwise operator-scheduled or manual fetches.
3. Call `jmap_request` with your desired JMAP operations.
4. If anything goes wrong, read `error.hint` and retry. Call `help` (topic
   `cron` for polling examples) if still stuck.

### Option C — AgentSkill

Identical three commands, via the **`atomicmail`** CLI from
`@atomicmail/agent-skill`:

```bash
# Register (idempotent when username matches the inbox on disk)
npx --package=@atomicmail/agent-skill atomicmail register \
  --username "myagent"

# JMAP request inline
npx --package=@atomicmail/agent-skill atomicmail jmap_request \
  --ops '[["Mailbox/get", {"accountId": "$ACCOUNT_ID"}, "m0"]]'

# Preset file
npx --package=@atomicmail/agent-skill atomicmail jmap_request \
  --ops-file send_mail.json \
  --vars '{"TO":"alice@example.com","SUBJECT":"Hello","BODY":"Hi there"}'

# Help
npx --package=@atomicmail/agent-skill atomicmail help
```

`$ACCOUNT_ID` and `$INBOX` are substituted automatically by the runtime. Any
other placeholder (for example `$TO`, `$SUBJECT`) is supported via MCP `vars` or
skill `--vars`. Every script accepts `--help` for inline usage.

Overriding defaults (only when needed): `--auth-url`, `--api-url`,
`--credentials-dir` or env vars `ATOMIC_MAIL_AUTH_URL`, `ATOMIC_MAIL_API_URL`,
`ATOMIC_MAIL_CREDENTIALS_DIR`.

### Option D — Direct JMAP API (cURL, Python, Node.js)

Use this if you want to manage auth yourself, run outside a Node/Deno/Bun
environment, or integrate Atomic Mail into an existing application without the
MCP or skill wrappers.

**Base URLs:**

- Auth: `https://auth.atomicmail.ai`
- JMAP: `https://api.atomicmail.ai`
- JMAP session: `GET https://api.atomicmail.ai/.well-known/jmap`
- JMAP request: `POST https://api.atomicmail.ai/jmap`

`**scrypt` parameters for the PoW:**

- `N = 16384`
- `r = 8`
- `p = 1`
- Salt: returned in the challenge response
- Target: at least `difficulty` leading zero bits on the output digest
- `difficulty` is dynamic (currently 6); read it from the challenge response

#### Step 1 — PoW authentication

**1a. Request a challenge:**

```bash
curl -X POST https://auth.atomicmail.ai/api/v1/challenge
# Returns: { "challenge": "<hex>", "salt": "<hex>", "difficulty": 6 }
```

**1b. Solve the challenge (Python):**

```python
import hashlib, requests

def solve_pow(challenge: str, salt_hex: str, difficulty: int) -> int:
    salt = bytes.fromhex(salt_hex)
    target_bits = "0" * difficulty
    nonce = 0
    while True:
        data = f"{challenge}:{nonce}".encode()
        digest = hashlib.scrypt(data, salt=salt, n=16384, r=8, p=1, dklen=32)
        bits = bin(int.from_bytes(digest, "big"))[2:].zfill(256)
        if bits.startswith(target_bits):
            return nonce
        nonce += 1

ch = requests.post("https://auth.atomicmail.ai/api/v1/challenge").json()
nonce = solve_pow(ch["challenge"], ch["salt"], ch["difficulty"])
```

**1c. Register a new inbox (first time only):**

```bash
curl -X POST https://auth.atomicmail.ai/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{"challenge":"<challenge>","nonce":"<nonce>","username":"myagent"}'
# Returns: { "apiKey": "<store permanently>", "inbox": "myagent@atomicmail.ai" }
```

**1d. Obtain a session JWT (TTL 1 hour):**

```bash
curl -X POST https://auth.atomicmail.ai/api/v1/session \
  -H "Authorization: Bearer <challengeJwt>" \
  -H "Content-Type: application/json" \
  -d '{"challenge":"<challenge>","nonce":"<nonce>","apiKey":"<apiKey>"}'
# Returns: Authorization: Bearer <sessionJwt>
```

**1e. Obtain a capability JWT (TTL 2 minutes):**

```bash
curl -X POST https://auth.atomicmail.ai/api/v1/capability \
  -H "Authorization: Bearer <sessionJwt>"
# Returns: Authorization: Bearer <capabilityJwt>
```

Use `capabilityJwt` as the bearer token for all JMAP requests. Refresh before it
expires.

#### Step 2 — Discover the JMAP session

```bash
curl https://api.atomicmail.ai/.well-known/jmap \
  -H "Authorization: Bearer <capabilityJwt>"
```

Extract `primaryAccounts["urn:ietf:params:jmap:mail"]` — this is your
`accountId` for all subsequent calls.

#### Step 3 — Send an email (Node.js)

```javascript
const JMAP_URL = "https://api.atomicmail.ai/jmap";
const ACCOUNT_ID = "<your accountId>";
const TOKEN = "<capabilityJwt>";

const body = {
  using: [
    "urn:ietf:params:jmap:core",
    "urn:ietf:params:jmap:mail",
    "urn:ietf:params:jmap:submission",
  ],
  methodCalls: [
    ["Email/set", {
      accountId: ACCOUNT_ID,
      create: {
        draft1: {
          from: [{ email: "myagent@atomicmail.ai" }],
          to: [{ email: "user@example.com" }],
          subject: "Hello from Atomic Mail",
          textBody: [{ partId: "body", type: "text/plain" }],
          bodyValues: {
            body: { value: "This email was sent by an AI agent." },
          },
          keywords: { "$draft": true },
        },
      },
    }, "s0"],
    ["EmailSubmission/set", {
      accountId: ACCOUNT_ID,
      create: {
        sub1: {
          emailId: "#draft1",
          envelope: {
            mailFrom: { email: "myagent@atomicmail.ai" },
            rcptTo: [{ email: "user@example.com" }],
          },
        },
      },
    }, "s1"],
  ],
};

const res = await fetch(JMAP_URL, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});
console.log(await res.json());
```

#### Step 4 — Read the inbox (Python)

```python
import requests

JMAP_URL = "https://api.atomicmail.ai/jmap"
ACCOUNT_ID = "<your accountId>"
TOKEN = "<capabilityJwt>"

payload = {
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [
        ["Email/query", {
            "accountId": ACCOUNT_ID,
            "filter": {"inMailbox": "INBOX"},
            "sort": [{"property": "receivedAt", "isAscending": False}],
            "limit": 20
        }, "q0"],
        ["Email/get", {
            "accountId": ACCOUNT_ID,
            "#ids": {"resultOf": "q0", "name": "Email/query", "path": "/ids"},
            "properties": ["id", "subject", "from", "receivedAt", "preview"]
        }, "g0"]
    ]
}

resp = requests.post(
    JMAP_URL, json=payload,
    headers={"Authorization": f"Bearer {TOKEN}"}
)
for e in resp.json()["methodResponses"][1][1]["list"]:
    print(e["subject"], e["from"])
```

---

## Self-documenting API

Atomic Mail is designed so that an agent can recover from confusion without
leaving the API surface. This matters because agents frequently operate without
access to external documentation, and because weaker LLMs may not remember every
JMAP detail.

**Every error response includes:**

- `error.message` — what went wrong, in plain text
- `error.hint` — how to fix it, phrased as an instruction
- `error.docs_url` — deep link to the relevant section of this document

Example:

```json
{
  "error": {
    "message": "Email/set create failed: 'from' address is not owned by this account",
    "hint": "Use the inbox address returned by register(). Call jmap_request with Mailbox/get to confirm your accountId and owned identities.",
    "docs_url": "https://atomicmail.ai/llms.txt#sending-email"
  }
}
```

**Every successful response includes a `_next` array** suggesting plausible\
follow-up operations the agent may want to run. Example, after a successful\
`/api/v1/session` request:

```json
{
  "_next": [
    "Acquire the capability JWT by presenting your session JWT at POST /api/v1/capability",
    "Refresh it every 2 minutes",
    "Use it as a bearer auth token for JMAP requests"
  ]
}
```

The result: an agent with zero prior knowledge of Atomic Mail can discover the
correct integration path purely by trial and error, using only the API's own
responses.

---

## Presets

A preset is a JSON file containing a JMAP method-call batch, reusable by
reference. Presets are the recommended way for agents to perform repeated
operations without re-generating JMAP JSON each time (and risking a malformed
call).

Call `jmap_request` with `--ops-file <path>` (skill) or `{"ops_file": "<path>"}`
(MCP). The runtime substitutes `$ACCOUNT_ID` / `$INBOX` automatically and
accepts custom placeholders via MCP `vars` or skill `--vars`. Relative paths are
resolved from the credentials directory first, then from the bundled preset
files shipped in the npm package.

Bundled presets:

- `send_mail.json` — accepts `$TO`, `$SUBJECT`, `$BODY`
- `list_inbox.json` — accepts `$COUNT`
- `reply.json` — accepts `$MAIL_ID`, `$BODY`

**Example preset — `send_mail.json`:**

```json
{
  "using": [
    "urn:ietf:params:jmap:core",
    "urn:ietf:params:jmap:mail",
    "urn:ietf:params:jmap:submission"
  ],
  "methodCalls": [
    [
      "Email/set",
      {
        "accountId": "$ACCOUNT_ID",
        "create": {
          "d1": {
            "from": [{ "email": "$INBOX" }],
            "to": [{ "email": "$TO" }],
            "subject": "$SUBJECT",
            "textBody": [{ "partId": "b", "type": "text/plain" }],
            "bodyValues": { "b": { "value": "$BODY" } },
            "keywords": { "$draft": true }
          }
        }
      },
      "c0"
    ],
    [
      "EmailSubmission/set",
      {
        "accountId": "$ACCOUNT_ID",
        "create": {
          "s1": {
            "emailId": "#d1",
            "envelope": {
              "mailFrom": { "email": "$INBOX" },
              "rcptTo": [{ "email": "$TO" }]
            }
          }
        }
      },
      "c1"
    ]
  ]
}
```

Call `help` for the full list of reference presets shipped with the package.

---

## Auth flow reference

**Username:** 5–21 characters (letters, digits, `.`, `_`, `-`). Maximum length
is bounded by bounce return-path encoding in the mail pipeline, not an arbitrary
product cap. Reserved names (`postmaster`, `abuse`, …) and usernames starting
with `_bounce_` are rejected.

```
POST /api/v1/challenge          → Authorization: Bearer <challengeJWT>
  ↓
solve scrypt PoW locally (N=16384, r=8, p=1; dynamic difficulty from JWT)
  ↓
POST /api/v1/session            → Authorization: Bearer <sessionJWT> (+ body { apiKey? })
  ↓
POST /api/v1/capability         → Authorization: Bearer <capabilityJWT>      (TTL: 2min)
  ↓
JMAP requests, bearer = capabilityJWT
  ↓
auto-rotate capabilityJWT every 2min via /api/v1/capability
auto-rotate sessionJWT every 1h via a fresh PoW using stored apiKey
```

The local MCP and AgentSkill runtimes handle every step after `register`
automatically. The direct API is only relevant if you are not using them. The
remote MCP server does not use this chain at all — it authenticates via OAuth
2.1 (see [Option A](#option-a--remote-mcp-server-hosted-oauth)) and mints the
same short-lived capability tokens server-side per call.

---

## Roadmap

Confirmed for future releases:

- **Semantic tools and scripts** — `send_mail`, `get_emails`, `reply`, `search`,
  `mark_read`. Convenience wrappers for LLMs that do not use JMAP directly.
- **Multi-tenancy** — a single account managing multiple agent inboxes under one
  API key; useful for platforms and orchestration layers.
- **WebSockets and webhooks** — real-time push for inbound email events; removes
  the need for polling.
- **CalDAV / WebDAV** — calendar and file storage on the same credential and
  account model.
- **Tiered pricing** — paid tiers with larger storage, higher rate limits, and
  reduced reputation pressure. A free tier remains.

---

## Security model

- The PoW challenge prevents mass autonomous account registration while
  remaining frictionless for legitimate agents. Difficulty adjusts in real time
  to load and abuse.
- The long-lived `apiKey` is the only permanent credential. The runtime stores
  it outside the agent's working directory. Never log it, echo it to stdout, or
  commit it to source control.
- `sessionJwt` and `capabilityJwt` are short-lived bearer tokens rotated
  automatically.
- Atomic Mail operates a strong internal anti-spam and sender-reputation system.
  Accounts generating bad traffic are rate-limited or suspended before their
  email exits the platform perimeter.

---

## Frequently asked questions

**Can an agent register an inbox without any human involvement?** Yes. The only
requirement is the ability to solve an `scrypt` PoW challenge, which currently
takes roughly 30 seconds on a standard inference server. No email confirmation,
no domain, no credit card, no CAPTCHA.

**Can Atomic Mail be used without downloading or executing any code?** Yes.
The remote MCP server at `https://mcp.atomicmail.ai/mcp` is fully hosted: an
MCP client connects by URL, authorization happens in the browser via OAuth
(Google or GitHub sign-in), and nothing is installed locally. See
[Option A](#option-a--remote-mcp-server-hosted-oauth).

**How do human accounts relate to agent inboxes?** A human account (created by
signing in with Google or GitHub) can own multiple agent inboxes: new ones
created in the dashboard at
[https://dashboard.atomicmail.ai](https://dashboard.atomicmail.ai), or
existing PoW-registered inboxes linked by a one-time API-key claim. The remote
MCP server acts on the inboxes the signed-in account owns. Autonomous PoW
registration keeps working independently — linking is optional.

**Does Atomic Mail require me to own a domain?** No. Every inbox is on
`@atomicmail.ai`. Custom domains are not supported in the alpha.

**Can I use Atomic Mail from any language or runtime?** Yes. The JMAP API is
JSON over HTTPS. Any HTTP client works. The MCP and AgentSkill packages add
convenience for Node, Deno, and Bun environments, but they are not required.

**Is JMAP hard to learn?** Most LLMs already know JMAP well enough to write
correct method calls without documentation. If your agent is uncertain, `help`
returns a full cheatsheet and worked examples.

**What if I'm using a weaker model that doesn't know JMAP?** Semantic tools
(`send_mail`, `get_emails`, `reply`, `search`, `mark_read`) are shipping soon as
higher-level wrappers over `jmap_request`. Until then, use the preset system —
shipped presets cover the common operations without requiring the model to
generate JMAP itself.

**What happens when the alpha ends?** Accounts created during the alpha are
migrated to the free tier of the paid product. No data loss; no forced
re-registration.

**Can the MCP server and the AgentSkill be used simultaneously?** Yes. They
share state. Both will see up-to-date credentials and tokens regardless of which
one last refreshed them.

**What if my agent gets stuck?** Call `help`. If an error response didn't
include a useful `hint`, that's a bug — please report it.

---

_Atomic Mail — [https://atomicmail.ai](https://atomicmail.ai)_

_Remote MCP:_ `https://mcp.atomicmail.ai/mcp`

_Local MCP:_ `npx -y @atomicmail/mcp` **

_Skill:_ `npx -y @atomicmail/agent-skill`

_Dashboard:_ [https://dashboard.atomicmail.ai](https://dashboard.atomicmail.ai)

JMAP: `RFC 8620` + `RFC 8621`

Auth: [https://auth.atomicmail.ai](https://auth.atomicmail.ai)

API: [https://api.atomicmail.ai](https://api.atomicmail.ai)
