Schema Reference
Complete reference for every top-level key in a Kanoniv spec.
Required Structure
Every spec must contain at minimum:
api_version: kanoniv/v2
identity_version: "1.0"
entity:
name: customer
sources:
crm:
adapter: csv
location: data/contacts.csv
primary_key: id
rules:
- name: email_exact
type: exact
field: email
weight: 1.0
decision:
thresholds:
match: 0.9Top-Level Keys
| Key | Type | Required | Default | Tier | Description |
|---|---|---|---|---|---|
api_version | string | Yes | — | Local | Must match kanoniv/v<N> (currently kanoniv/v1) |
identity_version | string | Yes | — | Local | Semantic version for this spec (e.g. "1.0", "2.3.1") |
entity | object | Yes | — | Local | Entity type configuration |
sources | object | array | Yes | — | Local | Data source definitions (max 10); accepts map-style or array-style |
rules | array | Yes | — | Local | Matching rule definitions (max 50) |
decision | object | Yes | — | Local | Scoring and threshold configuration |
survivorship | object | No | { default: most_recent } | Local | Field survivorship strategy |
blocking | object | No | { strategy: composite, keys: [] } | Local | Candidate pair blocking |
metadata | object | No | — | Local | Owner, tags, description |
governance | object | No | — | Cloud | Data governance policies |
exclusions | array | No | [] | Cloud | Record exclusion rules |
relations | array | No | [] | Cloud | Cross-entity relation definitions |
reference_identifiers | array | No | [] | Cloud | External reference ID mappings |
Nested sub-keys (not top-level):
| Key | Nested Under | Tier | Description |
|---|---|---|---|
compliance | entity | Cloud | Compliance frameworks, PII fields, retention |
hierarchy | entity | Cloud | Entity hierarchy configuration |
audit | decision | Cloud | Audit logging configuration |
Entity
The identity you're resolving. The entity name defines what kind of real-world thing your spec describes — a customer, patient, product, or vendor. This name appears in API responses, audit logs, SDK results, and everywhere the system refers to your resolved identities.
entity:
name: customer # Required: entity type name
description: "B2B customers across all systems" # Optional| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Entity type identifier (alphanumeric + underscore) |
description | string | No | Human-readable description |
See Entity for compliance and hierarchy sub-blocks.
Sources
Where your data lives. Each source points to a specific data store — a CSV file, a PostgreSQL table, a Snowflake view, a dbt model — and tells the engine how to read records from it. The engine pulls records from every source, normalizes them, and feeds them into the matching pipeline. You can define up to 10 sources per spec.
sources:
crm:
adapter: csv
location: data/contacts.csv
primary_key: id
attributes:
name: full_name
email: email_address
phone: phone_number
freshness:
max_age_hours: 24
schema:
email: { type: string, pii: true }
phone: { type: string, pii: true }
name: { type: string }| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique source identifier (the YAML map key) |
adapter | string | Yes | Adapter type: csv, pandas, postgres, snowflake, bigquery, redshift, dbt, file, webhook, api |
location | string | Yes | Adapter-specific location (file path, table name, connection string) |
primary_key | string | Yes | Column used as unique record identifier |
attributes | map[string, string] | No | Canonical-to-source field name mapping (default: all columns, identity mapped) |
freshness | object | No | Freshness constraints (Cloud tier) |
schema | object | No | Column type/format validation |
tags | array[string] | No | Source tags for filtering |
sampling | object | No | Row sampling configuration |
See Sources for detailed adapter configuration.
Rules
How records are compared. Rules encode your domain knowledge about what makes two records the same entity. Each rule specifies a comparison type (exact, similarity, range, composite, or ML), the fields to compare, and a weight that reflects how strongly a match on that rule indicates a true identity match. The engine evaluates every rule against every candidate pair and aggregates the scores.
rules:
- name: email_exact
type: exact
field: email
weight: 1.0
- name: name_fuzzy
type: similarity
field: name
algorithm: jaro_winkler
threshold: 0.88
weight: 0.6| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique rule identifier |
type | string | Yes | exact, similarity, range, composite, ml |
field | string | Yes | Field this rule compares |
weight | float | No | Score contribution (default: 1.0) |
algorithm | string | Conditional | Required for similarity type |
threshold | float | Conditional | Required for similarity and range types |
children | array | Conditional | Sub-rules for composite type |
operator | string | Conditional | and / or for composite type |
normalizer | string | No | Pre-match normalizer: email, phone, name, nickname, domain, generic (applies to similarity and range; for exact, set inside options; also supported on Fellegi-Sunter fields) |
See Rules for algorithm details and examples.
Decision
How scores become outcomes. After rules produce a weighted score for each candidate pair, the decision block determines what happens next. Pairs above the match threshold are automatically merged. Pairs between review and match go to the review queue for human verification. Pairs below review are rejected. The decision block also controls how conflicts are resolved when one record matches multiple candidates.
decision:
scoring:
method: weighted_sum
thresholds:
match: 0.9
review: 0.7
reject: 0.3
conflict_strategy: prefer_high_confidence
review_queue:
enabled: true
webhook: "https://hooks.example.com/review"
sla_hours: 48
audit:
log_all_comparisons: false
log_decisions: true
log_conflicts: true| Field | Type | Required | Default | Description |
|---|---|---|---|---|
scoring | object | No | { method: weighted_sum } | Scoring configuration (see below) |
scoring.method | string | No | weighted_sum | Scoring method: weighted_sum, ml_ensemble, custom |
thresholds.match | float | Yes | — | Minimum score for automatic match |
thresholds.review | float | No | — | Minimum score for manual review |
thresholds.reject | float | No | — | Below this score, pairs are rejected |
thresholds.merge | float | No | match * 1.5 | Real-time entity merge threshold (used by POST /v1/resolve/realtime) |
conflict_strategy | string | No | prefer_high_confidence | How to resolve conflicting matches: prefer_high_confidence, prefer_recent, manual_review |
review_queue | object | No | — | Review queue settings |
audit | object | No | — | Audit logging configuration (Cloud tier) |
See Decision for scoring math and examples.
Survivorship
Which value wins. When multiple sources describe the same entity, survivorship determines which source's value makes it into the golden record for each field. You set a global strategy — trust CRM first, use the most recent update, or pick the most complete record — and override it per field where a different source is authoritative.
survivorship:
default: source_priority
overrides:
- field: email
strategy: most_recent
- field: phone
strategy: most_complete
- field: name
strategy: source_priority
priority: [crm, billing, support]| Field | Type | Required | Default | Description |
|---|---|---|---|---|
default | string | No | most_recent | Global strategy: source_priority, most_recent, most_complete, aggregate, custom |
overrides | array | No | [] | Per-field strategy overrides |
overrides[].field | string | Yes | — | Field name to override |
overrides[].strategy | string | Yes | — | Strategy for this field |
overrides[].priority | array[string] | No | [] | Source priority order (for source_priority) |
overrides[].function | string | No | — | Aggregate function: max, min, sum, avg, union, intersection |
See Survivorship for strategy details and examples.
Blocking
How the engine reduces comparison space. Without blocking, every record is compared against every other record — O(n²) comparisons. Blocking groups records into buckets by shared field values (exact match, phonetic encoding, or n-gram overlap) and only compares records within the same bucket. This makes reconciliation feasible on large datasets.
blocking:
strategy: composite
keys:
- [email]
- [last_name, zip_code]
fallback:
enabled: true
sample_rate: 0.01| Field | Type | Required | Default | Description |
|---|---|---|---|---|
strategy | string | No | composite | Blocking strategy: single, composite, lsh, ml_predicted |
keys | array[array[string]] | No | [] | Blocking key groups (each group is a list of fields) |
fallback | object | No | — | Fallback configuration for non-blocked pairs |
fallback.enabled | bool | No | false | Enable fallback sampling |
fallback.sample_rate | float | No | — | Sample rate for fallback pairs |
Compliance
Regulatory guardrails for sensitive data. Compliance settings declare which frameworks apply (GDPR, HIPAA, SOC2, CCPA), which fields contain PII, how long records are retained, and whether audit logging is required. These settings flow through the entire pipeline — PII fields are automatically masked in non-admin API responses, and the retention policy triggers automatic cleanup.
Compliance is nested under entity, not a top-level key.
entity:
name: customer
compliance: # Cloud tier
frameworks: [GDPR, HIPAA]
pii_fields: [email, phone, ssn, date_of_birth]
audit_required: true
retention_days: 365See Entity for full compliance reference.
Hierarchy
Parent-child relationships between entities. Hierarchies model organizational structures — a parent company with regional subsidiaries, a hospital system with individual clinics, a franchise with locations. Field values can flow from parent to child through inheritance rules, ensuring consistency across the tree without duplicating data in every record.
Hierarchy is nested under entity, not a top-level key.
entity:
name: customer
hierarchy: # Cloud tier
parent_field: parent_company_id
depth: 3
inheritance: [industry, region]| Field | Type | Required | Default | Description |
|---|---|---|---|---|
parent_field | string | Yes | — | Field containing parent reference |
depth | integer | No | 5 | Maximum hierarchy depth |
inheritance | array[string] | No | [] | Fields inherited down the hierarchy |
See Entity for hierarchy configuration.
Audit
What gets logged about every decision. Audit configuration controls whether the engine records match/review/reject decisions, the per-rule scores that produced each decision, and optionally the raw field values involved. Essential for compliance reporting, debugging false positives, and understanding why any two records were or weren't merged.
Audit is nested under decision, not a top-level key.
decision:
thresholds:
match: 0.9
audit: # Cloud tier
log_all_comparisons: false
log_decisions: true
log_conflicts: true| Field | Type | Required | Default | Description |
|---|---|---|---|---|
log_all_comparisons | bool | No | false | Log every pairwise comparison, including NoMerge (non-match) decisions |
log_decisions | bool | No | true | Log Merge and Review decisions (enabled by default) |
log_conflicts | bool | No | false | Log survivorship conflict resolution outcomes |
When no audit block is present, log_decisions defaults to true so Merge and Review decisions are audited automatically. When compliance.audit_required is true, all decisions are force-audited regardless of these flags.
Metadata
Informational context about the spec itself. Metadata has no effect on reconciliation — it exists for organizational purposes. Track who owns the spec, which environment it targets, when it was last updated, and what it does. Metadata is returned in API responses and included in audit logs, making it easy to trace a decision back to the spec version that produced it.
metadata:
owner: [email protected]
tags: [production, customer, v2]
description: "Production customer identity spec"
created: "2026-01-15"Governance
Policies that enforce data quality and change management. Governance rules make certain best practices mandatory: every source must declare freshness constraints, every source must declare a schema for field-presence drift detection, and sources must carry required tags. When require_schema is enabled and drift is detected at reconciliation time, the pipeline aborts. These policies are checked during validation and enforced in CI/CD.
governance: # Cloud tier
require_freshness: true
require_schema: true
required_tags: [production]See Governance for policy configuration.
Exclusions
Records to filter out before matching begins. Exclusions remove test accounts, demo data, invalid records, and any other noise from the reconciliation pipeline. Excluded records never enter candidate pair generation, which improves both data quality and performance. Each exclusion targets a specific field and matches by regex pattern or an exact values list.
exclusions: # Cloud tier
- field: email
pattern: ".*@test\\.com$"
reason: "Test accounts"
- field: name
values: ["Test User", "Demo Account"]
reason: "Known test records"Relations
Connections between different entity types. Relations don't affect how individual entities are resolved — they define how resolved entities relate to each other across specs. A customer belongs to a company, a company has invoices, an employee is part of a department. After reconciliation, these relationships enable cross-entity queries and graph-based analysis.
relations: # Cloud tier
- name: company_contacts
from_entity: customer
to_entity: company
join_key: company_id
cardinality: many_to_one
propagate_match: falseReference Identifiers
Links to external identifier systems. Reference identifiers map your canonical records to well-known registries — DUNS numbers, LEI codes, tax IDs, VAT numbers. They serve two purposes: cross-referencing (look up a canonical entity by its DUNS number) and deterministic matching (two records sharing the same reference identifier value are immediately merged with confidence 1.0, short-circuiting all other matching rules).
reference_identifiers: # Cloud tier
- name: duns_number
source: dnb
field: duns
- name: lei_code
source: gleif
field: leiNote: The match_weight field is accepted in the spec for forward compatibility but does not currently affect scoring. All reference ID matches produce confidence 1.0. See Governance - Reference Identifiers for full details.
Validation Limits
| Constraint | Limit |
|---|---|
| Max sources | 10 |
| Max rules | 50 |
| Max blocking keys | 5 |
| Rule weight range | 0.0 - 1.0 |
| Threshold range | 0.0 - 1.0 |
| Max hierarchy depth | 10 |
| Max exclusion patterns | 100 |
