PPactDocs
Enrichment

Providers

The enrichment provider roster — free public sources, paid commercial APIs, BYOK vendors — and the per-tenant config, trust weights, and circuit breakers that govern them.

The provider contract

Every provider implements the EnrichmentProvider ABC (core/enrichment/providers/base.py). A provider declares four class attributes — name, supported_fields, latency_budget_ms, is_paid — and one async fetch(subject, fields_requested, *, deadline) method that returns a ProviderResult. The async signature is what lets the runner fan out to many providers concurrently per record with asyncio.gather.

Providers must respect their deadline and return partial results rather than raising, and each paid provider default-denies when its credential is missing — so adding one to the roster is safe even before ops provisions a key.

The default roster

core.enrichment.registry.default_providers() returns one instance of every shipping provider, in fan-out order:

ProvidernamePaid?Latency budget
OpenAIopenaiyes90s
Anthropicanthropicyes60s
SEC EDGAR (full)sec_edgar_fullno25s
Web scraperweb_scraperno12s
Company registrycompany_registryno8s
Hunterhunteryes15s
Clearbitclearbityes20s
People Data Labspdlyes20s
News (recent)news_recentno8s
Funding eventsfunding_eventsno10s
Tech stack (live)tech_stack_liveno6s
Exec movesexec_movesno8s
Hiring trendshiring_trendsno8s

Free public-data providers (SEC EDGAR, web scraper, company registry, the Wave "live/deep" signal providers) carry no marginal API spend. Paid providers route through your credentials and bill the ledger.

Live/Deep signal providers need a fetcher wired

news_recent, funding_events, tech_stack_live, exec_moves, and hiring_trends (added in alembic 0323) ship with an injectable fetcher that default-denies when it's the empty stub. Production deploys wire real fetchers via Wave connectors or BYOK credentials — until then these providers return nothing rather than erroring.

BYOK vendors

Beyond the default roster, Pact ships adapters for commercial data vendors you connect with your own key (BYOK): Apollo, ZoomInfo, Lusha, Cognism, RocketReach, and Seamless (the BYOK_ADAPTERS tuple). These are deliberately not in default_providers() — they're driven from your provisioned credentials via byok_adapter_classes() and materialized into the enrichment_providers catalogue by sync_provider_catalogue.

MCP-federated enrichment (McpFederatedProvider, name="mcp_federated") is a separate path: it is not a BYOK adapter and is not in BYOK_ADAPTERS. Instead, Wave BC-connected MCP enrichment servers are discovered per tenant by federated_providers_for_tenant / register_federated_providers, each materializing as a fresh provider bound to its discovery handle.

Per-tenant config and health

GET /v1/enrichment/providers returns each provider with your tenant's overrides and live health. PATCH /v1/enrichment/providers/{name} updates them.

  • enabled and trust_weight (0–10) are stored per tenant in enrichment_provider_config (alembic 0017). Trust weight tunes how a provider's answer is scored during multi-source merge.
  • Circuit breaker + failure history live in enrichment_provider_health (alembic 0018): circuit_breaker_state, consecutive_failures, last_success_at, last_failure_at. A provider that keeps failing trips its breaker and is skipped until it recovers.

Providers can additionally be gated by feature flag — enabled_providers_for_tenant is default-on, disabling a provider only when an explicit flag evaluates false, and falling back to the full roster if the flag store is unavailable.

bash
curl -X PATCH https://api.pact.place/v1/enrichment/providers/clearbit \
  -H "Authorization: Bearer $PACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "trust_weight": 2.5}'