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) | |
|---|---|---|
| Prerequisite | none | a Cloudflare account with Registrar access |
| Domain purchase | billed in OXYGEN credits at the live price | billed to your Cloudflare card, 0 OXYGEN credits |
| SPF / DKIM / DMARC / MX | set by the vendor | you run domains dns plan then dns apply |
| Mailbox | billed in OXYGEN credits per inbox monthly | you 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 inboxes → Buy 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 --jsonThe 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 domains → Get quote → Approve 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 domain → Apex domain → Create 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 inboxes → Connect 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 --approvedVerify 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 --clearCheck before launching
oxygen mailboxes health --json # deliverability reputation per mailbox
oxygen mailboxes oauth-health --json
oxygen billing balance --jsonIf 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.