Enrichfold
Provider-neutral, provenance-first entity enrichment for people and companies.
enrichfold is an offline-first core, not a scraping product: applications supply
their own discovery providers and credentials. Every accepted attribute retains its
source URL, observation time, and confidence, so downstream systems can decide
whether a result is suitable for an automated action or requires review.
from enrichfold import Entity, EnrichmentPipeline, Evidence
class CompanyProvider:
def discover(self, entity):
return [Evidence(
source_url="https://example.com/team",
observed_at="2026-08-20T12:00:00Z",
confidence=0.94,
attributes={"industry": "software", "company_size": "51-200"},
)]
company = Entity.company(domain="example.com")
result = EnrichmentPipeline([CompanyProvider()]).enrich(company)
print(result.attributes["industry"].value) # software
Evidence, conflicts, and review gates
enrichfold keeps provider I/O outside the library. Its core turns supplied
evidence into deterministic decisions while retaining disagreements for a human
approval flow. A conflicting value is never silently accepted:
from enrichfold import Claim, Evidence, reconcile_claims
result = reconcile_claims([
Claim(
field="industry",
value="software",
evidence=Evidence(
source_url="https://acme.example/about",
observed_at="2026-08-20T12:00:00Z",
confidence=0.91,
),
),
Claim(
field="industry",
value="retail",
evidence=Evidence(
source_url="https://directory.example/acme",
observed_at="2026-08-20T12:00:00Z",
confidence=0.88,
),
),
])
industry = result.fields["industry"]
assert industry.value == "software" # stable suggested value
assert industry.status == "needs_review" # do not automate an action
assert result.requires_review is True
Claim(kind="inferred", ...) also requires review even with no competing
claim. This distinction makes it possible to keep model-produced hypotheses
without presenting them as observed facts.
Multi-provider research runs
ResearchEngine is the boundary for applications that call several research
providers. It runs caller-owned adapters concurrently, reserves generic units
before starting work, retains each provider outcome, and returns an explicit
coverage/review state. It does not make network calls itself.
from enrichfold import (
Claim,
Evidence,
Entity,
ProviderOutput,
ProviderSpec,
ResearchBudget,
ResearchEngine,
)
def official_site(entity):
# The host application owns this adapter, its HTTP client, and credentials.
return ProviderOutput(
claims=(Claim(
field="industry",
value="software",
evidence=Evidence(
source_url="https://example.com/about",
observed_at="2026-08-20T12:00:00Z",
confidence=0.9,
),
),),
usage_units=2,
)
result = ResearchEngine(
[ProviderSpec("official-site", official_site, reserved_units=2)],
budget=ResearchBudget(max_units=5),
).run(Entity.company(domain="example.com"), requested_fields=("industry",))
assert result.status in {"completed", "partial", "needs_review", "failed"}
assert result.budget.reserved_units == 2
An optional EvidenceValidator can return EvidenceVerdict("needs_review", reason) for a weak source or EvidenceVerdict("rejected", reason) to keep
it out of resolution. In either case, the result preserves the original claim,
source URL, and verdict in evidence_assessments.
Company identity gate
Before a caller enriches or acts on a company, use the offline identity gate. It is deliberately conservative: free mailboxes, invalid sites, domain conflicts, and corporate domains that do not exactly match the name receive a review status. Applications can pass separately verified site metadata when they have it.
from enrichfold import derive_company_identity
identity = derive_company_identity(
email="hello@acme.example",
company_name="Acme",
website="https://www.acme.example/about",
)
assert identity.status == "verified"
assert identity.canonical_domain == "acme.example"
Design boundaries
- No network calls, scraping, credential handling, contact exporting, or outreach.
- No inferred facts: a field is returned only when a provider supplies evidence.
- Conflicts have a deterministic suggested value but are marked
needs_review. - Inferred claims are always marked
needs_review. - Multi-provider runs reserve caller-defined generic units before execution and expose partial coverage rather than hiding failed or skipped providers.
- Optional source-policy hooks can accept, reject, or route evidence to review; the package never fetches or validates URLs on its own.
- Company identity is verified only through an exact name/domain match or caller-supplied, independently verified same-domain site metadata.
- Bring your own providers for search engines, public data APIs, browser tools, or internal approved sources.
The package intentionally does not decide whether a review is approved or run an action after one; persistence, permissions, UI, and provider adapters stay with the host application.
Installation
pip install enrichfold
Development
uv run --with pytest pytest -q
python -m build
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enrichfold-0.3.0.tar.gz.
File metadata
- Download URL: enrichfold-0.3.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f28f9a18116bf770518cacf75a77d29da0a9b2f0cb28289f7aa8dd3ad5206a06
|
|
| MD5 |
f31fd5f1fab6a0acdfc37fcf5474fcc8
|
|
| BLAKE2b-256 |
b588b8a6b1d7f3354c910772f5cd0d1c86ac51e720f93d0aa4fbd3698a42eb64
|
Provenance
The following attestation bundles were made for enrichfold-0.3.0.tar.gz:
Publisher:
publish.yml on Mihailorama/enrichfold
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enrichfold-0.3.0.tar.gz -
Subject digest:
f28f9a18116bf770518cacf75a77d29da0a9b2f0cb28289f7aa8dd3ad5206a06 - Sigstore transparency entry: 2534964353
- Sigstore integration time:
-
Permalink:
Mihailorama/enrichfold@579df497ebff025fc7682477aaa0b2a9131dfa36 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Mihailorama
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@579df497ebff025fc7682477aaa0b2a9131dfa36 -
Trigger Event:
release
-
Statement type:
File details
Details for the file enrichfold-0.3.0-py3-none-any.whl.
File metadata
- Download URL: enrichfold-0.3.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6afd24e8ec9d11b780808f3c5bfb8e88719c8276c6ba89b1fa3900aca7fccf31
|
|
| MD5 |
c9678bc09498a85572d601ab8b777204
|
|
| BLAKE2b-256 |
cee3af04e248471dfb9a30c57b7669dfc4b09fb4042d5bdea5d1af0bf6be558a
|
Provenance
The following attestation bundles were made for enrichfold-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on Mihailorama/enrichfold
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enrichfold-0.3.0-py3-none-any.whl -
Subject digest:
6afd24e8ec9d11b780808f3c5bfb8e88719c8276c6ba89b1fa3900aca7fccf31 - Sigstore transparency entry: 2534965208
- Sigstore integration time:
-
Permalink:
Mihailorama/enrichfold@579df497ebff025fc7682477aaa0b2a9131dfa36 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Mihailorama
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@579df497ebff025fc7682477aaa0b2a9131dfa36 -
Trigger Event:
release
-
Statement type: