> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kodexa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Validation and Conditional Formatting

> Define validation rules and conditional formatting on Data Definition elements, using Kodexa formulas to evaluate business rules against document data.

Validation rules and conditional formats are configured on **data elements** in a Data Definition. In configuration, those elements are stored under `taxons`. At runtime Kodexa applies those rules to the matching data objects and data attributes in each document.

Use validation rules when the document data must satisfy a business rule. Use conditional formatting when reviewers need a visual cue while reviewing data. Both use the same formula language.

<Tip>
  You do not add `validationRules` or `conditionalFormats` directly to a stored data object or data attribute. You add them to the Data Definition element that models that object or attribute. Kodexa evaluates the rule wherever that element appears in document data.
</Tip>

## Where Rules Live

Rules can be attached at two levels:

| Level          | Attach to                                                               | Use for                                                                                              |
| -------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Data attribute | A field element such as `invoice_number`, `due_date`, or `total_amount` | Required fields, field-level ranges, confidence checks, and conditional formatting on a form control |
| Data object    | A group element such as `invoice`, `vendor`, or `line_items`            | Cross-field checks where the rule describes the whole object                                         |

Attribute-level rules are the most common. They bind cleanly to a field in the review UI and can create exceptions against a specific data attribute.

Object-level rules are useful when the business rule is about the row or section rather than a single field. For example, an `invoice` object can validate that `subtotal + tax_amount` matches `total_amount`.

```yaml Attribute-level rule theme={null}
taxons:
  - name: invoice
    group: true
    children:
      - name: invoice_number
        label: Invoice Number
        taxonType: STRING
        validationRules:
          - name: Invoice number required
            ruleFormula: "!isblank({invoice_number})"
            messageFormula: '"Invoice number is required"'
            exceptionId: INVOICE_NUMBER_REQUIRED
            overridable: false
```

```yaml Object-level rule theme={null}
taxons:
  - name: invoice
    group: true
    validationRules:
      - name: Invoice total matches components
        ruleFormula: |
          abs({total_amount} - (
            ifnull({subtotal}, 0) +
            ifnull({tax_amount}, 0) +
            ifnull({shipping_amount}, 0)
          )) < 0.01
        messageFormula: |
          concat(
            "Invoice total does not match the calculated total. Total: ",
            {total_amount}
          )
        exceptionId: INVOICE_TOTAL_MISMATCH
        overridable: true
    children:
      - name: subtotal
        taxonType: CURRENCY
      - name: tax_amount
        taxonType: CURRENCY
      - name: shipping_amount
        taxonType: CURRENCY
      - name: total_amount
        taxonType: CURRENCY
```

## Validation Rules

Validation rules are stored in the `validationRules` array on a taxon. Each rule evaluates a formula. If the formula returns truthy, the data passes. If it returns falsy or errors, Kodexa treats the rule as failed.

```yaml theme={null}
validationRules:
  - id: due-date-after-invoice-date
    name: Due date after invoice date
    description: The payment due date must not be earlier than the invoice date.
    disabled: false
    conditional: true
    conditionalFormula: "!isblank({due_date}) && !isblank({invoice_date})"
    ruleFormula: "isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date}"
    messageFormula: '"Due date must be on or after the invoice date"'
    detailFormula: |
      concat("Invoice date: ", {invoice_date}, ". Due date: ", {due_date}, ".")
    exceptionId: DUE_DATE_BEFORE_INVOICE_DATE
    supportArticleId: "9117988"
    overridable: false
```

### Rule Fields

