Automation · Revenue operations
How to build lead enrichment that does not stall the queue
Enrichment is the step that turns a form fill into something routable, and the step most likely to hold a lead in a queue while an API times out. What follows is the model behind enrichment that fails open, the prompts that build it, and what changes in production.
Built with Tray Headless
- System Marketo
- Step Cache check
- System Clearbit
- System ZoomInfo
- System Salesforce
The cache is checked before any provider is called, and the timeout releases the lead instead of holding it.
The short answer
What is lead enrichment?
Lead enrichment is four parts: a cache keyed on domain so the same company is never bought twice, a provider chain that falls back rather than failing, a hard timeout that lets routing proceed on partial data, and a record of which provider supplied each field. The part that goes wrong most often is the timeout. Enrichment that blocks routing turns a five second API problem into a lead that sat unassigned overnight.
What matters here
- Fail open, never closed. A lead with no firmographics still routes; a lead stuck waiting for firmographics does not.
- Cache on the registrable domain. Enriching the same company forty times in a month is the most common way credits disappear.
- Chain providers rather than trusting one. Coverage differs by geography and company size, and the second provider is cheaper than the missed lead.
- Record which provider gave you each field. When a value is wrong, you need to know where it came from before you can fix it.
- Never let enrichment overwrite a value a human typed. Sales corrections are data, not noise.
Who this is for
You run marketing or revenue operations. Routing and scoring both depend on firmographics you do not collect on the form, so enrichment sits in the critical path, and when it is slow everything behind it is slow.
How it works in practice
Everything that sits between a form fill and a routable lead.
- 1
The domain is extracted and normalised
Generic consumer domains are marked as unenrichable immediately, not sent to a provider that will charge for nothing.
- 2
The cache is checked first
If the company was enriched in the last 90 days, use it. Firmographics do not change weekly and credits are finite.
- 3
The provider chain runs in order
Primary, then fallback on a miss. Coverage differs by region and size, and one provider is never enough.
- 4
A hard timeout releases the lead
Routing proceeds on whatever arrived. Enrichment continues in the background and updates the record when it lands.
- 5
Fields are written with their source
Provider and timestamp per field, so a wrong value can be traced instead of argued about.
- 6
Human-entered values are never overwritten
A rep who corrected the employee count knows something the provider does not.
What enrichment is made of
Enrichment is not an API call. It is four parts, and the third one keeps the pipeline moving.
A cache keyed on domain
Ninety day freshness on the registrable domain. The same company arrives repeatedly and should be bought once.
A provider chain
Primary then fallback, with per-provider coverage tracked so the order can be changed on evidence rather than on a contract renewal.
A hard timeout that fails open
A few seconds, then routing proceeds. Enrichment catches up asynchronously and updates the record.
Provenance per field
Which provider supplied each field, and on what date. A rep who says the job title is wrong deserves a reply better than the CRM says so.
The Tray Headless prompts
Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.
Once per project, run
/tray-workflows:set-workspace
to pick the workspace these build in. Point it at a sandbox first.
- 1
Set up, then check what enrichment is even connected
Provider coverage is the constraint, so establish it first.
Headless skills
build-workflowUse build-workflow. Tell me which enrichment providers are authenticated and what the CRM connection is. Tell me what is missing. Do not stub a provider I have not authenticated, and do not assume a single provider covers everything.
- 2
Cache before you buy anything
The single change that most reduces enrichment spend.
Headless skills
build-workflowUse build-workflow. Before any provider call: 1. Extract and reduce the email domain to the registrable domain 2. Mark generic consumer domains as unenrichable and skip the call entirely, do not spend a credit finding out 3. Look the domain up in a cache table keyed on registrable domain 4. If the record is under 90 days old, use it and stop Write every successful enrichment back to that cache with a timestamp. The same company arrives repeatedly from different people, and buying it each time is how a credit budget disappears by October. - 3
Chain the providers, and measure them
One provider never covers everything, and coverage is testable.
Build the provider chain. Call the primary first. On a miss or a partial result, call the fallback for the fields still empty. Never call both when the first one succeeded. Merge the results field by field rather than taking whichever responded last. Record which provider supplied each field. Then report coverage by provider, split by region and by company size band. Provider order should be a decision you make from that table, not from whoever sold to you most recently.
Coverage differs sharply outside North America. If the report is not split by region it will hide the gap that matters most.
- 4
Time it out, and fail open
The rule that keeps enrichment out of the critical path.
Headless skills
tray-gotchasUse tray-gotchas, then set a hard timeout of a few seconds on the whole enrichment step. When the timeout fires, routing proceeds on whatever data exists. Do not hold the lead. Continue enrichment in the background and update the record when it lands, then re-evaluate routing only if a field that affects a rule actually changed. A lead with no employee count still routes, to the fallback if it must. A lead that sat in a queue overnight waiting for an employee count is a lost lead and a support ticket.
- 5
Write carefully, and never overwrite a human
Because sales corrections carry information the provider lacks.
When writing enriched fields back: Never overwrite a value that was entered by a person. Track which fields are human-entered and treat them as authoritative. Never overwrite a non-empty field with an empty one. A provider returning null is not a correction. Stamp each enriched field with its provider and timestamp. Then report the enrichment rate, the cache hit rate, the timeout rate and spend per enriched lead. If the cache hit rate is below half, the cache key is wrong.
- 6
Check it end to end, then hand it over
So provider order and freshness are tunable by the team that pays for it.
Run the per-step schema checks and the whole-workflow audit before this touches production. Then open the same workflow in Tray Build so marketing operations can change the provider order, the cache freshness window or the timeout in the visual canvas. Those are commercial decisions, and they should not need a coding assistant.
What it connects to
Enrichment sits between the form and the CRM, and it should touch as little as possible in between.
ZoomInfo
Fallback coverage where the primary misses, which is most often outside North America and below fifty employees.
Reads
Salesforce
Write enriched fields with their provenance, without touching anything a person entered.
Reads and writes
Marketo
Take the form fill at source and hand back the enriched record for scoring.
Reads and writes
Snowflake
Hold the enrichment cache and the coverage history, so provider decisions are made on measured data.
Reads and writes
Slack
Alert when the timeout rate or the miss rate crosses a threshold, because a provider degrading quietly looks like a routing problem.
Writes
Same build, other stacks
The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, Google BigQuery, Microsoft Teams, HubSpot, Databricks or Google Chat.
Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.
Running it in production
Enrichment costs money per call and sits in the critical path. Both of those need watching.
It runs where production runs, not on a laptop
Retries, backoff and background completion execute on the same engine, so a provider having a slow morning does not become a queue of unrouted leads.
Spend is visible
Calls, cache hits and cost per enriched lead reported continuously. Enrichment overspend is usually discovered at renewal, which is too late.
Managed credentials, not secrets in a config file
Every provider key is an authentication in your workspace. Enrichment keys are metered and expensive, so they should never sit in a file somebody can copy.
Ops owns the provider order
Chain order, cache window and timeout open in Tray Build, so the team holding the contract can change them.
Degradation is caught early
Alert on timeout rate and miss rate. A provider getting slower looks exactly like a routing problem until somebody checks.
Questions people ask
Why cache enrichment results?
Because the same company arrives repeatedly from different people. Enriching one domain forty times a month is the most common reason an enrichment budget runs out early.
Should enrichment block routing?
No. Set a hard timeout of a few seconds and route on whatever data exists, then complete enrichment in the background. A lead held overnight waiting for a field is worse than a lead routed without it.
Why use more than one provider?
Because coverage differs by geography and company size. The second provider costs less than the leads the first one silently misses, particularly outside North America.
Should enrichment overwrite existing values?
Never a value a person entered, and never a populated field with an empty one. A rep who corrected the employee count knows something the provider does not.
Can marketing operations change providers without engineering?
Yes. The provider chain, cache freshness and timeout open in Tray Build, so the team that owns the contract owns the settings.
Vibe-coding app guides
Vibe-code an entire app with Helix
This moves the data between systems. It does not give anybody a screen to work in. Build that app in Claude Code, Codex or Cursor, then deploy and run it governed on Tray Helix. Same kind of guide, same kind of prompts.
How to build an account research app (opens helix.tray.ai in a new tab)
Further reading
Background on the same subject, for the case rather than the build.
Related guides
Revenue operations
How to build lead import and validation
Validate before you write, quarantine the bad rows instead of dropping them, dedupe against what is already there, and keep the source. The prompts that build it.
Revenue operations
How to build lead routing that assigns in seconds
Match the lead to an account first, evaluate rules in a fixed order, catch what they miss, and time it from arrival. The Headless prompts that build it.
Revenue operations
How to build lead deduplication and merge
Detect on a scored rule set, survive the merge with field-level rules, keep the activity history, and never silently reassign an owner. The prompts that build it.
Data operations
How to build a CRM to warehouse sync
Capture history instead of current state, handle deletes and field changes, land raw then model, and prove the row counts. The Headless prompts that build it.
Last reviewed September 2026.