> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Validate

A [contract](/preview/pr-749/docs/getting-started/flow/contract.md) is the canonical definition of what your events should look like. Validation enforces that contract at runtime with an explicit transformer step, `@walkeros/transformer-validate`, the same way enrichment or decoding is a transformer step. The contract describes the shape; the transformer applies it.

## Where event shapes live[​](#where-event-shapes-live "Direct link to Where event shapes live")

Event shapes live in the top-level `contract` block of flow\.json (a sibling of `flows`). Each named contract holds JSON Schemas under `events`, keyed by entity then action, with `extend` inheritance and `*` wildcards. Reference any contract from a step with `$contract.<name>`.

```
{

  "version": 4,

  "contract": {

    "web": { "events": { "order": { "complete": { "properties": { "data": { "required": ["total", "currency"] } } } } } }

  }

}
```

See [Contract](/preview/pr-749/docs/getting-started/flow/contract.md) for the full shape, inheritance with `extend`, and wildcard merging.

## Enforce at runtime[​](#enforce-at-runtime "Direct link to Enforce at runtime")

Add a `@walkeros/transformer-validate` step, point its `contract` setting at the contract, and reference the step from a destination's `before`, so it validates the full event after the collector. A `$contract` reference picks its per-event rules by the event's `entity` and `action`. The collector derives both from `name`, so in a source's `next` chain (before the collector) per-event rules only match when the source already sets `entity` and `action`.

```json
{
  "version": 4,
  "contract": {
    "web": { "events": { "order": { "complete": { "properties": { "data": { "required": ["total", "currency"] } } } } } }
  },
  "flows": {
    "default": {
      "config": { "platform": "web" },
      "transformers": {
        "validate": {
          "package": "@walkeros/transformer-validate",
          "config": { "settings": {
            "contract": ["$contract.web"],
            "format": true,
            "mode": "strict",
            "output": { "isValid": "source.valid", "errors": "validation" }
          } }
        }
      },
      "destinations": {
        "ga4": {
          "package": "@walkeros/web-destination-gtag",
          "config": { "settings": { "ga4": { "measurementId": "G-DEMO123456" } } },
          "before": "validate"
        }
      }
    }
  }
}
```

### Settings[​](#settings "Direct link to Settings")

| Field       | Type                                  | Description                                                                                                                                                                                               |
| ----------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contract?` | `Array<$contract ref \| JSON Schema>` | Constraints. A `$contract.<name>` ref selects per `entity.action`; an inline schema applies to the whole event. All entries must pass (AND).                                                              |
| `format?`   | `boolean`                             | Also validate the canonical `WalkerOS.Event` structure. All fields are optional, so this checks shape and field types, not presence. A top-level key that is not part of the event model fails the check. |
| `mode?`     | `'strict' \| 'pass'`                  | `strict` drops invalid events (stops the chain); `pass` (default) annotates and continues so a downstream route can act.                                                                                  |
| `output?`   | `{ isValid?, errors? }`               | Split targets. `isValid` (default `source.valid`) writes the boolean verdict onto the event; `errors` (default `validation`) writes the issue list onto the ingest context. Empty string skips a write.   |

The verdict travels with the event as analytics-grade data (`event.source.valid`). The issue list (`{ path, message, level }` entries) stays off the event, on the observer-visible ingest context, so it survives even a strict-mode drop.

## Filter unwanted events[​](#filter-unwanted-events "Direct link to Filter unwanted events")

Filtering is the same mechanism, not a separate setting. Author an inline schema that rejects the unwanted events and run `mode: "strict"`. For example, drop GTM lifecycle noise (`gtm.js`, `gtm.dom`) by rejecting any `name` matching `^gtm\.`. Reference this step from the source's `next`, so it runs before the collector:

```json
"validate": {
  "package": "@walkeros/transformer-validate",
  "config": { "settings": {
    "contract": [{ "type": "object", "properties": { "name": { "not": { "pattern": "^gtm\\." } } } }],
    "mode": "strict"
  } }
}
```

## Design-time checks[​](#design-time-checks "Direct link to Design-time checks")

`walkeros validate` checks contracts the way the runtime applies them. A contract binds only where a `@walkeros/transformer-validate` step links it, so the validator runs each validate step's own examples through that step's resolved `contract`, with that step's `mode`, `format` and `output` settings, and checks that each example's `out` agrees with the verdict: a valid event goes on, an invalid one is dropped in `strict` mode or goes on with its `isValid` flag set to `false` in `pass` mode.

```bash
walkeros validate flow.json
```

The top-level `contract` block is the reference for setting up mappings, `$contract` references and editor IntelliSense, but it binds nothing by itself. Examples of other steps are never checked against it, and a flow without a validate step gets no contract checks.

## Break it on purpose[​](#break-it-on-purpose "Direct link to Break it on purpose")

Contracts are easiest to trust after watching one fail. Take the contract above (it requires `total` and `currency` on `order complete` data), run the validate step with `mode: "strict"`, and give it an example whose `in` misses `currency` while its `out` still passes the event on. The validator names the exact path and what the step really does:

```bash
Validation Results:
  ⚠ flows.default.transformers.validate.examples.missingCurrency.out: Example out disagrees with the validate step: the event breaks the contract (#: Property "data" does not match schema.; #/data: Instance does not have required property "currency".), so with mode "strict" the step drops it (["return", false]).
    → Make the example out show what the validate step does with its settings.
  ✓ All checks passed

Summary: 0 error(s), 1 warning(s), 0 skipped
```

A disagreement is a warning by default; add `--strict` to turn it into an error (exit code 1; any other warning under `--strict` exits with code 2) and fail CI before a broken example ships. Fix it by making the example `out` show the drop (`[["return", false]]`), or by completing the `in` event.

## Next steps[​](#next-steps "Direct link to Next steps")

* **[Contract](/preview/pr-749/docs/getting-started/flow/contract.md)**: named, inheritable schemas referenced via `$contract`
* **[Validate transformer](/preview/pr-749/docs/transformers/validate.md)**: full settings reference and recipes
* **[Mapping](/preview/pr-749/docs/mapping.md)**: transform events between steps
