Waterfalls
Ordered provider attempts that stop at the first usable result.
A waterfall tries providers in order until one returns a usable result.
Waterfalls find a value the row is missing. To check an address the row already has, see Email verification — a different chain with its own catch-all escalation.
Capabilities
--capability takes work_email, mobile_phone, or linkedin_url. Each writes the value, a match confidence, and the provider that produced it. mobile_phone adds --verify-phone (line type and carrier); linkedin_url gates every hit through name-and-company identity validation.
Which provider each chain tries first and what it falls back to is listed under Waterfall defaults. Override the order for one run with --provider-order.
Previewing an enrichment
oxygen enrich-column preview <contacts-table> \
--capability work_email \
--linkedin-url-column linkedin_url \
--company-domain-column domain \
--limit 10 \
--jsonPreview calls no provider and spends 0 credits. It returns the resolved provider profile, per-provider preflight, and the credit estimate that sizes --max-credits.
For a one-off company lookup, oxygen find company --domain <domain> --json (its default mode is dry_run) prints the same thing as a plan: would_attempt lists, per requested field, the lanes in the order a live run would walk them, each with its estimated_credits and, where the run would not attempt it, a pre_skip reason. It is a plan, not the answer — found stays null until you run it live. The order is a fixed preference per field, not cheapest-first; a free lane behind a priced one is reached only if the priced one finds nothing.
pre_skip reasons:
missing_identity_input— the lane keys on an identity the row does not hold yet (requires_identitynames it, usuallylinkedin_urlfor a domain-only row). When another lane can resolve that identity from what the row holds, the plan adds aprerequisite: truestep ahead of the field and plans the field as if it succeeds; alinkedin_urlordomainfield you request yourself does the same for the fields listed after it.byok_only— the provider runs only on a key you connect (oxygen integrations connect <provider> --api-key <key>); Oxygen holds no managed account for it. Itsestimated_creditsis0because nothing managed would be billed, not because it is free.credit_ceiling_reached— the lane's estimate does not fit under the--max-creditsyou passed.identity_prerequisite_unaffordable— a priced identity pre-step would eat the budget the requested field itself needs.
Per-cell provenance
An enrichment cell records the whole call trace — source is the tool that won:
{
"value": "ada@acme.com",
"source": "leadmagic.email_finder",
"confidence": "verified",
"attempts": [
{ "provider": "hunter", "operation": "email_finder", "status": "no_result", "latency_ms": 412 },
{ "provider": "leadmagic", "operation": "email_finder", "status": "success", "latency_ms": 380, "billing": { "credits_used": 1 } }
]
}Attempt status is success, no_result, skipped, error, or incomplete_async (the provider took the request async and is polled later — not a failure). A skipped attempt carries a reason such as missing_input or credit_ceiling_reached. Inspect a cell with oxygen cells inspect.
Cost behavior
Billing depends on the intent. Work-email and phone waterfalls charge only the lane that returns a value, so a row that resolves early costs less than one that exhausts the list. LinkedIn-URL and company-field waterfalls charge every managed lane they attempt, whether or not it finds anything — see tool costs. --max-credits is required on a live run and caps total spend either way.
Lanes priced above 500 credits per row are pre-skipped unless you pass --allow-premium-lanes, so an unattended run cannot bill-shock. They appear in the trace as skipped with reason premium_lane_not_opted_in — that, not an empty chain, is why an expensive mobile_phone provider never fires by default.
Running live
oxygen enrich-column run <contacts-table> \
--capability work_email \
--linkedin-url-column linkedin_url \
--company-domain-column domain \
--limit 100 \
--approved \
--max-credits 100 \
--jsonCompany field enrichment has its own helper. run defaults to live mode, so it needs --approved and --max-credits:
oxygen companies enrich preview <accounts-table> --missing-fields headcount,industry --limit 10 --json
oxygen companies enrich run <accounts-table> --missing-fields headcount,industry --limit 100 --approved --max-credits 100 --jsonRelated
- Provider reference — the default and fallback provider for every chain.
- Columns — enrichment columns and materialized outputs.
- Cells — inspecting per-attempt history.