Mailbox warmup and monitoring compatibility
Which mailbox origins OXYGEN can warm up and monitor, how to import each one, and what to do when a pairing is blocked.
OXYGEN Warm-up and EmailGuard deliverability monitoring need more than a sending connection, and whether your inboxes qualify depends on where they came from. Find your row:
| What you have | Warm-up | EmailGuard | Do this |
|---|---|---|---|
| Google inboxes bought through OXYGEN | yes, automatic | yes | nothing — the approved order starts warm-up after provisioning |
| Microsoft 365 or Azure inboxes bought through OXYGEN | yes, automatic | no | nothing; leave monitoring off |
| A Google Workspace export containing app passwords | yes | yes | oxygen mailboxes import --from credentials |
| Google Workspace with identity only, or connected by OAuth | no | no | re-import with app passwords, or warm up at the source vendor |
| Microsoft 365 / Entra you did not buy through OXYGEN | after one consent each | no | oxygen mailboxes warmup microsoft |
EmailGuard is blocked on every Microsoft mailbox: its public account API exposes neither Microsoft OAuth nor tenant consent, and OXYGEN will not downgrade to password auth, which Exchange Online retired. That is a vendor limit, not a setting. OXYGEN's own Microsoft placement-test send still works.
Buying domains, provisioning inboxes, DNS, and the warm-up wait are in Set up email sending. For the exact imported/manual Google, Microsoft individual, and Entra tenant authorization sequence, use Connect and warm up existing mailboxes.
Check your own pool
Sidebar Sequencer → Accounts (/sequencer/accounts), tab Email, lists every domain with its inboxes nested; rows with no usable grant are badged Needs connection. For the exact verdict per mailbox:
# Readiness for the mailboxes you name — or the whole pool if you omit --mailboxes.
oxygen mailboxes compatibility --mailboxes ada@send-acme.com --json
# The workspace-independent contract only: no mailbox rows are read or returned.
oxygen mailboxes compatibility --catalog-only --jsonThe two flags cannot be combined. Both forms are pure reads: 0 credits, no credential decrypted, no call to OXYGEN Warm-up or EmailGuard. The first may make one bounded read-only warmup-status call to the managed vendor, so a conflicting source subscription surfaces before you approve anything.
| JSON field | Meaning |
|---|---|
data.compatibility | Verdicts for the checked mailboxes: origin, platform, native send, warmup, emailguard |
data.provider_matrix | The public contract below, returned even when --mailboxes narrows the rows; its warm-up key is still spelled trulyinbox after the previous vendor |
data.import_methods | Each ingestion method with its surface, inputs, credential policy, and command |
data.import_fields | Canonical fields, aliases, requiredness, and whether each crosses the network |
data.non_transferable_auth | Credential kinds OXYGEN will never accept as an import |
data.summary | Pool rollup by native-send, warmup, and EmailGuard state |
data.web_url | The Accounts → Email page showing the same state |
The matrix
Three axes decide a verdict, and collapsing them is the usual mistake. Origin is how the mailbox entered OXYGEN: inboxkit and cmr are OXYGEN-managed provisioning (current and legacy), zapmail a connected inventory pull, external a file import carrying vendor provenance, manual an address you registered yourself, native one created in OXYGEN. Infrastructure platform is where it is really hosted — Azure is Microsoft transport, not a separate password protocol. Downstream auth is what a warmup or monitoring vendor can actually consume. A pairing absent from this table is not silently inferred or supported.
| Origin | Infrastructure platform | Downstream auth | OXYGEN Warm-up | EmailGuard monitoring |
|---|---|---|---|---|
| InboxKit | Google Workspace | Provider-native export | ready | ready |
| InboxKit | Microsoft 365 | Provider-native export | ready | vendor_blocked |
| InboxKit | Microsoft Azure | Provider-native export | ready | vendor_blocked |
| Legacy CMR | Google Workspace | Google app password | ready | ready |
| Legacy CMR | Microsoft 365 | Microsoft tenant consent | consent_required | vendor_blocked |
| Zapmail | Google Workspace | Google app password | ready | ready |
| Zapmail | Microsoft 365 | Microsoft tenant consent | consent_required | vendor_blocked |
| External | Google Workspace | Google app password | verification_required | verification_required |
| External | Microsoft 365 | Microsoft tenant consent | consent_required | vendor_blocked |
| External | Microsoft Azure | Microsoft tenant consent | consent_required | vendor_blocked |
| Manual | Google Workspace | none | credential_required | credential_required |
| Manual | Microsoft 365 | Microsoft tenant consent | consent_required | vendor_blocked |
| Manual | Microsoft Azure | Microsoft tenant consent | consent_required | vendor_blocked |
| Native | Google Workspace | none | credential_required | credential_required |
| Native | Microsoft 365 | Microsoft tenant consent | consent_required | vendor_blocked |
External Google reads verification_required because only a mailbox-scoped vault check separates an identity-only import from a credential-bearing one. Run the exact command above to resolve it.
| State | Meaning | Next action |
|---|---|---|
ready | A safe handoff exists; a fresh managed-order quote may already authorize it | Approve the managed order, or preview the standalone warmup / EmailGuard action |
verification_required | The origin can support the handoff, but no mailbox-scoped credential was checked | oxygen mailboxes compatibility --mailboxes <address>, then read the verdict |
credential_required | No credential-capable origin; OXYGEN never exports its OAuth token as a password | Re-import through a credential file, or warm up at the source provider |
consent_required | A non-managed Microsoft mailbox needs one Outlook OAuth authorization first | oxygen mailboxes warmup microsoft — preview, approve, open the consent_url |
vendor_blocked | EmailGuard has no Microsoft OAuth or consent handoff, and password auth is retired | Leave monitoring off; use the native Microsoft placement-test send |
Warm-up is not send authorization
A managed order whose warmup_activation.automatic_after_provisioning is true authorizes warm-up only — scope: "warmup_only", mailbox_credentials_required: false, native_send_oauth_separate: true — and needs no mailbox password or Google/Microsoft OAuth. Sending is a separate destination-bound grant: verify it per address with oxygen mailboxes oauth-health --json before your first campaign, because a token on one mailbox proves nothing about another.
That object is the only authority for automatic warm-up: a selected add-on or a lifecycle status does not imply it, and null means the order grants none. Preview an order or expansion without --approved — nothing is charged — and read mailboxes, addons, addons_monthly_credits, warmup_activation, and quote_id.
Getting mailboxes in
| Source shape | CLI (oxygen …) | Browser | Credentials |
|---|---|---|---|
| Identity file (CSV/JSON/JSONL/XLSX) | mailboxes import --file … --vendor <slug> | Add inboxes → Import a file | Rejected |
| Google app-password export | mailboxes import --from credentials --vendor <slug> --file … | Import a file → Credential export | Encrypted on arrival; seven-day transfer TTL |
| Addresses you already own | mailboxes import then mailboxes connect-oauth | Add inboxes → Connect existing | Fresh destination OAuth; source grants never transfer |
| Connected Zapmail inventory | mailboxes import --from zapmail | Connected integration flow | Provider inventory is authoritative; no file password |
| Managed provisioning | managed-inboxes subscribe / add-inboxes | Add inboxes → Buy managed | The provider adapter owns the handoff |
Vendor brands do not create new authentication protocols, so pick by the export you hold, not by the logo on it:
| Export family | Preferred import | Boundary |
|---|---|---|
| Zapmail | --from zapmail | The API pull is authoritative; convert a legacy .xls workbook to CSV — .xls is not accepted |
| Instantly, Smartlead, PlusVibe | file import | Use credential mode only for rows holding real Google app passwords |
| Salesforge / Mailforge / Infraforge / Primeforge | file import | Strip generic SMTP passwords and import identity only; custom SMTP handoff is not implemented |
| Lemlist or another sequencer | identity file import | Import identities and reconnect OAuth at the destination |
The generic Tables upload is not a mailbox import — it creates rows and registers no sender.
In the web app
All three browser paths sit behind Add inboxes, top right of the Email tab.
Connect existing takes a Mailbox provider (Google Workspace, or Microsoft 365 / Entra) and up to 500 exact addresses in Inbox addresses, then Add and review connection. OXYGEN registers only what you paste; it never discovers a domain or enumerates directory users. Google authorization is reviewed in batches of 10. Microsoft can instead take one tenant-admin authorization covering up to 500 selected addresses — the batch of 10 applies only to the one-link-per-mailbox path.
Import a file takes an Import source (Credential export or Identity-only export), an optional Source label kept as non-secret provenance, and the file. The signed-in browser parses and policy-checks it whole in memory, then shows Local validation passed with Rows, Google, Microsoft, Credentials, and Connection steps. Until you click Import <n> inboxes there is no network request and no workspace write.
Secret-bearing files are accepted only by this authenticated same-origin flow or the OXYGEN CLI; MCP, OAuth, Copilot, and Agent identities stay identity-only, so a credential never lands in an LLM transcript. On import the browser submits canonical fields only — raw vendor columns and SMTP/IMAP hosts never leave it, and a Google app password is encrypted server-side and never returned.
From the CLI
# Identity file. --validate-only needs no auth, makes no network request, writes nothing.
oxygen mailboxes import --file ./mailboxes.csv --vendor instantly --validate-only --json
oxygen mailboxes import --file ./mailboxes.csv --vendor instantly --json
# Google app-password export. Same preflight, then re-run without --validate-only.
oxygen mailboxes import --from credentials --vendor mailforge --file ./mailboxes.csv --validate-only --jsonFiles may hold 500 rows and 5 MB. Canonical fields: email_address, provider, and optional workspace_external_id, infrastructure_platform, and Microsoft tenant_id. Aliases such as Email, From Email, ESP, Mailbox ID, Mailbox UID, and Entra Tenant ID are mapped locally. SMTP/IMAP hosts count only when they exactly match the standard Google or Microsoft endpoints — a custom host fails closed. In identity mode, OAuth and refresh tokens, MFA/TOTP seeds, client secrets, delegation keys, and every password field are rejected.
Credential-mode JSON:
{
"mailboxes": [
{
"email_address": "ada@send-acme.com",
"provider": "google",
"app_password": "<Google mailbox app password>"
}
]
}Only Google rows carry an app password, created from that mailbox's own Google Account after 2-Step Verification is on — never a super-admin credential. Microsoft rows should omit password fields; the parser discards them locally, and imported non-managed Microsoft inboxes warm up through per-mailbox Outlook OAuth instead.
Import spends 0 credits but does write mailbox registrations. Any parse, shape, provider, platform, tenant, secret-policy, duplicate, or credential-conflict error rejects the whole file before the first write and names the failing row as mailboxes[index]. If infrastructure fails mid-upsert, re-run the same file — import is idempotent by address.
Google Workspace
Managed Google mailboxes hand off through provider-native export and never materialize an app password for warm-up. EmailGuard still receives a scoped Google SMTP/IMAP app password, fetched just in time after its own approval. Legacy CMR, Zapmail, and credential-file imports use that scoped app-password handoff for both services. A preview never materializes it; the stored secret is encrypted immediately and deleted once a handoff confirms the same revision, or after seven days unused.
Manual and native Google rows stay credential_required: OXYGEN does not turn its Gmail OAuth refresh token into a third-party password.
Connecting a ready Google row to EmailGuard is a recurring paid action, so preview it first. The preview writes nothing externally and materializes no password:
oxygen mailboxes emailguard connect --mailboxes ada@send-acme.com --json
oxygen mailboxes emailguard connect --mailboxes ada@send-acme.com \
--approved --plan <plan_hash> --max-credits <n> --jsonBoth --plan and --max-credits must echo that fresh preview (--max-credits 0 on your own EmailGuard key). Account connection sends no placement probe.
Microsoft 365 and Azure
Microsoft warm-up never uses a mailbox password or app password — Exchange Online retired that authentication and OXYGEN refuses it categorically.
Managed Microsoft 365 and Azure inboxes use native provider export. Warm-up defaults on for a fresh managed order where available: the preview itemizes the recurring add-on and the exact future addresses under warmup_activation, and approving that quote lets the worker export and start only those mailboxes once they provision. An approved expansion does the same for its new addresses. No second mailboxes warmup enable approval is needed, and there is no Outlook browser step. A non-cancelled warmup subscription at the provider blocks the handoff — cancel it (pausing is not enough) and let the worker retry.
Every non-managed Microsoft mailbox needs one Outlook OAuth authorization, opened by someone who can sign in to that exact address. Warm-up has no tenant-wide shortcut: a 40-inbox pool means 40 authorizations.
# 1. Preview the exact mailbox scope and plan hash. 0 credits, nothing minted.
oxygen mailboxes warmup microsoft --mailboxes ada@send-acme.com --json
# 2. Approve minting the per-mailbox consent links. Still 0 credits; --max-credits 0 is required.
oxygen mailboxes warmup microsoft --mailboxes ada@send-acme.com \
--approved --plan <plan_hash> --max-credits 0 --json
# 3. Poll, then open a returned consent_url only for the mailboxes that have one.
oxygen mailboxes warmup microsoft --mailboxes ada@send-acme.com --status --json--status returns one headline consent_state plus a per-mailbox reason. A mailbox counts as authorized only when the warm-up rail confirms it. Links are reused rather than re-minted on a repeat run and minting is bounded per run, so a large pool takes several passes. Warm-up stays off throughout — starting it is the separate priced approval oxygen mailboxes warmup enable --approved --plan <hash> --max-credits <n>. An unauthorized mailbox reports consent_required and is never billed or reported as warming.
Sending is a different grant again: oxygen mailboxes connect-oauth --provider microsoft --vendor oxygen --authorization-mode tenant covers up to 500 selected addresses with one administrator approval, while individual signs in per mailbox, 10 per run. Microsoft's application grant is tenant-wide at the provider, so OXYGEN — not Microsoft — enforces the selected-address boundary at verification and send time unless the tenant also configures Exchange Application RBAC.
What it costs
Compatibility reads, file validation, import and registration, connect-oauth, and the Microsoft warm-up consent all cost 0 credits. Validation additionally makes no network request and needs no authentication; import and consent do write workspace state.
OXYGEN Warm-up and EmailGuard monitoring are recurring per-inbox subscriptions; their prices are in the pricing reference. EmailGuard monitoring is included in a new managed order unless you pass --no-placement — on Microsoft inboxes too — and bills 0 credits when it runs on your own EmailGuard key.
Wiring a mailbox to the Sequencer is not itself billed today. A per-connected-mailbox monthly rail exists in code behind a flag that is off; see Built, not currently charged.
Removing a mailbox
Select rows in Accounts → Email and press Delete; the dialog confirms with Delete mailboxes. This disconnects the address from OXYGEN and does not delete the Google or Microsoft account. History stays inspectable, the current period is not refunded, and any recurring line on that mailbox stops at the next renewal. Sequences referencing the selection keep their status but lose those senders — they are not auto-paused. Managed subscriptions, warm-up, and monitoring recur independently, so the preview blocks until you cancel those lines through their own controls. Re-importing a deleted address restores the row and needs fresh OAuth.
oxygen mailboxes delete --mailboxes ada@send-acme.com --json
oxygen mailboxes delete --mailboxes ada@send-acme.com \
--approved --plan-hash <fresh-plan-hash> --confirmation "DELETE 1 MAILBOX" --jsonRelated
- Set up email sending — the end-to-end setup walkthrough.
- Connect and warm up existing mailboxes — exact Google, Microsoft individual, and Entra tenant authorization steps.
- Integrations — how provider credentials are connected.
- Approvals — previews, exact scope, and approval tokens.
- Pricing reference — the current price of every email-infrastructure line.