Data quality reference
Validation rule types
The data quality report aggregates the score across an entity type.
| Type | Constrains | Configuration |
|---|---|---|
| Regular expression | Format | A pattern |
| Range | Bounds | Minimum, maximum |
| Length | String length | Minimum, maximum |
| Lookup | Membership of a controlled list | The lookup type |
| Unique | Non-repetition within a scope | The scope |
| Cross-field | A condition on one attribute given another | A condition and a consequence |
| Custom | A named validator | The validator name and its parameters |
The catalog of custom validators is served by the platform. Query it rather than transcribing it — a list copied into a document drifts from the software.
What the shipped packs actually configure
Rules as they appear in the seed packs, so the shapes below are the ones the platform accepts:
{ "attribute": "lei", "ruleType": "regex",
"ruleConfig": { "pattern": "^[A-Z0-9]{18}[0-9]{2}$" },
"severity": "error", "errorMessageKey": "errors.LEI_FORMAT" }
{ "attribute": "iban", "ruleType": "regex",
"ruleConfig": { "pattern": "^[A-Z]{2}\\d{2}[A-Z0-9]{11,30}$" },
"severity": "error" }
{ "attribute": "npi", "ruleType": "regex",
"ruleConfig": { "pattern": "^[0-9]{10}$" },
"severity": "error" }
{ "attribute": "chassis", "ruleType": "regex",
"ruleConfig": { "pattern": "^[A-HJ-NPR-Z0-9]{17}$" },
"severity": "error" }
{ "attribute": "date_of_birth", "ruleType": "range",
"ruleConfig": { "min": "1900-01-01", "max": "today" },
"severity": "error" }
{ "attribute": "administrative_gender", "ruleType": "lookup",
"ruleConfig": { "lookupType": "administrative_genders" },
"severity": "error" }
{ "ruleType": "cross_field",
"ruleConfig": {
"if": { "attribute": "address.country", "op": "eq", "value": "US" },
"then": { "attribute": "address.state", "condition": "required" } },
"severity": "warning" }
Three of those repay a second look. The vehicle pattern excludes I, O and
Q because a VIN never contains them. max: "today" is a relative bound, not a
date you have to maintain. And the cross-field rule is structured rather than a
free-text expression, which is what lets the platform reject a malformed rule
when you save it instead of failing silently at evaluation time.
Two rules on one attribute
Rules compose. A US identifier gets a format rule and a rule that rejects known placeholder values:
{ "attribute": "ssn", "ruleType": "regex",
"ruleConfig": { "pattern": "^\\d{3}-?\\d{2}-?\\d{4}$" },
"severity": "error", "errorMessageKey": "errors.SSN_FORMAT" }
{ "attribute": "ssn", "ruleType": "regex",
"ruleConfig": { "pattern": "^(?!(\\d)\\1{8}$)\\d{3}-?\\d{2}-?\\d{4}$" },
"severity": "error", "errorMessageKey": "errors.SSN_PLACEHOLDER" }
The second refuses nine repeated digits — 111-11-1111, 000-00-0000. That
matters far more than it looks: a placeholder identifier that passes validation
becomes a shared identifier, and a shared identifier is what merges two
unrelated people into one record.
The platform registers named checksum validators — for the NPI Luhn digit, ABA
routing, the LEI ISO 17442 check, and IBAN MOD-97 — and the only one any shipped
pack configures is validate_calendar_date, which rejects impossible dates like
30 February.
So out of the box, the identifier rules above verify format, not checksum. A well-formed NPI with a wrong check digit passes. Wiring the checksum validators onto those attributes is a reasonable early hardening step, and it is a configuration change rather than a code one.

Validation rule fields
Every field a validation rule accepts.
| Field | Accepts | Default | Changeable | What it does |
|---|---|---|---|---|
entityTypeId | An entity type | — | No | Which type the rule applies to. |
attributeDefId | An attribute, or empty | — | No | Which attribute the rule constrains. Left empty for rules that are not about a single attribute — a cross-field rule spans two, so it binds to neither. |
ruleType | One of the seven types above | — | Yes | What kind of constraint this is. |
ruleConfig | An object | {} | Yes | The constraint's settings. The shape follows the type — see the examples above. |
severity | error, warning, info | error | Yes | Whether a violation blocks the write. See the warning below. |
errorMessageKey | Up to 255 chars | — | Yes | The message shown when the rule fails, as a translation key so it renders in the reader's language. |
sourceTypeFilter | Up to 100 chars | — | Yes | Restricts the rule to records from one source system. This is what lets a field be mandatory from one system and optional from another. |
isActive | true or false | true | Yes | Whether the rule is evaluated. Deactivating is the reversible alternative to deleting. |
metadata | An object | {} | Yes | Your own annotations, stored and returned uninterpreted. |
The type and attribute are fixed once created. To constrain a different attribute, create another rule.
severity defaults to error, and an error-severity violation refuses the
record. So a rule added without setting a severity will start rejecting
incoming data as soon as it is saved.
That default is the right one — a constraint you meant to enforce should
enforce — but if you are introducing a rule against data you have not yet
cleaned, save it as warning first. You then get the violation counts without
turning away records, and can promote it to error once the backlog is clear.
Scoping a rule to one source system
sourceTypeFilter is the field most often missed. Without it a rule applies to
every record of the type, whichever system it arrived from — which is rarely
what you want when one system is authoritative for a field and another does not
carry it at all.
With it, the same attribute can be mandatory from the system that owns it and absent from the rest, without either side reporting a false violation.
Severities
| Severity | Blocks the write | Recorded as a violation |
|---|---|---|
| Error | Yes | Yes |
| Warning | No | Yes |
| Info | No | Yes |
Severity governs whether the write proceeds. No severity changes the score — the score measures completeness, not rule outcomes. Violations are surfaced in their own right.
Cross-field rules
A cross-field rule expresses "if this, then that" across two attributes — for example, requiring a region when the country is one that has them.
Both halves are structured rather than free-form expressions, which is what allows the configuration to be validated when you save it rather than failing at evaluation time.
Scoring dimensions
| Dimension | Measures |
|---|---|
| Completeness | Filled attributes as a share of those expected |
| Freshness | Recency, decaying with age |
The overall score is completeness. Freshness is reported alongside it as a separate signal and is not folded into the overall number.
The score is not a weighted blend of several dimensions, and there is no per-tenant weighting to configure. Accuracy, consistency, and uniqueness are not scoring dimensions — validation results are recorded as violations, and duplication is the concern of matching rather than of this score.
Multi-value attribute violations
| Situation | Severity |
|---|---|
| A value carries no usage type where the attribute expects one | Warning — recorded, not blocking |
| A value carries a usage type that is not in the bound list | Error |
The asymmetry is deliberate. A missing usage type is incomplete data worth flagging; an unrecognized one is unambiguously wrong, and accepting it would silently discard the caller's intent.
Configuration validity
A rule whose configuration cannot be evaluated is rejected when saved. A rule that silently never fires is worse than no rule, because it looks like coverage.
Next
Last verified against commit a0765982 (2026-08-03)