# Quickstart

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

Create a Molted Email account, choose a plan, and send your first policy-checked email in under five minutes.

Prefer the terminal? The [CLI Quickstart](https://molted.email/docs/cli-quickstart.md) does the same with `molted` commands. Joining a workspace someone already runs? Use [adoption](https://molted.email/docs/adoption.md) instead of signing up.

## 1. Sign up

Create an account at [molted.email/signup](https://molted.email/signup). Signing up creates your workspace with a default mailbox on the shared `agent.molted.email` domain and a `_default` template. We'll email you a link to verify your address; open it to confirm your email. You can keep setting up while you wait, and if the link doesn't arrive or expires (links work for 24 hours), send a new one from **Settings**.

## 2. Choose a plan

A new workspace starts on the trial plan with sends blocked (`trial_not_activated`). Go to **Settings > Plan** and pick one:

- **Free**: no card. 3,000 sends a month, 100 a day (20 a day in the first week, 50 in the second), 3 mailboxes, no custom domains. Free is for a verified human owner, so verify your email first.
- **Solo** ($19/mo: 10,000 sends a month, 10 mailboxes, 3 custom domains) or **Team** ($79/mo: 50,000 sends a month, 50 mailboxes, 10 custom domains), through Stripe checkout. A first paid plan starts with a 3-day trial on restricted limits (100 sends, 3 mailboxes, 1 custom domain); tick **Skip the 3-day trial** to start paying right away.

See [Billing & Plans](https://molted.email/docs/billing.md) for every limit.

## 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. Add your own domain for production mail.

Go to **Settings > Domains** (or press **⌘K** and type `add domain`) and add your sending domain. Custom domains need a paid plan. You'll receive DNS records to add:

- **DKIM**: signs your outgoing email so recipients can verify it came from you.
- **SPF**: authorizes Molted Email to send on behalf of your domain.
- **DMARC**: tells recipient servers how to handle emails that fail DKIM or SPF.

Add these records to your DNS provider and click **Check again** on the domain's DNS page. Verification usually completes within a few minutes.

> **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. Create an API key

Go to **Settings > Access** and click **Create key**. Choose all mailboxes or specific ones with their own permissions (see [Authentication](https://molted.email/docs/authentication.md#mailbox-scoped-keys)). Copy the key immediately: it is shown only once, along with a ready-to-paste MCP config snippet if you're connecting an MCP client.

The raw API calls below go to `https://mailbox.molted.email`, the default host for agents, which reads your tenant from the key. If you call `https://api.molted.email` instead, every call needs your `tenantId`; ask for it:

```bash title="curl"
curl -X POST https://mailbox.molted.email/v1/agent/whoami \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" -d '{}'
# → { "tenant_id": "tenant-..." }
```

## 5. Check your setup

Before the first send, ask the API whether you can send yet:

```bash title="curl"
curl "https://mailbox.molted.email/v1/agent/setup" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json title="Response (trial workspace, trimmed)"
{
  "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: ...",
    "request": { "method": "POST", "path": "/v1/agent/login-token", "body": { "tenantId": "your-tenant-id" } }
  },
  "steps": [
    { "id": "credentials", "status": "done", "owner": "agent", "why": "..." },
    { "id": "plan", "status": "blocked", "owner": "human", "why": "Sends are blocked: the workspace is still on the trial plan and no plan has been activated." },
    { "id": "holds", "status": "done", "owner": "agent", "why": "..." },
    { "id": "mailbox", "status": "done", "owner": "agent", "why": "..." },
    { "id": "sending_domain", "status": "optional", "owner": "agent", "why": "..." },
    { "id": "policy", "status": "done", "owner": "agent", "why": "..." },
    { "id": "first_send", "status": "todo", "owner": "agent", "why": "Nothing has been sent yet." }
  ]
}
```

`readyToSend` is `true` once every step before `first_send` is `done` or `optional`. `next` is the single next step and whose move it is: the agent's, or the human's (with the call that hands it over, such as `POST /v1/agent/login-token`, which emails a take-over link or returns a claim or portal link, or a Stripe checkout link from `POST /v1/billing/setup-link`). `next` is `null` when nothing is left to do. Each step is `done`, `todo`, `blocked`, `optional` or `unverified` (a check couldn't run just now; call again), with `why` and usually a `fix`, a ready-to-run `request` and `details`. Every `request` already carries your `tenantId`, so it works as given. Add `&mailboxId=<uuid>` to check one mailbox the key may send from. Call it whenever you need to check, not on every request cycle: it allows 30 calls a minute per key, and an over-limit call gets a `429 { "error": "rate_limited", "retryAfterSeconds": ... }`.

| Step | Checks |
| - | - |
| `credentials` | The key, its scopes and the mailboxes it may use |
| `plan` | Trial, Free or paid, whether sends are blocked, quota left |
| `holds` | Account pause, exhausted send quota, held inbound mail |
| `mailbox` | An active mailbox this key may send from |
| `sending_domain` | The shared domain is fine for a first send; a custom domain must be verified |
| `policy` | What the send path does with a first send (sent, held for human approval, or blocked), from the mailbox's real autonomy level |
| `first_send` | Done once a send has reached the provider |

Once `readyToSend` is `true`, `next` is a first send (`POST /v1/agent/request-send` from your own mailbox to itself) that works as given. The CLI shows the same checklist with `molted doctor`; MCP clients call `setup_status`. A mailbox-scoped key only sees its own mailboxes.

## 6. Send your first email

```bash title="curl"
curl -X POST https://mailbox.molted.email/v1/agent/request-send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipientEmail": "recipient@example.com",
    "templateId": "_default",
    "dedupeKey": "welcome-recipient@example.com",
    "payload": {
      "subject": "Hello from Molted Email",
      "html": "<p>Your first email, sent via the API.</p>"
    }
  }'
```

The `_default` template sends the `subject`, `html` and `text` from `payload` as-is. Without `mailboxId` the send goes out from the key's own mailbox (the workspace default for an all-mailbox key). A successful response returns:

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

`status` can also be `scheduled` (with `scheduledAt`), `pending_approval` (held for a human by the mailbox's [autonomy level](https://molted.email/docs/autonomy-levels.md) or a risk check) or `blocked`, each with a `reason` (see [Errors](https://molted.email/docs/errors.md)). A `dedupeKey` is used once per workspace: sending it again returns the first request as `blocked` with `reason: "duplicate"`. CLI: `molted send --to recipient@example.com --subject "Hello" --body "..."`. MCP: `send_email`.

## 7. Check delivery

Open the mailbox in the portal (press **⌘K** and type its name, or pick it in **Settings**). The send shows in its activity, and its thread page shows the delivery status under the message. It will progress through `queued → accepted → sent → delivered`. Your agent can read the same status with `GET /v1/ops/trace/:requestId` (the request with its `delivery_status`, the policy decision and every delivery event; CLI: `molted trace <requestId>`), or on the thread with `GET /v1/agent/threads/:id`.

## Related reading

- [How to Send Your First Policy-Checked Email](https://molted.email/blog/send-your-first-policy-checked-email) - the same flow using curl with full policy trace walkthrough
- [What Is Agent-Native Email?](https://molted.email/blog/what-is-agent-native-email) - why Molted's architecture is different from traditional email APIs