| Field                | Type           | Required                              | Behavior                                                                                          |
| -------------------- | -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `id`                 | string         | No                                    | Stable rule identifier in metadata. Use one when configuration is managed in Git.                 |
| `name`               | string         | Recommended                           | Human-readable rule name shown in tooling and useful for derived exception keys.                  |
| `description`        | string         | No                                    | Explains why the rule exists.                                                                     |
| `disabled`           | boolean        | No                                    | When `true`, Kodexa skips the rule.                                                               |
| `conditional`        | boolean        | No                                    | When `true`, Kodexa evaluates `conditionalFormula` before the rule.                               |
| `conditionalFormula` | formula string | Required when `conditional` is `true` | If the formula is falsy or errors, Kodexa skips `ruleFormula`.                                    |
| `ruleFormula`        | formula string | Yes                                   | Must evaluate truthy for the data to pass. Empty formulas are skipped.                            |
| `messageFormula`     | formula string | Recommended                           | Builds the message for the exception when the rule fails.                                         |
| `detailFormula`      | formula string | No                                    | Builds additional details for the exception.                                                      |
| `exceptionId`        | string         | Recommended                           | Stable exception type. If omitted, Kodexa derives a key from the taxon path and rule name.        |
| `supportArticleId`   | string         | No                                    | Help article ID attached to the resulting exception.                                              |
| `overridable`        | boolean        | No                                    | Controls whether a reviewer can override the exception. Defaults to not overridable when omitted. |

### Evaluation Flow

1. Kodexa finds the taxon for the changed data object or data attribute.
2. Disabled rules are skipped.
3. If `conditional` is `true` and `conditionalFormula` is present, Kodexa evaluates the condition.
4. If the condition is truthy, Kodexa evaluates `ruleFormula`.
5. Truthy results pass. Falsy results fail.
6. Formula errors are treated as failed validation and marked as evaluation errors so authors can diagnose bad rules.
7. Failing rules create or reopen data exceptions. Passing rules close matching open exceptions.

<Note>
  Validation formulas are reactive. When a formula references `{subtotal}`, `{tax_amount}`, or another attribute, Kodexa tracks that dependency and re-evaluates the affected rule when the referenced value changes.
</Note>

### Required Fields

Use `isblank` for required field checks. Missing references resolve to `null`, so `isblank` handles missing, null, empty-string, and empty-list values.

```yaml theme={null}
validationRules:
  - name: Vendor name required
    ruleFormula: "!isblank({vendor_name})"
    messageFormula: '"Vendor name is required"'
    exceptionId: VENDOR_NAME_REQUIRED
    overridable: false
```

### Conditional Rules

Set `conditional: true` when a rule should apply only for some records.

```yaml theme={null}
validationRules:
  - name: PO number required for purchase-order invoices
    conditional: true
    conditionalFormula: "{invoice_type} = 'Purchase Order'"
    ruleFormula: "!isblank({purchase_order_number})"
    messageFormula: '"Purchase order number is required for PO invoices"'
    exceptionId: PO_NUMBER_REQUIRED
    overridable: false
```

If `conditional` is omitted or false, Kodexa evaluates `ruleFormula` regardless of `conditionalFormula`.

### Cross-Field Rules

Attach cross-field rules to the field that should receive the exception, or to the group data element when the rule describes the full object.

```yaml theme={null}
validationRules:
  - name: Line total matches quantity times unit price
    conditional: true
    conditionalFormula: |
      !isblank({quantity}) && !isblank({unit_price}) && !isblank({line_total})
    ruleFormula: "abs({line_total} - ({quantity} * {unit_price})) < 0.01"
    messageFormula: |
      concat("Line total should be ", {quantity} * {unit_price}, " but was ", {line_total})
    exceptionId: LINE_TOTAL_MISMATCH
    overridable: true
```

### Repeating Groups

When a formula references a child path that has multiple data objects, Kodexa returns an array of values. Math functions such as `sum`, `average`, `min`, and `max` can work across those arrays.

```yaml theme={null}
validationRules:
  - name: Invoice total equals line item total
    ruleFormula: "abs({total_amount} - sum({line_items/line_total})) < 0.01"
    messageFormula: |
      concat(
        "Invoice total ",
        {total_amount},
        " does not match line item total ",
        sum({line_items/line_total})
      )
    exceptionId: INVOICE_LINES_DO_NOT_SUM
    overridable: true
```

### Formula Errors

Formula errors are configuration problems. For example, an unknown function, invalid syntax, or unsupported comparison can cause the rule to fail with `evaluationErrored`.

```yaml Avoid theme={null}
ruleFormula: "NOT_EMPTY({invoice_number})"
```

```yaml Use theme={null}
ruleFormula: "!isblank({invoice_number})"
```

