Workflow Engine · ac-python-api · Intelligence DB

People Enrichment

People enrichment is the step that turns a resolved LinkedIn URL into a complete, trustworthy contact profile. It reads the global people intelligence cache first, runs a fixed-order provider waterfall only on what is missing, and writes every resolved fact back with provenance - so the first org to enrich a person pays once and every later search rides free. Headhunter and People Signals discover who; this fills them in.

Entry enrich_person()Identity LinkedIn URLCost of a hit $0Write-back provenance-tracked
Resolved LinkedIn URLCache lookupWaterfall on miss·Write back · serve
00

What enrichment does

Discovery finds a person worth contacting and resolves them to a canonical LinkedIn URL - Headhunter from a brief, People Signals from a signal. Enrichment is the separate step that fills the profile, contact and employment facts they need - cheaply, and only once across all tenants. It is a cache-first read in front of the people intelligence database: a fresh hit costs nothing, a miss runs the waterfall and writes back with provenance.

5
Waterfall tiers
cache → cheap → structured → email → web
$0
Cost of a cache hit
vs ~$0.04-0.30 per chain
Enrichment per person
across all tenants
URL
Identity is the LinkedIn URL
never the employer
KEYDiscovery finds who, enrichment fills them in

Headhunter is the search - criteria in, a ranked candidate set out. enrich_person() is the fill - one identity in, one complete profile out, cache-first and written back. Splitting the two means there is exactly one enrichment path, shared by Headhunter, People Signals, signal monitoring and the CRM save - so the expensive part of learning a person is paid once and reused everywhere at $0.

01

Data flow

How a single enrich_person() call moves through the system. Pick a path - a cache hit, a miss that runs the waterfall and writes back, or a job-change re-verify - and the diagram lights the nodes and edges it touches, in order. Click any node for detail.

1234
Cache hit

Fresh hit: the facade finds an in-TTL profile in intel_people and serves it straight into the result and the tenant pre-fill. Zero provider calls, $0 spend.

Click any node for detail.
WHYOne miss pays for every later hit

The write-back path is what turns a per-org cost into a one-time cost: the first tenant to touch a person runs the waterfall, the result lands in intel_* with provenance, and every later run by any tenant follows the cache-hit path at $0. The pre-fill into the tenant CRM is one-way by construction - nothing tenant-derived ever travels back up.

02

Cache-first read

The core mechanic: enrichment asks the intelligence DB first, the waterfall runs only on a miss, and every miss makes the cache smarter. Pick a scenario and watch the lookup path - including a job change, the one event that deliberately invalidates a fresh row.

$0provider spend
0provider calls
cacheserved as-is
WHYTTL tiers keep the hot path free

Person facts age at different speeds, so freshness is checked per field group: the hot employment edge and title (~30 days), warm work-email re-verification and skills (~90 days), cold education (~365 days). A lookup never blocks on a refresh - stale data is served immediately and a background job re-runs just the expired tier. A job-change signal can force-expire the hot tier early.

03

Enrichment waterfall

On a miss the waterfall runs in fixed order - cache, then cheap search, then structured extraction + email, then web fallback. The model never reorders tiers; its only decision is escalate-or-stop against a completeness target, so the run stops the moment the profile is good enough. Pick a tier to inspect it.

Tier 0 · deterministic

Intelligence DB cache Hit: skip all paid tiers

A global people-facts cache keyed by the normalized LinkedIn URL, TTL per field group. A hit returns the profile, current employer edge and work email instantly and skips every paid tier - the cross-org payoff where the first org to enrich a person pays, and every later search rides free.

free
Cost
per person
inherited
Reputation
provenance weight
WHYStructured beats web on conflict

Every resolved field carries the reputation of the tier that produced it. When two tiers disagree, the higher reputation wins - a structured provider value is never silently overwritten by a web-inferred one, and an unverified web value is stored low-reputation so it caps its own contribution to the downstream lead score.

04

Provider categories

The structured tier is a registry of enrichers grouped by what they fill. Each is an enricher, never a discovery source - Headhunter's search runs first, then enrichment calls these on the one resolved person. Pick a category to see what runs in production today and the vetted candidates that would close its gaps.

The durable person record - name, current title, seniority, location, skills, keyed on the LinkedIn URL.

In production

Running today

  • Exa - Neural profile / company search; the cheap first paid tier.
  • Firecrawl - Structured extraction from the public profile page. In production today.
Vetted candidates

Recommended next

  • People Data Labs - 1.5B+ person profiles; structured employment and skills via API.
  • Proxycurl - LinkedIn-shaped person + company endpoints, pay-per-lookup.
05

Privacy and erasure

Enrichment is the main writer into the shared layer, so the seam matters most here. It is enforced twice: by policy (only publicly observable professional facts plus discovered work emails may be written back) and by mechanism (client roles get RLS deny-all on intel_*; the ac-python-api service role is the only door).

Global intel layerno org_idRLS deny-all · service role only
intel_peopleintel_person_employmentsintel_sources
Public professional facts + work email, with provenance

Every fact the waterfall resolves flows up into the shared layer, tagged with source and freshness.

Cached profile pre-fills the tenant row

The public-fact profile pre-fills crm_people enrichment fields at save time.

CRM stage, shortlist, outreach, notes, lead score

Tenant judgement and workflow state never enter the shared layer, even aggregated.

Tenant layerorg_id + RLS per org
crm_peopleworkflow_run_peopleshortlists · outreach · notes
Written back to the shared layer
  • Public professional profile: name, current title, seniority, location
  • Employment history edges joined to intel_companies
  • Verified work emails, tied to the current employer edge
  • Public posts, speaking, thought leadership
Never written back
  • CRM stage, shortlist, outreach history, notes, tasks
  • Whether any tenant saved, rejected or contacted a person
  • Per-tenant ICP scores, lead scores, fit judgement
  • Anything derived from tenant activity, even aggregated
GDPRErasure is first-class

Enriching people stores personal data, so deletion is a build requirement, not a follow-up. A hard delete spans the layer plus a SHA-256 suppression hash that blocks re-ingestion; the intel_sources rows a person references are the data-subject-access inventory and the Article 14 source lineage. Caching paid provider output cross-tenant (Hunter in particular) needs a terms review before write-back is enabled for it.

06

What enrichment writes

Three tables carry the output. The profile is the fast read path the next run hits; the employment edge models a career as append-only history so a job change supersedes rather than overwrites; sources keep every claim explainable and revocable in one delete. Click a table.

intel_peopleFlat canonical profile - the fast read path enrichment fills
linkedin_urltext · uniquenormalized, primary identity key - never the employer
full_name · headline · title · senioritytext
location · avatar_urltext
skillstext[]
extrajsonblong-tail provider fields until usage earns a column
fetched_hot_attimestamptzemployment + title TTL (~30d)
fetched_warm_attimestamptzwork email re-verify, skills (~90d)
fetched_cold_attimestamptzeducation (~365d)
1

Structured provider payloads (Hunter, People Data Labs)

highest confidence, contractual accuracy

2

First-party extraction of the public profile (Firecrawl)

public and current, less structured

3

Web-inferred (GPT web_search)

lowest confidence, never overwrites a higher rank silently

See the people intelligence database for the full storage architecture, identity model and erasure path, and Headhunter for the discovery pipeline this enrichment step fills in. The company twin is Company Enrichment.
AgencyCore · People EnrichmentWorkflow Engine · intelligence DB