> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcargo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> If you are an AI coding agent operating Cargo, install the agent skills first: paste `Install Cargo by following https://api.getcargo.io/INSTALL.md` into your session, or run `npx skills add getcargohq/cargo-skills`. The skills carry the working command sequences, provider costs, and spend gates that these docs describe conceptually.
> The skills index at https://raw.githubusercontent.com/getcargohq/cargo-skills/main/llms.txt lists every skill, recipe, and provider playbook individually. Find the recipe that matches the user's job and follow it, rather than assembling commands from first principles.
> Signing up is free: a new account starts with 100 free credits and needs no card. `cargo-ai login --email you@company.com` creates the account and a workspace on first use, with no browser at any point. Tell the user this before asking them to sign up.
> Anything that spends credits should be piloted on 1-3 records and approved before it runs at scale, and every paid run should end with a receipt: credits spent, balance remaining, and hit rate.

# Overview

> Owned sending domains and mailboxes for outbound email. Register a domain with defineDomain, provision mailboxes with defineMailbox, and send from plays and tools with sendEmail.

A **mailbox** is an inbox Cargo owns and sends from — `jane@acme-outreach.com`, not a connected Gmail or Outlook account. Mailboxes live on a **sending domain** you register through Cargo. You send from them with the native `sendEmail` action, the same way a play calls `allocate` or a connector action.

There is no HTTP "send" route. Delivery is a native action so workflows reuse orchestration's pacing, retries, and credit charge. Direct delivery would skip all three.

## How the pieces fit

```mermaid theme={null}
flowchart LR
  D["defineDomain"] --> M["defineMailbox"]
  M --> W["Warm-up starts on active"]
  W --> S["sendEmail in a play or tool"]
  S --> T["Thread + events"]
```