<Warning>
  The formula language is not SQL, JavaScript, or Excel. Use `&&` and `||` for logic, `=` for equality, `!` for not, and built-in functions such as `isblank`, `ifnull`, `datemath`, and `contains`.
</Warning>

## Conditional Formatting

Conditional formats are stored in the `conditionalFormats` array on a taxon. Each format has:

| Field        | Type           | Required | Behavior                                                  |
| ------------ | -------------- | -------- | --------------------------------------------------------- |
| `type`       | string         | Yes      | Formatter type consumed by the review UI.                 |
| `condition`  | formula string | Yes      | Formula that activates the format when truthy.            |
| `properties` | object         | No       | Formatter-specific properties, usually `color` or `icon`. |

```yaml theme={null}
conditionalFormats:
  - type: backgroundColor
    condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'"
    properties:
      color: "#FEE2E2"

  - type: textColor
    condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'"
    properties:
      color: "#991B1B"

  - type: icon
    condition: "{total_amount} > 10000"
    properties:
      icon: alert-circle
      color: "#D97706"
```

### Supported Formatter Types

| Type              | Properties      | Result                                                  |
| ----------------- | --------------- | ------------------------------------------------------- |
| `backgroundColor` | `color`         | Applies a background color to the attribute control.    |
| `textColor`       | `color`         | Applies a text color to the attribute control.          |
| `outlineColor`    | `color`         | Applies a colored outline around the attribute control. |
| `icon`            | `icon`, `color` | Displays a review icon for the attribute.               |

Conditional formatting is attribute-oriented. Add `conditionalFormats` to the field taxon whose control should be styled.

```yaml Attribute styling theme={null}
taxons:
  - name: invoice
    group: true
    children:
      - name: total_amount
        label: Total Amount
        taxonType: CURRENCY
        conditionalFormats:
          - type: backgroundColor
            condition: "{total_amount} > 10000"
            properties:
              color: "#FEF3C7"
          - type: icon
            condition: "{total_amount} > 10000"
            properties:
              icon: alert-circle-outline
              color: "#92400E"
```

<Note>
  Older examples may use `formula`, `backgroundColor`, `textColor`, or `icon` at the top level of a conditional format. The current Kodexa document runtime evaluates `condition` and returns active `{ type, properties }` records, so new configuration should use the canonical shape shown above.
</Note>

### How Conditional Formats Recalculate

Kodexa builds a dependency graph from every `condition`. When any referenced value changes, the affected conditional formats are evaluated for that data object.

* A truthy condition activates the format.
* A falsy condition deactivates the format.
* An empty `condition` is skipped.
* A condition that errors is treated as inactive.
* Kodexa caches active/inactive state and emits conditional format change events only when the state changes.

## Formula Language

Validation rules and conditional formats use Kodexa's KEXL formula language. The same syntax is used by formula data elements and other Data Definition features.

### Literals

```typescript theme={null}
123
123.45
"Invoice total is required"
'PAID'
true
false
null
[1, 2, 3]
```

Strings can be single-quoted or double-quoted. Use backslash escapes such as `\"`, `\'`, `\n`, and `\t`, or double the quote character inside the same quote style.

### Attribute References

Use curly braces to read values from the current data object or related data objects.

```typescript theme={null}
{invoice_number}              // Attribute on the current data object
{line_items/line_total}       // Child data object path, often returns an array
{../invoice_number}           // Attribute on the parent data object
{../../customer_name}         // Attribute two levels up
{./status}                    // Explicit self-relative attribute reference
```

References are case-insensitive when matching existing attributes. The formula service also understands data element `externalName` mappings, so generated data can use external names without changing the underlying element names.

### Operators

| Operator             | Use                         |
| -------------------- | --------------------------- |
| `+`, `-`, `*`, `/`   | Arithmetic                  |
| `^`                  | Power                       |
| `&`                  | String concatenation        |
| `=`                  | Equality                    |
| `!=`                 | Inequality                  |
| `<`, `<=`, `>`, `>=` | Ordered comparison          |
| `&&`                 | Logical and, short-circuits |
| `\|\|`               | Logical or, short-circuits  |
| `!`                  | Logical not                 |
| `[]`                 | Array index access          |
| `()`                 | Grouping                    |

