OXYGENOxygen/ Docs
Guides

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

Read expected_credits (typical), estimated_credits (worst case), recommended_max_credits, estimated_row_count, selected_providers, blocked_providers, and runnable.

The next command spends credits. --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 --json

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

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

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

More on the mechanics: Waterfalls, Cells, Spend caps.

On this page