Enrichment overview
How Pact's enrichment engine fans out to data providers, enforces consent and budgets, and records every field's source and cost.
What the enrichment engine does
Pact's enrichment engine takes a thin contact or company record and fills in the fields you're missing — job title, verified email, employee count, tech stack, recent funding, compliance flags — by fanning out to a roster of data providers, validating what comes back, and recording the source, confidence, and cost of every value it writes.
Everything runs under the /v1/enrichment/* API surface (implemented in
api/routes/enrichment_v2.py). Every read and write is scoped to the
tenant_id derived from your auth context — never from the request body — so one
tenant can never see or spend against another's data.
Consent-native by construction
Enrichment is profiling under GDPR Art. 4(4). Before any run touches a subject,
_require_enrichment_consent calls core.enrichment.consent_gate.check. A
subject who has withdrawn consent or is on the suppression list is never
disclosed to a third-party vendor and never costs you a credit — the request
returns 409 Conflict instead.
The core building blocks
- Levels (L1–L4) — presets that bundle a set of enrichment types.
L1Basic ($0.01/record),$0.10),L2Standard (L3Premium ($0.50),$2). Defined inL4Premium+ (core/enrichment/levels.py. - Providers — pluggable adapters built on the
EnrichmentProviderABC. Free public sources plus paid commercial APIs and your own BYOK vendors, invoked concurrently per record. - Runs — a batch of targets at a chosen level. Preview first (
POST /v1/enrichment/runs/preview) to see cost and budget headroom, then execute (POST /v1/enrichment/runs). - Ledger — every provider call lands one row in the
enrichment_callstable with a three-number cost breakdown. - Budgets — multi-scope caps that alert, throttle, or hard-stop spend.
- Quarantine — values that fail validation are held for human review rather than silently written.
- Tier policies — rules that auto-select a level per record based on segment, status, deal stage, and more.
The three-number cost model
Every enrichment call carries three figures, resolved by
core.enrichment.pricing_policy.compute_customer_price:
| Number | Meaning |
|---|---|
provider_cost_cents | What Pact pays the vendor (raw) |
platform_fee_cents | Pact's markup |
customer_price_cents | What appears on your ledger (provider + fee) |
The platform fee buys consent enforcement, validation and quarantine, dedupe and
idempotency (no double-charge for repeat calls within 30 days), full provenance,
and retry/circuit-breaker reliability — the PLATFORM_FEE_EXPLAINER string
surfaced in the run preview.
A typical run
- 1
Preview
POST /v1/enrichment/runs/previewwithtarget_type(contactorcompany),target_ids, and alevel. You get an itemized per-field cost breakdown, budget headroom, and whether the run would block. - 2
Execute
POST /v1/enrichment/runs. If any budget would block, the run returns402before spending. Otherwise providers fan out concurrently per record. - 3
Validate and record
Each returned value is validated. Clean values are written as facts with source and confidence; suspect values go to quarantine. Every call is billed to the ledger.
- 4
Review and reconcile
Inspect the ledger, clear quarantine, and watch budget alerts. Per-record provenance is available at
/v1/enrichment/contacts/{id}/provenanceand the company equivalent.