```typescript theme={null}
({subtotal} + {tax_amount}) = {total_amount}
!isblank({invoice_number}) && {total_amount} > 0
concat("Invoice ", {invoice_number}) & " is ready"
```

<Warning>
  Use `=` for equality. Do not use `==`. Use `&&` and `||`; uppercase `AND` and `OR` are not formula operators.
</Warning>

### Truthy and Falsy Values

Kodexa uses truthiness when deciding whether a validation passes or a conditional format is active.

| Value            | Truthiness |
| ---------------- | ---------- |
| `true`           | truthy     |
| `false`          | falsy      |
| Non-zero number  | truthy     |
| Zero             | falsy      |
| Non-empty string | truthy     |
| Empty string     | falsy      |
| Non-empty array  | truthy     |
| Empty array      | falsy      |
| `null`           | falsy      |

### Function Reference

Function names are case-insensitive, but examples should use lower-case names for consistency.

| Function            | Example                                                              | Notes                                                                         |
| ------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `sum`               | `sum({line_items/line_total})`                                       | Adds numeric values. Arrays are summed.                                       |
| `average`           | `average({scores/value})`                                            | Mean of numeric values. Returns zero when there are no numeric values.        |
| `min`               | `min({line_items/amount})`                                           | Lowest numeric value.                                                         |
| `max`               | `max({line_items/amount})`                                           | Highest numeric value.                                                        |
| `abs`               | `abs({expected} - {actual})`                                         | Absolute value. Arrays are summed first.                                      |
| `ceil`              | `ceil({amount})`                                                     | Round toward positive infinity.                                               |
| `floor`             | `floor({amount})`                                                    | Round toward negative infinity.                                               |
| `round`             | `round({amount})`                                                    | Round to nearest integer.                                                     |
| `stddeviation`      | `stddeviation(1, 2, 3)`                                              | Standard deviation.                                                           |
| `decimalplaces`     | `decimalplaces({amount}, 2, "round_half_up")`                        | Decimal rounding. Default mode is floor.                                      |
| `concat`            | `concat("PO ", {purchase_order_number})`                             | Converts arguments to strings and joins them.                                 |
| `substring`         | `substring({code}, 0, 3)`                                            | String slice using start and end indexes.                                     |
| `lowercase`         | `lowercase({vendor_name})`                                           | Returns lower-case string values.                                             |
| `uppercase`         | `uppercase({vendor_name})`                                           | Returns upper-case string values.                                             |
| `trim`              | `trim({vendor_name})`                                                | Trims whitespace.                                                             |
| `replace`           | `replace({phone}, "-", "")`                                          | Replaces all occurrences.                                                     |
| `split`             | `split({csv_value}, ",")`                                            | Returns an array.                                                             |
| `strlen`            | `strlen({invoice_number})`                                           | Length of string value.                                                       |
| `length`            | `length({line_items/line_total})`                                    | Length of string, array, map, or object representation.                       |
| `len`               | `len({line_items/line_total})`                                       | Alias for `length`.                                                           |
| `if`                | `if({amount} > 0, "debit", "credit")`                                | Conditional result. Arguments are evaluated before the function runs.         |
| `ifnull`            | `ifnull({tax_amount}, 0)`                                            | Returns fallback for `null` or empty arrays.                                  |
| `sumifs`            | `sumifs({amounts}, {types}, "freight")`                              | Sums values where criteria match.                                             |
| `countifs`          | `countifs({statuses}, {statuses}, "open")`                           | Counts values where criteria match.                                           |
| `isnull`            | `isnull({field})`                                                    | True for null or empty arrays.                                                |
| `isblank`           | `isblank({field})`                                                   | True for null, empty arrays, or whitespace-only strings.                      |
| `contains`          | `contains({description}, "freight")`                                 | Substring check.                                                              |
| `startswith`        | `startswith({code}, "INV")`                                          | Prefix check.                                                                 |
| `endswith`          | `endswith({code}, "-US")`                                            | Suffix check.                                                                 |
| `datemath`          | `datemath({invoice_date}, "days", 30)`                               | Adds days, weeks, months, or years. `datemath("today")` returns today's date. |
| `isdate`            | `isdate({invoice_date})`                                             | True when the value can be parsed as a date.                                  |
| `isbeforedate`      | `isbeforedate({invoice_date}, {due_date})`                           | Date comparison, ignoring time.                                               |
| `isafterdate`       | `isafterdate({due_date}, {invoice_date})`                            | Date comparison, ignoring time.                                               |
| `daysbetween`       | `daysbetween({invoice_date}, {due_date})`                            | Full days between two dates.                                                  |
| `weeksbetween`      | `weeksbetween({start_date}, {end_date})`                             | Full weeks between two dates.                                                 |
| `monthsbetween`     | `monthsbetween({start_date}, {end_date})`                            | Month difference between two dates.                                           |
| `validatedate`      | `validatedate({invoice_date})`                                       | True when the date parses.                                                    |
| `formatdate`        | `formatdate({invoice_date}, "yyyy-MM-dd")`                           | Formats a date using Java date-pattern syntax.                                |
| `regex`             | `regex({invoice_number}, "^[A-Z0-9-]+$")`                            | Go regular expression match.                                                  |
| `matches`           | `matches({invoice_number}, "^[A-Z0-9-]+$")`                          | Alias for `regex`.                                                            |
| `confidence`        | `confidence("invoice_number")`                                       | Reads confidence for an attribute on the current data object.                 |
| `count`             | `count(1, 2, 3)`                                                     | Counts function arguments.                                                    |
| `selectioncontains` | `selectioncontains({available_codes}, {code})`                       | Checks string values or `{ label, value }` option objects.                    |
| `getvalue`          | `getvalue("../invoice_number")`                                      | Resolves a path supplied as a string.                                         |
| `servicebridgecall` | `servicebridgecall("VendorLookup", "lookup", "name", {vendor_name})` | Calls a configured Service Bridge when available in the runtime.              |