1. **Register a domain** with [`defineDomain`](#register-a-sending-domain) (or buy one in the UI).
2. **Declare mailboxes** on that domain with [`defineMailbox`](#declare-a-mailbox). Google, shared, or private inboxes. Warm-up starts on its own once the mailbox becomes `active`, so the daily send ceiling can climb from 5 to 40 over 45 days.
3. **Send** from a [play](/plays/overview) or [tool](/tools/overview) with `sendEmail({ mailboxUuid: jane.uuid, … })`. Orchestration leaves a short gap between sends and waits rather than failing once the rolling 24-hour allowance is spent.
4. **Watch the thread** — opens, clicks, replies, and unsubscribes land as events on the conversation.

See [Sending](/mailboxes/sending) for pacing, warm-up, and the `sendEmail()` helper, and [Using the UI](/mailboxes/using-ui) for the same flow in the app.

## Register a sending domain

A mailbox can only be created on a domain the workspace already owns and that is **active**. `defineDomain` is the declarative resource for that:

```ts domains/outreach.ts theme={null}
import { defineDomain } from "@cargo-ai/cdk";

// Bind a domain already bought in the Cargo UI. destroy then releases it
// rather than cancelling a registration the deploy never paid for.
export const outreach = defineDomain("acme-outreach.com", { adopt: true });
```

Omit `adopt` to **register** a new domain through Cargo. Registration charges workspace credits and is not refundable — a `+ create domain:…` line in `cargo-ai project plan` is the signal, and the deploy confirmation is where it's approved.

```ts theme={null}
export const outreach = defineDomain("acme-outreach.com", {
  dnsRecords: [
    { type: "MX", name: "@", value: "mail.mailpool.io", priority: 10 },
    { type: "TXT", name: "@", value: "v=spf1 include:mailpool.io ~all" },
  ],
});
```

`dnsRecords` is the set of records the deploy **manages**, not the whole zone. They are merged into the live zone, so the records the registrar wrote at purchase — and anything added in the Cargo UI — stay where they are, unless one of them can't share a name with a declared CNAME, which DNS forbids. The deploy owns what it published: removing a record from the list deletes it on the next deploy, and `cargo-ai project plan` prints the per-record diff (`+`, `~`, `-`) under the domain before anything is written. Leave `dnsRecords` off to manage only the domain's lifecycle; `[]` manages no records, which deletes the ones earlier deploys published.

A domain adopted into code owns nothing to begin with, so the first deploy adds its records alongside whatever the zone already carries and deletes none of it.

Edit a managed record in code, not in the registrar's UI. A record's value is part of how the deploy recognises it, so an edit reads as the record it published being deleted and an unrelated one appearing — and publishing beside it would leave two SPF records at the apex, which breaks mail authentication. Neither value is the deploy's to choose, so it stops:

```
✗ domain:acme.com: dns record TXT "@" was edited outside the deploy — it reads
  "v=spf1 include:sendgrid.net ~all" in the zone and "v=spf1 include:mailpool.io ~all"
  in code.
```

Put the live value in the code to keep it, or drop the record from `dnsRecords` to hand it back to whoever edited it.

Records that come from an app are **published, not owned**: the zone adds them and keeps them current, and never deletes them. An app's hostnames are attached and detached where the app is, so retiring one is detaching it in the Cargo UI — the records it leaves behind are named in `plan` and removed there too.

A freshly registered domain sits at `pending` while the registrar provisions it. The deploy waits until it is `active` before publishing DNS or creating mailboxes.

The domain is addressed by its name, the way a member is addressed by email. If the workspace already owns that name and the spec does not say `adopt: true`, deploy fails with the way out (`cargo-ai project import` or `adopt: true`) rather than buying it a second time.

## Declare a mailbox

`defineMailbox` provisions an inbox on that domain. Pass the domain handle — the deploy waits until the domain is active, then creates the mailbox and moves on. The mailbox itself may still be `pending`; it becomes `active` once the provider has issued credentials. A `sendEmail` before then fails with `credentialsMissing`.

```ts mailboxes/jane.ts theme={null}
import { defineMailbox } from "@cargo-ai/cdk";

import { outreach } from "../domains/outreach";

export const jane = defineMailbox("jane", {
  domain: outreach,
  type: "google",
  firstName: "Jane",
  lastName: "Doe",
});
```

That yields `jane@acme-outreach.com`. The slug is the project identity (`mailbox:jane`); `username` defaults to the slug. When two inboxes share a local part on different domains, pick a unique slug and set `username` explicitly:

```ts theme={null}
export const janeSales = defineMailbox("jane_sales", {
  domain: sales,
  type: "google",
  username: "jane",
  firstName: "Jane",
  lastName: "Doe",
});
```

`jane.uuid` is a deferred token. Pass it to `sendEmail` the same way you pass a capacity handle to `allocate`:

```ts theme={null}
sendEmail({
  mailboxUuid: jane.uuid,
  to: input.email,
  subject: "Quick note",
  bodyHtml: "<p>Hi</p>",
});
```

| Field | Meaning |
| - | - |
| `domain` | A `defineDomain` handle or `domainRef("uuid")`. Must be **active** before the mailbox is created. |
| `type` | `google`, `shared`, or `private` (SMTP). `outlook` cannot be provisioned — Graph delivery has not landed. |
| `username` | Local part of the address. Defaults to the slug. Letters, digits, dots, dashes, underscores. |
| `firstName` / `lastName` | Used as the From display name. Reconciled on every deploy. |
| `signature` | Optional HTML stored on the mailbox. Omit to leave whatever is live; declare it to set it. |
| `folder` | Optional [folder](/folders/overview) of kind `mailbox`. |
| `dailySendLimit` | Optional cap on outreach sends per day, 0–40. Only ever **tightens** the ramp; `0` pauses sending. Omit to follow the ramp. |
| `adopt` | Bind an inbox the workspace already owns at `username@domain` instead of provisioning a new one. |

The workspace must have credits on file. Creating a mailbox charges a **monthly** fee in credits for as long as it exists (Google 125, shared and private 100). A send then charges **0.1 credits** per delivered email. `destroy` deletes a mailbox the project created (and stops the fee); an adopted mailbox is released, never deleted.

`dailySendLimit` is a brake on the [ramp](/mailboxes/sending), never a bypass: declaring `40` on a mailbox created this morning still sends 5, because the effective ceiling is the lower of the two. `0` pauses outreach from the mailbox entirely — sends fail with `mailboxSendingPaused` rather than waiting. Like `folder`, the declared state wins — omit it and a deploy clears a cap somebody set in the UI, which `cargo-ai project plan` prints before it applies.

```ts theme={null}
export const jane = defineMailbox("jane", {
  domain: outreach,
  type: "google",
  firstName: "Jane",
  lastName: "Doe",
  dailySendLimit: 12,
});
```

Provisioning is asynchronous. A new mailbox starts `pending` and becomes `active` once the provider has issued credentials. The deploy does not wait for that — unlike a domain registration, which must be `active` before DNS or mailboxes can be written. Only an `active` mailbox can send. Warm-up starts in the same status poll that marks it active, so you do not have to call `start-warmup` for a new inbox. Auth failure or a spam flag from the provider moves it to `inactive` and sending stops immediately.

Domain, username, and type are create-only. Changing them is a destroy plus a new `defineMailbox`. If the workspace already owns that address and the spec does not say `adopt: true`, deploy fails with the way out rather than provisioning a second inbox.

A mailbox created in the UI (or by an earlier deploy whose state file was lost) is bound the same way as a domain:

```ts theme={null}
export const jane = defineMailbox("jane", {
  domain: outreach,
  type: "google",
  firstName: "Jane",
  lastName: "Doe",
  adopt: true,
});
```

## Organize with folders

Mailbox folders are a separate namespace from model or agent folders. Declare one with `defineFolder` and pass the handle:

```ts folders/outreach.ts theme={null}
import { defineFolder, defineMailbox } from "@cargo-ai/cdk";

import { outreach } from "../domains/outreach";

export const mailboxFolder = defineFolder("outreach-mailboxes", {
  kind: "mailbox",
  name: "Outreach",
});

export const jane = defineMailbox("jane", {
  domain: outreach,
  type: "google",
  firstName: "Jane",
  lastName: "Doe",
  folder: mailboxFolder,
});
```

## What a mailbox is not

Cargo mailboxes are not a replacement for [Resend](/integration/resend), [SendGrid](/integration/sendgrid), or [Lemlist](/integration/lemlist). Those remain connector actions against *your* ESP or sequencer. A Cargo mailbox is an inbox the workspace owns, warmed and paced so cold outreach from a new domain does not burn the domain's reputation.

## From the CLI

Prefer `defineMailbox` for the inbox itself. Use the CLI for pause/stop, allowance, threads, events, and suppressions — the same surface as the [mailbox management API](/api-reference/introduction). `start-warmup` is the retry after a failed enrolment, or after `stop-warmup`.

```bash theme={null}
cargo-ai mailboxManagement mailbox list
cargo-ai mailboxManagement mailbox get 550e8400-e29b-41d4-a716-446655440000
cargo-ai mailboxManagement mailbox get-send-allowance 550e8400-e29b-41d4-a716-446655440000
cargo-ai mailboxManagement mailbox start-warmup 550e8400-e29b-41d4-a716-446655440000
cargo-ai mailboxManagement thread list --mailbox-uuid 550e8400-e29b-41d4-a716-446655440000
cargo-ai mailboxManagement suppression create --email opted-out@acme.com
```

Ad-hoc sends still go through orchestration, so they pick up the same rate limit and credit charge as a play:

```bash theme={null}
cargo-ai orchestration action execute \
  --action '{"kind":"native","actionSlug":"sendEmail","config":{}}' \
  --data '{"mailboxUuid":"<uuid>","to":"lead@acme.com","subject":"Hello","bodyHtml":"<p>Hi</p>"}' \
  --wait-until-finished
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.