Skip to main content

Validate

A contract 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

Event shapes live in the top-level contract block of flow.json (a sibling of flows), keyed by entity then action as JSON Schema, 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 for the full shape, inheritance with extend, and wildcard merging.

Enforce at runtime

Add a @walkeros/transformer-validate step and point its contract setting at the contract. The transformer reads the { ingest, event } context and validates the canonical event.

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

Settings

FieldTypeDescription
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?booleanAlso validate the canonical WalkerOS.Event structure. All fields are optional, so this checks shape and field types, not presence.
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 } entries) stays off the event, on the observer-visible ingest context, so it survives even a strict-mode drop.

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\.:

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

Design-time checks

The CLI flow validator checks step examples (destination.in, transformer.in) against the resolved contract and reports violations before you deploy, so a sample event that breaks the contract is caught at authoring time.

walkeros validate flow.json

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) and give a step example an event that misses currency. The validator names the exact path and problem:

Validation Results:
 destination.ga4.examples.purchase.in: Example violates contract: #/data: Instance does not have required property "currency".
 Fix the example data to satisfy the contract schema.

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

Contract violations are warnings by default; add --strict to turn them into errors (exit code 1) and fail CI before a broken event shape ships. A tag manager drops the same broken field silently, in production.

Next steps

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)