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:
| Provider | name | Paid? | Latency budget |
|---|---|---|---|
| OpenAI | openai | yes | 90s |
| Anthropic | anthropic | yes | 60s |
| SEC EDGAR (full) | sec_edgar_full | no | 25s |
| Web scraper | web_scraper | no | 12s |
| Company registry | company_registry | no | 8s |
| Hunter | hunter | yes | 15s |
| Clearbit | clearbit | yes | 20s |
| People Data Labs | pdl | yes | 20s |
| News (recent) | news_recent | no | 8s |
| Funding events | funding_events | no | 10s |
| Tech stack (live) | tech_stack_live | no | 6s |
| Exec moves | exec_moves | no | 8s |
| Hiring trends | hiring_trends | no | 8s |
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.
enabledandtrust_weight(0–10) are stored per tenant inenrichment_provider_config(alembic0017). Trust weight tunes how a provider's answer is scored during multi-source merge.- Circuit breaker + failure history live in
enrichment_provider_health(alembic0018):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.
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}'