OXYGENOxygen/ Docs
Providers

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 haveWarm-upEmailGuardDo this
Google inboxes bought through OXYGENyes, automaticyesnothing — the approved order starts warm-up after provisioning
Microsoft 365 or Azure inboxes bought through OXYGENyes, automaticnonothing; leave monitoring off
A Google Workspace export containing app passwordsyesyesoxygen mailboxes import --from credentials
Google Workspace with identity only, or connected by OAuthnonore-import with app passwords, or warm up at the source vendor
Microsoft 365 / Entra you did not buy through OXYGENafter one consent eachnooxygen 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 --json

The 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 fieldMeaning
data.compatibilityVerdicts for the checked mailboxes: origin, platform, native send, warmup, emailguard
data.provider_matrixThe public contract below, returned even when --mailboxes narrows the rows; its warm-up key is still spelled trulyinbox after the previous vendor
data.import_methodsEach ingestion method with its surface, inputs, credential policy, and command
data.import_fieldsCanonical fields, aliases, requiredness, and whether each crosses the network
data.non_transferable_authCredential kinds OXYGEN will never accept as an import
data.summaryPool rollup by native-send, warmup, and EmailGuard state
data.web_urlThe 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.

OriginInfrastructure platformDownstream authOXYGEN Warm-upEmailGuard monitoring
InboxKitGoogle WorkspaceProvider-native exportreadyready
InboxKitMicrosoft 365Provider-native exportreadyvendor_blocked
InboxKitMicrosoft AzureProvider-native exportreadyvendor_blocked
Legacy CMRGoogle WorkspaceGoogle app passwordreadyready
Legacy CMRMicrosoft 365Microsoft tenant consentconsent_requiredvendor_blocked
ZapmailGoogle WorkspaceGoogle app passwordreadyready
ZapmailMicrosoft 365Microsoft tenant consentconsent_requiredvendor_blocked
ExternalGoogle WorkspaceGoogle app passwordverification_requiredverification_required
ExternalMicrosoft 365Microsoft tenant consentconsent_requiredvendor_blocked
ExternalMicrosoft AzureMicrosoft tenant consentconsent_requiredvendor_blocked
ManualGoogle Workspacenonecredential_requiredcredential_required
ManualMicrosoft 365Microsoft tenant consentconsent_requiredvendor_blocked
ManualMicrosoft AzureMicrosoft tenant consentconsent_requiredvendor_blocked
NativeGoogle Workspacenonecredential_requiredcredential_required
NativeMicrosoft 365Microsoft tenant consentconsent_requiredvendor_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.

StateMeaningNext action
readyA safe handoff exists; a fresh managed-order quote may already authorize itApprove the managed order, or preview the standalone warmup / EmailGuard action
verification_requiredThe origin can support the handoff, but no mailbox-scoped credential was checkedoxygen mailboxes compatibility --mailboxes <address>, then read the verdict
credential_requiredNo credential-capable origin; OXYGEN never exports its OAuth token as a passwordRe-import through a credential file, or warm up at the source provider
consent_requiredA non-managed Microsoft mailbox needs one Outlook OAuth authorization firstoxygen mailboxes warmup microsoft — preview, approve, open the consent_url
vendor_blockedEmailGuard has no Microsoft OAuth or consent handoff, and password auth is retiredLeave 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 shapeCLI (oxygen …)BrowserCredentials
Identity file (CSV/JSON/JSONL/XLSX)mailboxes import --file … --vendor <slug>Add inboxes → Import a fileRejected
Google app-password exportmailboxes import --from credentials --vendor <slug> --file …Import a file → Credential exportEncrypted on arrival; seven-day transfer TTL
Addresses you already ownmailboxes import then mailboxes connect-oauthAdd inboxes → Connect existingFresh destination OAuth; source grants never transfer
Connected Zapmail inventorymailboxes import --from zapmailConnected integration flowProvider inventory is authoritative; no file password
Managed provisioningmanaged-inboxes subscribe / add-inboxesAdd inboxes → Buy managedThe 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 familyPreferred importBoundary
Zapmail--from zapmailThe API pull is authoritative; convert a legacy .xls workbook to CSV — .xls is not accepted
Instantly, Smartlead, PlusVibefile importUse credential mode only for rows holding real Google app passwords
Salesforge / Mailforge / Infraforge / Primeforgefile importStrip generic SMTP passwords and import identity only; custom SMTP handoff is not implemented
Lemlist or another sequenceridentity file importImport 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 --json

Files 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> --json

Both --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" --json

On this page