c1v-id
Identity resolution for AI applications
AI agents that interact with customers, CRMs, or any system of record face a critical decision point: is this person already in our system, or should we create a new record? Because both input and existing data are often messy, agents can confuse customer records, pollute data with duplicates, or deliver poor customer experiences.
c1v-id is an open-source identity resolution library that sits between the agent and the system of record, answering identity queries in milliseconds. It uses probabilistic record linkage with blocking strategies (~O(n) vs naive O(n²)), weighted multi-field scoring, and transitive clustering. Designed as a drop-in for LangChain agents, n8n workflows, and RAG pipelines. Zero ML dependencies. Configurable survivorship rules.
Installation
pip install c1v-id
Quick Start
Resolve duplicates in 10 lines of Python:
from c1v_id import IdentityResolver
resolver = IdentityResolver()
records = [
{"email": "john@gmail.com", "name": "John Doe", "phone": "555-1234"},
{"email": "john@gmail.com", "name": "J. Doe", "phone": "555-1234"},
{"email": "jane@gmail.com", "name": "Jane Smith"},
]
golden = resolver.resolve(records)
print(f"Input: {len(records)} records → Output: {len(golden)} golden records")
# Input: 3 records → Output: 2 golden records
Match Two Records
result = resolver.match(
{"email": "john@gmail.com", "name": "John"},
{"email": "john@gmail.com", "name": "Johnny"}
)
print(result.score) # 0.97
print(result.decision) # 'auto_merge'
print(result.matched_on) # ['email', 'name']
Find Matches in Existing Data
incoming = {"email": "john@gmail.com", "name": "John"}
existing = [
{"id": "1", "email": "john@gmail.com", "name": "John Doe"},
{"id": "2", "email": "jane@gmail.com", "name": "Jane Doe"},
]
matches = resolver.find_matches(incoming, existing)
# Returns best matches sorted by score
Custom Configuration
from c1v_id import IdentityResolver, ResolverConfig, Thresholds, Weights
config = ResolverConfig(
thresholds=Thresholds(auto_merge=0.95, needs_review=0.8),
weights=Weights(email=0.6, phone=0.3, name=0.1, address=0.0),
)
resolver = IdentityResolver(config=config)
Why c1v-id?
vs. Splink
| c1v-id | Splink | |
|---|---|---|
| Hello World | 10 lines | 50+ lines |
| Target | AI builders | Data analysts |
| Setup | pip install |
Spark/DuckDB config |
| ML Required | No | Optional |
| Use Case | Real-time matching | Batch analytics |
Splink is powerful for large-scale data linkage projects with dedicated analysts. c1v-id is for developers who need identity resolution as a feature, not a project.
vs. dedupe
| c1v-id | dedupe | |
|---|---|---|
| Maintenance | Active | Stale (2+ years) |
| Dependencies | 3 (pandas, rapidfuzz, pyyaml) | 10+ |
| Learning Curve | Minimal | Requires training data |
| API Style | resolve(records) |
Iterative labeling |
dedupe requires interactive labeling to train a model. c1v-id works out of the box with sensible defaults.
vs. Enterprise CDPs (Segment, mParticle)
| c1v-id | Enterprise CDP | |
|---|---|---|
| Cost | Free | $100K+/year |
| Data Location | Your infrastructure | Their cloud |
| Customization | Full control | Limited |
| Integration | Any Python app | Vendor lock-in |
Enterprise CDPs solve identity as part of a larger platform. c1v-id gives you just the identity resolution piece to embed anywhere.
Core Concepts
| Concept | What It Does | Why It Matters |
|---|---|---|
| Normalization | Cleans emails, phones, names | John.Doe+tag@Gmail.com → johndoe@gmail.com |
| Blocking | Groups likely matches | Reduces O(n²) to ~O(n) |
| Scoring | Calculates similarity | Weighted fuzzy matching across fields |
| Clustering | Groups transitive matches | If A≈B and B≈C, then A∈C |
| Golden Records | Merges duplicates | Best value wins per survivorship rules |
Low-Level API
For custom pipelines, use the building blocks directly:
Normalization
from c1v_id import norm_email, norm_phone, norm_name
norm_email("John.Doe+tag@Gmail.com") # 'johndoe@gmail.com'
norm_phone("(555) 123-4567") # '5551234567'
norm_name(" JOHN DOE ") # 'john doe'
Blocking
from c1v_id import email_domain_last4, phone_last7, make_blocks
email_domain_last4("john@gmail.com") # 'gmail.com|john'
phone_last7("555-123-4567") # '1234567'
blocks = make_blocks(df, ["email_domain_last4", "phone_last7"])
Clustering
from c1v_id import UnionFind
uf = UnionFind([1, 2, 3, 4, 5])
uf.union(1, 2)
uf.union(2, 3)
uf.find(1) == uf.find(3) # True (transitive)
uf.get_clusters() # {1: [1, 2, 3], 4: [4], 5: [5]}
Golden Records
from c1v_id import build_golden_records, SurvivorshipRule
rules = {
"email": SurvivorshipRule.MOST_RECENT,
"address": SurvivorshipRule.LONGEST,
"first": SurvivorshipRule.FIRST_NON_NULL,
}
golden = build_golden_records(df, clusters, rules, source_priority=["crm", "web"])
Use Cases
- AI Agents: Check if a customer exists before creating a new record
- CRM Deduplication: Merge duplicate contacts from multiple sources
- Lead Routing: Match incoming leads to existing opportunities
- Customer Support: Find customer context across fragmented records
- Data Migration: Deduplicate when merging systems
License
MIT
Metadata
Release files for c1v-id 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| c1v_id-0.1.0.tar.gz | 18.2 kB | With provenance | Details for c1v_id-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| c1v_id-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for c1v_id-0.1.0-py3-none-any.whl |
Total release size: 40.3 kB
Release files / c1v_id-0.1.0.tar.gz
| Download URL | c1v_id-0.1.0.tar.gz |
|---|---|
| Size | 18.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
080282e8f1c325cbecb0ac8647c25cbd7f59f6b2f9188a43520769b8b018accc
|
|
BLAKE2b-256 checksum How to use checksums |
ad704a644e6cae5fb66c5605c71986eddbc655a0364dfd3ed30ef0418282a713
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 24, 2026.
Transparency logRelease files / c1v_id-0.1.0-py3-none-any.whl
| Download URL | c1v_id-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d0ca779c292e550238482a39b8f2c3306cd8582f58c23999ce46a90fb8074fbf
|
|
BLAKE2b-256 checksum How to use checksums |
118dd14e900ff1a8d39708c28f5dd0438c85fd61356a0021b166abb84ff3a038
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 24, 2026.
Transparency logSecurity for c1v-id 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for c1v_id-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.