Skip to main content

Data quality reference

Validation rule types

The data quality report The data quality report aggregates the score across an entity type.

TypeConstrainsConfiguration
Regular expressionFormatA pattern
RangeBoundsMinimum, maximum
LengthString lengthMinimum, maximum
LookupMembership of a controlled listThe lookup type
UniqueNon-repetition within a scopeThe scope
Cross-fieldA condition on one attribute given anotherA condition and a consequence
CustomA named validatorThe 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.

Checksum validators exist, but no shipped pack uses one

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.

The validation rule editor, with the rule list and its test panel.

Validation rule fields

Every field a validation rule accepts.

FieldAcceptsDefaultChangeableWhat it does
entityTypeIdAn entity typeNoWhich type the rule applies to.
attributeDefIdAn attribute, or emptyNoWhich 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.
ruleTypeOne of the seven types aboveYesWhat kind of constraint this is.
ruleConfigAn object{}YesThe constraint's settings. The shape follows the type — see the examples above.
severityerror, warning, infoerrorYesWhether a violation blocks the write. See the warning below.
errorMessageKeyUp to 255 charsYesThe message shown when the rule fails, as a translation key so it renders in the reader's language.
sourceTypeFilterUp to 100 charsYesRestricts the rule to records from one source system. This is what lets a field be mandatory from one system and optional from another.
isActivetrue or falsetrueYesWhether the rule is evaluated. Deactivating is the reversible alternative to deleting.
metadataAn object{}YesYour own annotations, stored and returned uninterpreted.

The type and attribute are fixed once created. To constrain a different attribute, create another rule.

A new rule blocks writes unless you say otherwise

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

SeverityBlocks the writeRecorded as a violation
ErrorYesYes
WarningNoYes
InfoNoYes

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

DimensionMeasures
CompletenessFilled attributes as a share of those expected
FreshnessRecency, 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.

Not a weighted composite

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

SituationSeverity
A value carries no usage type where the attribute expects oneWarning — recorded, not blocking
A value carries a usage type that is not in the bound listError

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)