### Eager and Short-Circuit Evaluation

Function arguments are evaluated before the function runs. That means `if` does not protect an invalid branch from being evaluated.

```typescript Avoid theme={null}
if({denominator} = 0, 0, {numerator} / {denominator})
```

Use logical operators when you need short-circuit behavior.

```typescript Use theme={null}
{denominator} != 0 && ({numerator} / {denominator}) > 10
```

### Common Formula Patterns

```typescript Required value theme={null}
!isblank({invoice_number})
```

```typescript Optional date ordering theme={null}
isblank({due_date}) || isblank({invoice_date}) || isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date}
```

```typescript Numeric tolerance theme={null}
abs({expected_total} - {actual_total}) < 0.01
```

```typescript Date window theme={null}
!isblank({due_date}) &&
isafterdate({due_date}, datemath("today", "days", -1)) &&
isbeforedate({due_date}, datemath("today", "days", 31))
```

```typescript Selection-like text theme={null}
{status} = "Approved" || {status} = "Approved with Conditions"
```

```typescript Dynamic path theme={null}
getvalue("../" & {field_name}) = "Complete"
```

## Recommended Authoring Pattern

1. Put field-level validation and formatting on the field taxon.
2. Put object-level validation on the group data element.
3. Use stable `exceptionId` values for customer-facing exceptions.
4. Use `conditional: true` only when the rule has a real gating condition.
5. Write formulas with explicit braces around every data reference.
6. Prefer `isblank`, `ifnull`, `abs`, `datemath`, and date comparison functions over hand-rolled string checks.
7. Keep conditional formatting visual and non-blocking; use validation rules for business conditions that must be resolved.

## Related

<CardGroup cols={2}>
  <Card title="Data Definitions Guide" icon="sitemap" href="/guides/data-definitions/index">
    Start with the overall data definition model.
  </Card>

  <Card title="Event-Based Scripting" icon="bolt" href="/guides/data-definitions/event-subscriptions">
    Use scripts when a rule needs side effects or procedural logic.
  </Card>

  <Card title="Selection Option Formulas" icon="list-dropdown" href="/guides/data-definitions/selection-option-formulas">
    Compute dropdown options dynamically from document context.
  </Card>

  <Card title="Formula Reference" icon="function" href="/guides/formulas/index">
    See the broader formula guide and examples.
  </Card>
</CardGroup>
