# CLI Quickstart

Source: https://molted.email/docs/cli-quickstart

Install the Molted CLI, authenticate your agent, and send your first policy-checked email from the terminal in under two minutes.

## 1. Sign up

> **Note:** Signup is open. If it's ever paused, `auth signup` returns a `403 SIGNUPS_CLOSED` error with a waitlist link to hand a person. Can't run `npx`? The same signup works over plain HTTP: see [Programmatic signup](https://molted.email/docs/authentication.md#programmatic-signup). Already have an account? Use `auth login`.

```bash
npx @molted/cli auth signup \
  --name "My Agent" \
  --email agent@yourapp.com \
  --password your-password
```

This creates your account (a workspace with a default mailbox on the shared `agent.molted.email` domain), mints an API key labelled `cli`, and saves the credentials to `~/.molted/profiles/default.json` (or `~/.molted/profiles/<name>.json` with `--profile <name>`). All future commands authenticate automatically; `MOLTED_API_KEY` in the environment overrides the stored key.

An account created this way is **agent-owned**: no person owns the workspace until a human claims it with a link from `molted auth login-link` (see [Links for your human](https://molted.email/docs/authentication.md#links-for-your-human-take-over-claim-or-sign-in)).

After signup, the CLI runs a brief onboarding sequence -- it fetches your default mailbox, optionally sets an autonomy level (`--autonomy-level 1|2|3`), and prints safety hints and next steps. To skip this and get a minimal JSON response (useful in scripted agent provisioning):

```bash
npx @molted/cli auth signup \
  --name "My Agent" \
  --email agent@yourapp.com \
  --password your-password \
  --skip-onboarding
```

`--skip-onboarding` and `--autonomy-level` are also available on `auth login`, which signs in to an existing account and mints a fresh key each time (older keys stay valid).

Already have an API key from the portal (**Settings > Access**)? Initialize the CLI directly:

```bash
npx @molted/cli auth init --key mm_live_xxx
```

### Joining an existing workspace: `molted adopt`

If someone already runs the workspace, don't sign up: adopt into it. The owner gives your agent an invite token (`molted adopt create` on their side, or the portal), or approves a device-flow request:

```bash
npx @molted/cli adopt claim adopt_...     # with an invite token
npx @molted/cli adopt request --label "sales-agent"   # device flow: prints a URL and code for the owner, then waits
```

Either way the CLI stores a key scoped to what the owner granted, then asks `GET /v1/agent/setup` for your next step. See [Adoption & Device Flow](https://molted.email/docs/adoption.md) for both flows.

## 2. Activate billing

New workspaces start on the trial plan with sends blocked (every send returns `trial_not_activated`). Check with `molted billing status` (`sendBlocked: true`), then activate a plan to unlock sending.

**Paid plan**, `--plan solo` ($19/mo: 10,000 sends a month, 1,000 a day, 10 mailboxes, 3 custom domains) or `--plan team` ($79/mo: 50,000 a month, 5,000 a day, 50 mailboxes, 10 custom domains). A first paid plan starts with a 3-day trial on restricted limits (100 sends, 3 mailboxes, 1 custom domain); add `--skip-trial` to start paying right away:

```bash
npx @molted/cli billing setup --plan solo
```

Open the returned checkout URL to complete payment -- open it yourself or send it to a human. Sends unblock once the checkout completes. `molted auth login-link` hands the workspace to your human: while no person owns it, a take-over email to the account's address when that is their own inbox (they set their own password there) or a claim link (they sign in or create an account and become its owner); otherwise the portal URL. It never signs anyone in.

> **Note:** There is also a no-card **Free plan** (`molted billing activate-free`: 3,000 sends a month, 100 a day after a two-week ramp, 3 mailboxes, no custom domains), but it only activates for a **verified human** account owner. An account created with `auth signup` is agent-owned, so this 403s with `free_plan_requires_human`. For Free, run `molted auth login-link --next "/app/settings#plan"` and follow what it returns; once your human has taken over or claimed the workspace, `activate-free` works with your existing key. Or use `billing setup` above. See [Billing & Plans](https://molted.email/docs/billing.md) for the full rule and error codes.

## 3. Add a sending domain (optional)

Optional for a first send: the default mailbox on the shared `agent.molted.email` domain sends without any DNS setup. Custom domains need a paid plan.

```bash
npx @molted/cli domains add yourapp.com
```

You'll get back a domain ID and DNS records (DKIM, SPF, DMARC) to add at your DNS provider. `domains add` also takes `--open-tracking`, `--click-tracking` and `--tracking-subdomain <name>`. After adding the records, verify using the domain ID from the `add` response:

```bash
npx @molted/cli domains verify <domain-id>
```

> **Note:** A send from a mailbox on a custom domain you haven't verified is blocked with `sender_domain_not_verified`. Complete verification before sending from it.

## 4. Send your first email

```bash
npx @molted/cli send \
  --to recipient@example.com \
  --subject "Hello" \
  --body "First email from the CLI!"
```

The `--to` flag takes a full email address (e.g. `recipient@example.com`).

The `--subject` and `--body` flags are shorthand for sending with the `_default` template, and the CLI generates a dedupe key for you. For HTML email, use `--html` instead of `--body`. For templates with custom payloads, use `--template` and `--payload`:

```bash
npx @molted/cli send \
  --to recipient@example.com \
  --template my-template \
  --dedupe-key "hello-1" \
  --payload '{"subject": "Hello", "firstName": "Ada"}'
```

A successful response:

```json title="Response"
{
  "requestId": "9b2c4e1a-...",
  "status": "queued"
}
```

`status` can also be `scheduled`, `pending_approval` (held for a human, see [Approval Queues](https://molted.email/docs/approval-queues.md)) or `blocked`, with a `reason` (see [Errors](https://molted.email/docs/errors.md)). A dedupe key is used once per workspace: reusing it returns the first request as `blocked` with `reason: "duplicate"`. Other useful flags: `--mailbox <id>` (send from a specific mailbox), `--reason <text>` (stored with the send), `--reply-to <address>`, `--attachments <json>`, and `--dry-run` (the same policy check as `molted send simulate`, nothing delivered).

Add `--no-humanize` to deliver a send exactly as written, or `--humanize --humanize-style casual` to ask for the [humanizer](https://molted.email/docs/humanizer.md) in a specific style. `molted send batch` and `molted threads reply` take the same flags. Run `molted send --help` for the full flag list.

To see how the humanizer would rewrite an email before sending it, preview it (nothing is sent; a preview uses one humanizer credit):

```bash
npx @molted/cli humanizer preview --template welcome --payload '{"firstName": "Ada"}' --diff
```

An applied rewrite comes with a preview id. To send exactly that rewrite, pass it with the same content: `--humanize-preview <previewId>` on `molted send` or `molted threads reply`. See [Preview a rewrite](https://molted.email/docs/humanizer.md#preview-a-rewrite) and [Send exactly what you previewed](https://molted.email/docs/humanizer.md#send-exactly-what-you-previewed).

## 5. Check delivery

```bash
npx @molted/cli trace 9b2c4e1a-...
```

This shows the request with its delivery status, the policy decision, every delivery event (with the provider) and the audit trail (`GET /v1/ops/trace/:requestId`). `molted timeline <requestId>` shows the same send as a timeline.

## Using the CLI without npx

If you prefer, install globally:

```bash
npm install -g @molted/cli
```

Then use `molted` directly instead of `npx @molted/cli`:

```bash
molted send --to recipient@example.com --template _default ...
```

## Updating

```bash
molted update
```

When a newer version is available, `molted update` installs it and shows a changelog summary of what changed between your old version and the new one:

```json title="Response"
{
  "status": "updated",
  "from": "1.13.0",
  "to": "1.16.0",
  "changelog": [
    {
      "version": "1.16.0",
      "date": "2026-04-02",
      "changes": [
        "Add mailbox delete, clone, and stats commands",
        "Add --payload flag to send simulate for policy testing"
      ]
    }
  ],
  "changelogUrl": "https://molted.email/changelog",
  "hint": "Updated successfully. Run `molted --version` to verify."
}
```

In interactive terminals, a human-readable summary is also printed to stderr so you can see what changed at a glance.

## Troubleshooting: molted doctor

To find out whether this key can send yet, or why mail isn't sending, run:

```bash
molted doctor
```

The "can I send?" checks run server-side in `GET /v1/agent/setup` (the same checklist the MCP `setup_status` tool returns): credentials, plan (trial, Free or paid, and whether sends are blocked), holds (an account-level pause, exhausted send quota, held inbound mail), the mailbox a send would use, its sending domain, what policy would do with a first send (sent, held for human approval, or blocked, from the mailbox's real autonomy level), and whether anything has been sent yet. Doctor leads with `READY TO SEND` or `NOT READY TO SEND YET`, the next step and whose move it is (yours, or your human's with the call that makes the link to hand them), then shows each step as a check. It also runs the after-send checks: items waiting for human approval, recent policy blocks, the dead-letter queue, and active alerts.

```json title="Response (--json, trial workspace, trimmed)"
{
  "setup": {
    "readyToSend": false,
    "next": {
      "step": "plan",
      "owner": "human",
      "do": "Ask your human to activate a plan (Free or paid) so sends unblock. Call POST /v1/agent/login-token (`molted auth login-link`) and follow its answer: `kind: \"takeover\"` means Molted emailed the owner address a link to set a password and take this account over (no url: tell your human to check that inbox), `kind: \"claim\"` gives a link to claim the workspace (valid 7 days), `kind: \"portal\"` a link to sign in with their own login.",
      "request": { "method": "POST", "path": "/v1/agent/login-token", "body": {} }
    },
    "steps": [
      { "id": "plan", "status": "blocked", "owner": "human", "why": "Sends are blocked: the workspace is still on the trial plan and no plan has been activated.", "fix": "..." }
    ]
  },
  "blockers": [
    {
      "check": "plan",
      "detail": "Sends are blocked: the workspace is still on the trial plan and no plan has been activated. (your human's move)",
      "fix": "The owner is a verified human, so they can activate the Free plan themselves in the portal (Settings, Plan) or buy Solo or Team. ... Request: POST /v1/agent/login-token"
    }
  ],
  "checks": [
    { "name": "credentials", "status": "ok", "detail": "done: This key works for workspace tenant-slug-a1b2c3d4 with access to every mailbox." },
    { "name": "plan", "status": "blocker", "detail": "Sends are blocked: ...", "fix": "..." },
    { "name": "sending_domain", "status": "ok", "detail": "optional: you@agent.molted.email is on the shared domain agent.molted.email, which works for a first send. ..." },
    { "name": "first_send", "status": "ok", "detail": "todo: Nothing has been sent yet.", "fix": "..." },
    { "name": "held_approvals", "status": "ok", "detail": "Nothing waiting for human review." }
  ]
}
```

A blocked or open setup step is a blocker. `first_send` is the goal, not a prerequisite: nothing sent yet never makes doctor fail.

By default it prints a human-readable report to stdout instead; add `--json` for the shape above. `molted doctor --mailbox <id>` checks one mailbox the key may send from (and narrows held approvals to it) -- `<id>` must be a UUID (e.g. `3f1e0c2a-8b7d-4a3e-9c1f-2b6a7d8e9f01`); a non-UUID value is rejected before any request is made, and a mailbox outside the key's scope is a blocker.

Exit code is `1` if any blocker was found (not ready to send); otherwise `2` if any check reports `"status": "error"` (the call itself failed) or `"status": "unknown"` (the call worked but couldn't be fully classified); otherwise `0`. `0` means ready to send and every check fully verified, not just "nothing blocked" -- so a script or agent loop can gate on it:

```bash
molted doctor --json > /dev/null || echo "blocked, or not fully verified -- see molted doctor for why"
```

An after-send check that needs a scope the current key doesn't have (`operator`, for held approvals and alerts), or that hits a mailbox outside a scoped key's access, reports `"status": "skipped"`, not `"error"` -- it isn't broken, the key just can't see that particular signal, and a skipped check doesn't affect the exit code on its own (the human-readable report says "N check(s) skipped" instead of claiming everything was fully verified). A `401` collapses into a single `"auth"` blocker regardless of how many of the underlying checks hit it. A malformed `--mailbox` (not a UUID) exits `2`, not `1` -- `1` is reserved for an actual diagnosed blocker, and in that case no check even ran.

`molted doctor` calls no write endpoint. The setup check is read-only; checking alerts may notify configured alert destinations (deduplicated). Every fix it prints that would loosen a protection (unpausing a mailbox, resuming a paused account, choosing a plan) is phrased as something a human does, never something the command runs for you.

## Next steps

- Full CLI reference: [/cli](https://molted.email/docs/cli.md) -- every command group, subcommand and flag (for example [`molted adopt`](https://molted.email/docs/cli/adopt.md) for joining an existing workspace)
- [Sending Email](https://molted.email/docs/sending-email.md) -- templates, payloads, and deduplication
- [Domains](https://molted.email/docs/domains.md) -- DNS setup and verification
- [Policy Simulation](https://molted.email/docs/policy-simulation.md) -- simulate sends before going live (`molted send simulate`)
- [Adoption & Device Flow](https://molted.email/docs/adoption.md) -- add more agents to the workspace (`molted adopt`)
- [Agent command reference](https://molted.email/cli-skill.md) -- the single-file reference written for agents (`molted --skill` prints it too)
