Enrich your leads
Fill work emails, mobile phones, LinkedIn URLs, and company facts onto a table you already have, for a cost you approve first.
Start from a table whose rows carry one identity signal each: a LinkedIn profile URL, or a full name plus a company domain.
Add the column in the app
Open Tables in the sidebar, open your table (/tables/<id>), then click the + at the right end of the column header row (tooltip: Add column).
The panel opens on Column types, and the first three entries are the enrichment ones: Work email, Mobile phone, LinkedIn URL. Everything under the Integrations divider is one vendor's tool rather than a waterfall.
Choosing an entry creates the column and opens its editor, which has five parts:
- Intent — what the column resolves.
- Waterfall sequence — the ordered provider lanes. On a work-email column you also pick a keying: Auto is recommended; LinkedIn URL, Name + Domain, and First/Last + Domain force every row down one.
- Input mapping — map your row columns onto the canonical inputs. A column with nothing mapped cannot run.
- Email verification (work email only) — a toggle. On, every found address is verified before it lands in the cell.
- Estimated cost — the typical spend, with the worst case shown as "up to".
Nothing is charged yet. The footer has Save without running and Run; Run offers Run for first 10 rows, Run for next 25 rows, Run missing results, Rerun full table, and selected rows, then a cost step showing Estimated credits, Rows affected, Per row, Recommended cap, Available, and a Max credits field ("Hard cap on credits spent. The run stops before exceeding it."). Credits move only when you confirm that step.
Why a waterfall is cheaper than one provider
Lanes run in order and a row stops at the first lane that returns a result. Billing depends on the intent: work email and phone bill on a hit — a lane that finds nothing releases its reservation, so a row resolved early never pays the lanes behind it. LinkedIn URL and company-field enrichment bill every lane they attempt, hit or miss. Full rules: tool costs. On Auto, a lane a row cannot drive is skipped, so a row with only a name and domain never pays for a LinkedIn-keyed provider. Premium high-cost lanes stay off unless you pass --allow-premium-lanes.
The same thing from the CLI
Preview first. It makes no provider calls and costs nothing:
oxygen enrich-column preview leads \
--capability work_email \
--linkedin-url-column linkedin_url \
--company-domain-column domain \
--limit 10 --jsonRead expected_credits (typical), estimated_credits (worst case), recommended_max_credits, estimated_row_count, selected_providers, blocked_providers, and runnable.
--approved authorizes this one run; --max-credits is the ceiling it cannot cross.oxygen enrich-column run leads \
--capability work_email \
--linkedin-url-column linkedin_url \
--company-domain-column domain \
--only-missing \
--max-credits 200 \
--approved --json--capability also takes mobile_phone and linkedin_url; add --verify-phone to get line type and carrier. Follow the run with oxygen table-runs wait <run-id> --json and oxygen table-runs provider-summary <run-id> --json. The response's web_url opens the same run in the app.
Three errors stop this command:
approval_required(exit 7) — you left off--approved. Read the preview, then retry with it.selection_required— pass--limit <n>,--all, or--only-missing.invalid_enrichment_mapping— no provider can run on the columns you mapped. Map a LinkedIn URL column, or a name column plus--company-domain-column.
What lands in the table
A work_email run writes email, email_provider, email_status, and email_enriched_at, plus the JSON column work_email_enrichment holding every attempt. A mobile_phone run writes mobile_phone_e164, phone_source, phone_line_type, phone_carrier, phone_enrichment_status, and phone_enriched_at.
Read a cell before you trust it
Unsettled cells wear a chip: Queued, Running, Retrying, Rate limited, Failed. A settled cell shows none, so a blank cell with no chip means the waterfall ran and found nothing — not that it never ran.
Double-click a cell (or select it and press Enter) to open Cell details: Last run (Status, Attempts, Completed), the Value tree, and Rerun this cell. The hover button on a filled cell's right edge is Expand into result columns — or Expand sub-columns on the column header — which fans the waterfall out into one column per provider attempt; clicking one opens it as a Waterfall attempt.
Same data on the terminal:
oxygen cells inspect leads <row-id> work_email_enrichment --jsonOne row, no table
Each lookup previews for free (dry_run is the default) and needs --mode live --max-credits <n> to actually spend:
oxygen find email --full-name "Ada Lovelace" --company-domain acme.com --json
oxygen find email --full-name "Ada Lovelace" --company-domain acme.com --mode live --max-credits 20 --jsonoxygen find phone, oxygen find linkedin, and oxygen find company --fields domain,linkedin_url,headcount,industry take the same two modes. With no identity signal at all they fail with missing_identity_input.
Verify addresses you already have
oxygen verify email ada@acme.com --json
oxygen verify email ada@acme.com --mode live --max-credits 40 --jsonThe free first call returns estimate.recommended_max_credits — use that number, because catch-all addresses escalate to a second, dearer verifier. Verdicts are valid, invalid, catch_all, or unknown; see Email verification before you filter on them.
Company facts across the table
oxygen companies enrich preview leads --missing-fields domain,linkedin_url,headcount,industry --limit 10 --json
oxygen companies enrich run leads --missing-fields domain,linkedin_url,headcount,industry --limit 100 --approved --max-credits 150 --jsonMore on the mechanics: Waterfalls, Cells, Spend caps.