OXYGENOxygen/ Docs
Guides

Set up email sending

Buy or bring sending domains, provision mailboxes, fix DNS, and warm up — so a Sequence has somewhere to send from.

Cold email needs separate sending domains, mailboxes on them, correct DNS, and a warm-up before the first campaign. Pick a road first — switching later means buying twice.

Managed (OXYGEN buys)BYOK (your Cloudflare)
Prerequisitenonea Cloudflare account with Registrar access
Domain purchasebilled in OXYGEN credits at the live pricebilled to your Cloudflare card, 0 OXYGEN credits
SPF / DKIM / DMARC / MXset by the vendoryou run domains dns plan then dns apply
Mailboxbilled in OXYGEN credits per inbox monthlyyou supply the Google/Microsoft accounts

Managed inboxes bill monthly per inbox, with warm-up and inbox placement as separate lines. Warm-up is not a managed-road-only cost: a BYOK mailbox enrolled with oxygen mailboxes warmup enable carries the same monthly per-inbox line, so both roads pay it while warm-up runs. Every price is in the pricing reference. A per-connected-mailbox monthly line exists in the billing code but is not charged today; it is listed there under Built, not currently charged.

Where this lives

Sidebar Sequencer → Accounts (/sequencer/accounts), tabs Senders / LinkedIn / Email / WhatsApp / Phone numbers. The Email tab nests inboxes under their domains; top-right is Add inboxes. Domains have their own page at /sequencer/domains — by URL only, deliberately not in the sidebar.

Road A — managed inboxes

In the app: Email tab → Add inboxesBuy managed. The wizard steps Domains → Inboxes → Review; the last button is Buy · <n> cr. The first order asks for a Registrant contact (the WHOIS record); later orders reuse it. Closing mid-order saves the cart — the button becomes Resume order.

From the CLI, preview first — without --approved nothing is ordered or charged:

oxygen managed-inboxes subscribe send-acme.com --provider google \
  --file ./mailboxes.json --billing ./registrant.json --json

The order includes warm-up and inbox placement by default, each a separate monthly per-inbox line (--no-warmup / --no-placement opt out). Check quote_id, the add-on lines, and warmup_activation, then order:

oxygen managed-inboxes subscribe send-acme.com --provider google \
  --file ./mailboxes.json --billing ./registrant.json \
  --approved --quote <quote_id>

--approved spends real credits and registers a real domain. Without a fresh --quote it is refused with quote_required, never charged; a stale quote is refused with quote_mismatch.

subscribe always registers a new domain and fails on one you already own — grow that one with add-inboxes instead. A domain holds at most 5 Google or Microsoft inboxes (100 on azure), counting existing ones:

oxygen managed-inboxes add-inboxes send-acme.com --count 3 --prefix ada --json
oxygen managed-inboxes add-inboxes send-acme.com --count 3 --prefix ada --approved --quote <quote_id>

Road B — your own Cloudflare

Connect the account once at Connections (/connections), or Connect Cloudflare on /sequencer/domains. The dialog needs a user-owned Cloudflare API token (account-owned tokens cannot use the Registrar API) scoped Zone → Zone: Read, Zone → DNS: Read (DNS Edit to let OXYGEN fix records), and Account → Registrar: Domains: Edit; finish with Verify & connect. Until then the Domains page shows the connect card, not the domain table.

Buy new domains in the app with Buy domainsGet quoteApprove purchase, or from the CLI:

oxygen domains check acme-mail.com try-acme.com   # free
oxygen domains buy acme-mail.com --json           # priced preview + quote_id
oxygen domains buy acme-mail.com --approved --quote <quote_id>

That purchase is non-refundable and bills your Cloudflare account. Registration can stay pending — oxygen domains registration-status acme-mail.com --wait polls to a terminal state.

Already own the domain? Add domainApex domainCreate zone, then set the printed nameservers at your registrar (CLI: oxygen domains add acme.com). Nothing works until the nameservers move.

Then write the mail records: plan is free, apply writes into your zone for 0 credits:

oxygen domains dns plan send-acme.com --provider google --json
oxygen domains dns apply send-acme.com --provider google --approved --plan <plan_hash>

A stale plan_hash is rejected — re-run plan. Both only work on Cloudflare zones; a managed domain's records are vendor-held, so read them with oxygen domains dns send-acme.com --json instead (--deep is ignored there).

Connect the mailboxes

Provisioning does not authorize sending — Google/Microsoft authorization is a separate step on both roads. In the app: Add inboxesConnect existing (paste up to 500 exact addresses) or Import a file for a CSV/JSON/JSONL/XLSX export. Google and ordinary Microsoft accounts sign in individually, in reviews of at most 10. A dedicated Microsoft sending tenant can instead use one Entra administrator authorization per resolved tenant, followed by read-only verification of only the exact addresses you submitted.

oxygen mailboxes import --file ./mailboxes.csv --validate-only --json   # local, no network
oxygen mailboxes import --file ./mailboxes.csv
oxygen mailboxes connect-oauth --provider google --vendor oxygen \
  --mailboxes ada@send-acme.com --json                                  # preview
oxygen mailboxes connect-oauth --provider google --vendor oxygen \
  --mailboxes ada@send-acme.com --approved

Verify with oxygen mailboxes oauth-health --json: it lists every Google/Microsoft inbox with no usable grant — a token on one mailbox proves nothing about another. The detailed existing-mailbox guide covers the Entra tenant and individual paths, secure file fields, and managed-mailbox fallback. Which origins support warm-up and monitoring at all: mailbox compatibility.

Warm up before you send

A managed order with the warm-up add-on activates itself after provisioning — do not enable it a second time. A non-managed Microsoft mailbox first needs one separate OXYGEN Warm-up Outlook authorization for that exact mailbox, even when an Entra administrator authorized the selected tenant for native sending; there is no tenant-wide warm-up shortcut. A compatible Google import instead uses its mailbox-scoped app password. Then, for BYOK, imported, or opted-out inboxes:

oxygen mailboxes warmup enable --mailboxes ada@send-acme.com --json
oxygen mailboxes warmup enable --mailboxes ada@send-acme.com \
  --approved --plan <plan_hash> --max-credits <n>

Both --plan and --max-credits must echo the fresh preview. In the app: the flame icon on a mailbox row (Enable OXYGEN Warm-up); the dialog quotes 4 weeks and confirms with Approve <n> credits/month. Track it with oxygen mailboxes warmup status --json.

Mailbox age clamps sends whatever the warm-up state: 5/day through day 2, 10/day through day 5, 20/day through day 8, 40/day through day 13, then only your configured caps apply. Two weeks is the realistic wait before an inbox carries volume.

Click a mailbox address to open its panel — OXYGEN Warm-up / Settings / Campaigns — and set Daily campaign limit on Settings. The ramp override is CLI-only:

oxygen mailboxes cap set ada@send-acme.com --daily-cap 40
oxygen mailboxes ramp set ada@send-acme.com --start-per-day 5 --increase-every-days 3 --step 5 --cap 40
oxygen mailboxes ramp set ada@send-acme.com --clear

Check before launching

oxygen mailboxes health --json      # deliverability reputation per mailbox
oxygen mailboxes oauth-health --json
oxygen billing balance --json

If one inbox degrades, pull it from rotation without losing its warm-up: tick its checkbox and hit Pause in the selection bar, or oxygen mailboxes status ada@send-acme.com --status paused. Sequences pick from this pool at their Senders stage.

On this page