cargo-mailbox-management
Send mail from inboxes Cargo owns — provision mailboxes on a sending domain, run provider warm-up and the 5→40/day send ramp, deliver with the `sendEmail` action, and read back threads, replies, delivery events, and the workspace suppression list. Triggers: "set up a sending mailbox", "provision inb
By getcargohq · 1,938 installs
npx skills add getcargohq/cargo-skills --skill cargo-mailbox-management
Source repository · Upstream listing
Cargo CLI — Mailbox Management
Mailboxes. A mailbox is a real sending inbox the workspace owns — provisioned through
Cargo on a sending domain the workspace also owns, with SMTP and IMAP credentials Cargo holds
and uses to deliver outbound mail and to read the replies that come back. Two things follow,
and both are easy to get wrong: a mailbox is a recurring monthly credit charge rather than
a per record one, and this domain does not send. It provisions the inbox, ramps it, and
reports on what it did; the send itself is the sendEmail action, under
[ cargo orchestration ](../cargo orchestration/SKILL.md).
Version note. Everything on this page is live in the pinned CLI (1.0.66), including
mailbox get warmup stats and the daily target default of 40 — both of which were
merged but unpublished when this skill was written. On an older CLI, daily target
reads "provider default if omitted" and get warmup stats silently prints the mailbox
group help instead of erroring , so a missing subcommand here looks like a usage mistake
rather than a stale install. cargo ai mailboxManagement mailbox help lists what your CLI
actually has; re run the session refresh in [ ../cargo/SKILL.md ](../cargo/SKILL.md) to catch up.
Bootstrap
Already signed in ( cargo ai whoami returns a workspace)? Skip to the next section.
Every command prints JSON to stdout; failures exit non zero with {"errorMessage": "..."} .
Nothing in this domain is asynchronous — every command is a single HTTP call, so there is no
run to poll (the one thing that looks async, a freshly created mailbox sitting at pending ,
is polled with mailbox refresh status , not with run get ). Mailboxes are guarded by
mailboxManagement:read / mailboxManagement:write permissions, which an admin and an editor
both hold and a viewer does not; if a create or update returns a permission error, the token is
read only ([ ../cargo workspace management/SKILL.md ](../cargo workspace management/SKILL.md)).
When the full skill bundle is installed, [ ../cargo/references/prerequisites.md ](../cargo/references/prerequisites.md)
adds the CLI version pin, token scopes, and the admin only surface.
Before any send — three checks
This is a blocking gate, not advice. Cargo owning the mailbox changes who presses send; it
changes nothing about whether the message should be sent. The canonical rules are
[ ../cargo gtm/references/acceptable use.md ](../cargo gtm/references/acceptable use.md) §3 and
they apply here unchanged — run all three before the first sendEmail , not after:
Basis — which permission covers this audience (customers, opted in contacts, event
attendees, or a documented legitimate interest case for a B2B role)?
Suppression — subtract the workspace suppression list from the audience before you
enrich or send. suppression list is free and this domain is the source of truth for it.
Relevance — can you name, per recipient, why this message is for them?
Any check that fails is a stop and ask. Two obligations are specific to Cargo owned sending:
The ramp is a ceiling, not a target. get send allowance reports what a mailbox may send
today; it is not a quota to fill. Asking to raise it, to spread one campaign across a fleet of
fresh mailboxes to clear the same volume, or to rotate identities so filters see less from
each, is the evasion refusal in acceptable use.md §2 — not a configuration question.
Unsubscribes are automatic and absolute. Every send carries a signed List Unsubscribe
header; a recipient using it writes a workspace wide suppression row, and the next send to
that address is refused by the engine. There is no removal command, and that is the point —
never route around a suppression to re contact someone.
The two numbers that govern a mailbox
They look similar and mean opposite things. Confusing them is the most common way to
mis read a mailbox's health.
mailbox get send allowance mailbox get warmup stats
Measures Your real outreach The provider's dummy warm up traffic
Window Rolling 24 hours Today, UTC
Returns dailyLimit , sentCount , remainingCount sentCount , deliveredCount , spamCount , inboxRate , spamRate
Answers "How many more may I send?" "Is this inbox landing in the inbox or in spam?"
dailyLimit is not a setting — it is derived from how long warm up has been running. The
arithmetic, the warmupStatus transitions, and how to size a fleet against it are in
[ references/warmup and allowance.md ](references/warmup and allowance.md) . Read it before
you promise anyone a send volume.
Commands
All commands output JSON. Reads need mailboxManagement:read ; everything that provisions,
updates, deletes, or suppresses needs mailboxManagement:write .
Provision a mailbox
First, the gap you will hit. mailbox create requires domain uuid , and there is no
cargo ai command that lists sending domains — the domainManagement API exists but has no
CLI surface yet. Get the UUID from the Cargo web app, or declare the domain in a CDK repo with
defineDomain and read it back from cargo.state.json . Say this to the user rather than
guessing a UUID.
type — google , shared , or private . outlook is accepted by the flag and always
fails with transportNotSupported : the Graph transport has not shipped, so an Outlook
mailbox cannot deliver. Pick one of the other three.
username — the local part only ( jane for jane@acme.com ). Lowercased; letters, digits,
dots, dashes and underscores, 1–64 characters, starting and ending alphanumeric.
first name / last name — the From header the recipient sees. Use a real person's name
under a real identity; a fabricated sender is a §2 refusal.
signature — HTML, stored on the mailbox (max 10,000 characters).
folder uuid — a folder of kind mailbox , from [ cargo workspace management ](../cargo workspace management/SKILL.md).
create returns immediately with status: "pending" — the provider has not issued credentials
yet. It reaches active when refresh status says so, or on its own within five minutes; a
send attempted before then fails with credentialsMissing .
statuses — pending , active , inactive , comma separated with no spaces . An
inactive mailbox was disabled by the provider; errorCode says why ( 401 auth, 402
spam) and it will not send until it is fixed.
none is the sentinel for "clear it" on folder uuid and signature . On list ,
folder uuid none means "mailboxes in no folder".
remove is how monthly billing stops. There is no pause.
Warm it up
A mailbox that never starts warm up is pinned at 5 real sends a day, forever . Warm up is
what moves it, and it takes 45 days to finish.
daily target — warm up messages per day at full ramp, 1–40 (default 40). This is the
provider's dummy traffic, not your send allowance.
status on update warmup accepts the whole enum, but only active and paused do
anything: pending and failed are states the provider reaches on its own, and disabled
is what stop warmup is for.
stop warmup resets the Cargo send ramp as well as tearing down provider warm up — the
mailbox drops back to 5/day and starts the 45 days over. Pause instead unless you mean it.
Check the allowance before you send
Read remainingCount before enrolling a batch. Sends past it do not queue for tomorrow — they
fail immediately with dailyLimitReached , one wasted run per row.
Read what happened
Message vs thread vs event. A message is one outbound send. A thread is a
conversation — its lastEmail and lastEvent are what you sort a reply queue on. An event
is something that happened to a message ( sent , opened , clicked , replied , bounced ,
unsubscribed ). Inbound replies are events, not message rows; only outbound mail is a message.
statuses on message list / thread list is the list status — pending , error ,
or an event kind. success is not in that set: a delivered message reads as sent or later.
kinds , statuses , reasons are all comma separated with no spaces.
bounced never fires yet. Nothing parses delivery status notifications, so bounces do not
produce events and do not auto suppress. Do not build a deliverability report that treats an
empty bounce count as a clean list.
message , thread , and event lists default to limit 50 (max 200) and return a count .
mailbox list and suppression list have no default limit (max 1000), and mailbox list
returns no count — see [ references/response shapes.md ](references/response shapes.md).
Suppression
Workspace wide, not per mailbox: a recipient opting out is opting out of the sender, not of one
address the sender happens to own.
Reasons are unsubscribed (the recipient's own choice, via List Unsubscribe ), bounced ,
complained , and manual . suppression create always records manual .
It is idempotent — suppressing an already suppressed address returns the existing row.
Addresses are normalised ( trim().toLowerCase() ) on both write and check, so casing and
stray whitespace cannot slip a suppressed recipient back into a send.
There is no suppression remove . That is deliberate.
Pricing
Read this live before quoting a fleet cost — the figures above are what the workspace
returned at the time of writing, not a constant.
Sending: the sendEmail action
Delivery is deliberately not in this CLI domain. It is a native orchestration action so that
sends inherit orchestration's pacing, retry, and credit machinery:
0.1 credits per send , fixed. The action carries no config ; the inputs go in data , like every
other action ([ ../cargo orchestration/SKILL.md ](../cargo orchestration/SKILL.md)).
Optional bodyText (generated from the HTML when omitted), inReplyTo , and references .
To keep a reply threaded, send the whole chain. references is every Message ID in the
thread so far, oldest first — not just the parent. Mail clients break the thread otherwise.
The action is rate limited per mailbox to that mailbox's own daily limit, spread across
the day. A burst of 100 on a mailbox with 40 left fails the 41st immediately rather than
parking it for a day.
A refused send is a node error, not a thrown exception. recipientSuppressed ,
mailboxNotActive , and transportNotSupported need a human and do not retry;
dailyLimitReached , credentialsMissing , and deliveryFailed retry on their own.
There is no dry run from the CLI. The engine has one, but no action execute flag reaches
it — the send is live the moment you run the command. Send to yourself first.
Threading, the full refusal table, and what Cargo injects into every message (unsubscribe
header, open pixel, click redirect) are in [ references/sending.md ](references/sending.md) .
Cost discipline
This domain bills differently from the rest of the pack, and the difference is the thing to say
out loud before provisioning anything.
A mailbox is a monthly, recurring charge — 100–160 credits per mailbox, per month , for
as long as it exists. Five mailboxes is 500–625 credits every month, not once. mailbox
remove is the only way to stop it; there is no pause. Quote the fleet size and the monthly
credit estimate from a live pricing get , and get an explicit yes, before the first create .
Sends are 0.1 each , so volume is cheap and the fleet is not. Do the arithmetic in that
order.
A play or scheduled tool that calls sendEmail re bills on every run — and re contacts
the same people on every run, which is the §6 cadence gate in acceptable use.md as much as a
spend gate. Check get send allowance before enrolling a batch: rows past the allowance burn
a run each and deliver