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

# Contract

A contract is the schema for your event data: which fields are required, what types they have, what values are allowed. It lives alongside `flows` in flow\.json as a named, inheritable block that any flow can reference via `$contract.<name>.<path>`.

```
{

  "contract": {

    "default": {

      "tagging": 1,

      "schema": {

        "type": "object",

        "properties": {

          "globals": { "required": ["country"] }

        }

      },

      "events": {

        "product": {

          "*": { "properties": { "data": { "required": ["id", "name"] } } },

          "add": { "properties": { "data": { "required": ["quantity"] } } }

        }

      }

    }

  }

}
```

## Why use a contract[​](#why-use-a-contract "Direct link to Why use a contract")

A contract is a single, inheritable description of what your events should look like. The contract itself describes; enforcement is an explicit [`@walkeros/transformer-validate`](/preview/pr-749/docs/transformers/validate.md) step that references it. Tools and humans also read the contract for governance, documentation, and schema-driven workflows.

* **Single source of truth.** Define `product add` requirements once. Every flow that ships these events references the same definition.
* **Inheritance.** Layer additional rules on top with `extend` (for example, `web_loggedin` extend `web` extend `default`) rather than copying.
* **Versioned.** `tagging` tracks contract revisions alongside the events they govern.
* **Self-documenting.** Each schema is JSON Schema, so `description` and `examples` annotate fields the same way humans and tools read them.
* **Decoupled from enforcement.** The validate transformer enforces the contract wherever you place it in the pipeline.
* **Composable with the rest of the config.** Reference fragments anywhere via `$contract.<name>.<path>` (see [Reference syntax](/preview/pr-749/docs/guides/reference-syntax.md#contract--contract-references)).

If your flow has a single throwaway event, you do not need a contract. Reach for one as soon as the same shape needs to hold across more than one flow.

## The shape[​](#the-shape "Direct link to The shape")

`contract` is a top-level key on flow\.json (parallel to `flows`, not nested inside a flow). Each entry is a named contract. The full shape:

| Field          | Purpose                                             |
| -------------- | --------------------------------------------------- |
| `extend?`      | Inherit from another named contract                 |
| `tagging?`     | Integer revision marker                             |
| `description?` | Human-readable note                                 |
| `events?`      | Entity-action keyed JSON Schemas, applied per event |
| `schema?`      | A single JSON Schema applied to every event         |

The `default` contract of [`flow-complete.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.json), the tested example flow, uses every field but `extend`:

`/contract/default`

```json
{
  "description": "Every event from the shop: explicit consent, a page language, and complete ecommerce data.",
  "tagging": 1,
  "schema": { … },
  "events": { … }
}
```

The contract names what every event must carry; the validate step and validate --strict read it. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

`/contract/default/description`: `"Every event from the shop: explicit consent, a page language, and complete ecommerce data."`. description says what the contract is for. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

`/contract/default/tagging`: `1`. tagging is the contract version the tagging follows. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

### Schema[​](#schema "Direct link to Schema")

`schema` is a JSON Schema for the full event. Standard event field names (`globals`, `context`, `consent`, `user`, `custom`, `source`, `data`) live inside `schema.properties`. The schema runs on every event the contract governs, in addition to any per-event rules under `events`.

`/contract/default/schema`

```json
{
  "type": "object",
  "required": [
    "consent",
    "globals"
  ],
  "properties": {
    "consent": {
      "type": "object",
      "required": [
        "functional"
      ],
      "properties": {
        "functional": {
          "const": true
        },
        "marketing": {
          "const": true
        }
      }
    },
    "globals": {
      "type": "object",
      "required": [
        "language"
      ],
      "properties": {
        "language": {
          "type": "string",
          "pattern": "^[a-z]{2}$"
        }
      }
    },
    "user": {
      "type": "object",
      "properties": {
        "device": {
          "type": "string"
        },
        "session": {
          "type": "string"
        }
      }
    }
  }
}
```

schema applies to every event: functional consent is a hard gate, language a required global. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

### Event schemas[​](#event-schemas "Direct link to Event schemas")

Inside `events`, entity-action keyed entries define JSON Schemas for a partial `WalkerOS.Event`. The `*` key matches anything (see [Wildcard inheritance](#wildcard-inheritance)).

`/contract/default/events`

```json
{
  "product": {
    "*": { … },
    "impression": { … },
    "add": { … }
  },
  "order": {
    "complete": { … }
  },
  "session": {
    "start": { … }
  }
}
```

events hold one JSON Schema per entity and action. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

`/contract/default/events/product/*`

```json
{
  "properties": {
    "data": {
      "type": "object",
      "required": [
        "id",
        "name",
        "price"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "name": {
          "type": "string",
          "minLength": 1
        },
        "price": {
          "type": "number",
          "minimum": 0
        }
      }
    }
  }
}
```

product \* applies to every product action. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

## Inheritance with extend[​](#inheritance-with-extend "Direct link to Inheritance with extend")

Use `extend` to inherit from another named contract. Inheritance is additive:

* **`schema`** merges additively across the chain (deep merge of `properties`, union of `required`)
* **`events`** merge at the entity-action level
* **Scalars** (`tagging`, `description`): child overrides if set, otherwise inherited from parent
* **Chains supported**: `web_loggedin` extend `web` extend `default`
* **Circular references** are detected and throw

Extend chains are resolved first, then wildcards expand on the merged result. Schema merging follows the same additive rules as wildcard merging (see [Merge rules](#merge-rules)), so the same limitations on JSON Schema composition keywords apply.

```
"contract": {

  "default": {

    "tagging": 1,

    "schema": {

      "properties": {

        "globals": { "required": ["country"] }

      }

    }

  },

  "web": {

    "extend": "default",

    "events": {

      "product": {

        "add": { "properties": { "data": { "required": ["id", "quantity"] } } }

      }

    }

  },

  "web_loggedin": {

    "extend": "web",

    "schema": {

      "properties": {

        "user": {

          "required": ["id"],

          "properties": { "id": { "type": "string" } }

        }

      }

    }

  }

}
```

`web_loggedin` resolves to: `tagging: 1` (from `default`), `globals.country` required (from `default`), `events.product.add` (from `web`), and `user.id` required (added by `web_loggedin`), all within a single merged `schema`.

In the complete example, the `server` contract extends `default` with what the server adds before any vendor sees an event, the cookieless visitor hash:

`/contract/server/extend`: `"default"`. server extends default with what the server adds: the visitor hash. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

`/contract/server/schema/properties/user/properties/hash`

```json
{
  "type": "string",
  "description": "Cookieless visitor id: HMAC-SHA256 with a secret salt over the anonymized IP, browser with major version, OS and site. Rotates daily (UTC) and is per site, so count visitors per day."
}
```

The description tells analysts what user.hash is and how to count it. [Guide chapter `contract`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#contract-the-contract-and-the-validate-step)

## Wildcard inheritance[​](#wildcard-inheritance "Direct link to Wildcard inheritance")

Contracts support four wildcard levels in `events`. For entity-action pairs listed under `events`, all matching levels merge **additively**:

| Level | Pattern             | Matches                               |
| ----- | ------------------- | ------------------------------------- |
| 1     | `*` → `*`           | All events (global rules)             |
| 2     | `*` → `action`      | A specific action across all entities |
| 3     | `entity` → `*`      | All actions of a specific entity      |
| 4     | `entity` → `action` | Exact match                           |

For `product add`, levels 1, 3, and 4 all apply and combine:

```
"events": {

  "*":       { "*":   { "properties": { "consent": { "required": ["analytics"] } } } },

  "product": { "*":   { "properties": { "data": { "required": ["id", "name"] } } },

               "add": { "properties": { "data": { "required": ["quantity"] } } } }

}



// Resolved "product add":

//   consent.analytics required   (from * -> *)

//   data.id, data.name required  (from product -> *)

//   data.quantity required       (from product -> add)
```

Wildcards expand into the entity-action pairs listed under `events`, and only there do all matching levels merge. An event whose pair is not listed gets a single entry instead, the first that exists of: `entity` → `*` (which already carries the `*` → `*` rules), `*` → `action` (without the `*` → `*` rules), `*` → `*`. In the example above, `product view` gets the `product` → `*` rules plus the `*` → `*` rules. List a pair explicitly when several levels must apply to it.

Contracts vs mapping wildcards

Contract wildcards use **additive merging** for pairs listed under `events`: all matching levels combine. Mapping wildcards use **fallback matching**: the first match wins.

This difference is intentional. Contracts express cumulative requirements (entity-level rules apply to every action); mappings select a single transformation target.

**Contract:** `product.*` rules and `product.add` rules both apply to `product add` **Mapping:** `product.add` matches first, so `product.*` is never checked

### Merge rules[​](#merge-rules "Direct link to Merge rules")

When multiple wildcard levels (or `extend` chains) match, the JSON Schemas merge with these rules:

| JSON Schema keyword                                                    | Merge behavior                       |
| ---------------------------------------------------------------------- | ------------------------------------ |
| `required`                                                             | Union (deduplicated)                 |
| `properties` and other object-valued keywords (`items`, `not`, ...)    | Deep merge                           |
| Scalar and array keywords (`minimum`, `pattern`, `enum`, `oneOf`, ...) | Child overrides parent               |
| Annotations (`description`, `examples`, `title`, `$comment`)           | Stripped from resolved event schemas |

`required` arrays are united and object values merge key by key. Every other array (`oneOf`, `enum`, `type` arrays, `allOf`) and every scalar is replaced by the child's value. Use `allOf` composition at the schema level if full JSON Schema composition is needed.

Annotations stay on the source contract for tooling (CLI hints, IDE descriptions). Resolved event schemas are stripped; the `schema` block keeps its annotations. Validators ignore annotations either way.

## `$contract` references[​](#contract-references "Direct link to contract-references")

Reference any part of a resolved contract with `$contract.<name>.<path>`. The contract is fully resolved (extend + wildcards) before path access, so the returned value is the merged shape, not the raw entry. A validate transformer references a contract via its `contract` setting:

```
"transformers": {

  "validate": {

    "package": "@walkeros/transformer-validate",

    "config": { "settings": { "contract": ["$contract.web"], "mode": "strict" } }

  }

}
```

### Deep paths[​](#deep-paths "Direct link to Deep paths")

Walk into the resolved shape with dot notation:

| Path                                      | Returns                                                                        |
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| `$contract.web`                           | The fully resolved contract entry                                              |
| `$contract.web.schema`                    | The merged event-level JSON Schema                                             |
| `$contract.web.schema.properties.globals` | Just the resolved globals sub-schema                                           |
| `$contract.web.schema.properties.consent` | Just the resolved consent sub-schema                                           |
| `$contract.web.events`                    | All resolved event schemas for the `web` contract                              |
| `$contract.web.events.product.add`        | Resolved schema for `product add`, with all matching wildcard levels merged in |
| `$contract.web.tagging`                   | The contract revision number                                                   |

## CLI validation[​](#cli-validation "Direct link to CLI validation")

Validate contracts standalone or as part of a flow:

```
# Validate a contract file

walkeros validate contract.json --type contract



# Validate inline

echo '{"default":{"events":{"product":{"add":{"properties":{}}}}}}' | walkeros validate --type contract



# Validate a full flow (covers contract structure + example compliance)

walkeros validate flow.json
```

The contract validator checks:

* The root is an object of named contract entries; a flat entity-action map or a `$`-prefixed root key is rejected as the flat shape
* `tagging` is a number (if present)
* `extend` references exist and are not circular
* Entity and action keys are non-empty
* `schema`, if present, is a valid JSON Schema object
* Each event entry is a valid JSON Schema object
* A key outside `extend`, `tagging`, `description`, `events`, `schema` is a warning, not an error. Exception: top-level `globals`, `context`, `custom`, `user` and `consent` keys pass without a warning, but no validator reads them. Put these rules under `schema.properties`.

Advanced: shared schema fragments via `variables`

Top-level `variables` holds reusable values that any part of the config can pull in with `$var.<name>`. Whole-string references preserve native type; deep paths walk into the value:

```
{

  "variables": {

    "idSchema": {

      "required": ["id"],

      "properties": { "id": { "type": "string" } }

    }

  },

  "contract": {

    "web": {

      "events": {

        "product": {

          "*": { "properties": { "data": "$var.idSchema" } }

        }

      }

    }

  }

}
```

Use `variables` when the same JSON Schema fragment shows up in many events. For contract-shaped reuse across flows, prefer `extend`.

`$var` references in a contract resolve at bundle time, so the runtime validate step enforces them.

## Complete example[​](#complete-example "Direct link to Complete example")

A complete example flow with a contract (a full-event `schema` plus per-event rules) lives at [`packages/cli/examples/flow-complete.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.json).

Its server flow's `validate` step references `$contract.server` (see [Validate](/preview/pr-749/docs/transformers/validate.md#reference-a-contract)). For `product add`, that reference resolves to these rules, all from the merged shape:

| Source                              | Rule                                                                  |
| ----------------------------------- | --------------------------------------------------------------------- |
| `default.schema.properties.consent` | `functional` required and `true`, `marketing` `true` when present     |
| `default.schema.properties.globals` | `language` required, two lower-case letters                           |
| `default.events.product.*`          | `data.id`, `data.name` non-empty, `data.price` a number of at least 0 |
| `default.events.product.add`        | `data.quantity` required, integer, minimum 1                          |
| `server.schema.properties.user`     | `user.hash` required                                                  |

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

* **[Validate](/preview/pr-749/docs/getting-started/flow/validate.md)**: enforce a contract at runtime with the validate transformer
* **[Mapping](/preview/pr-749/docs/mapping.md)**: transform events between steps
* **[Step examples](/preview/pr-749/docs/getting-started/flow/step-examples.md)**: pair every step with input/output fixtures
* **[Reference syntax](/preview/pr-749/docs/guides/reference-syntax.md)**: all `$contract`, `$var`, `$flow`, `$store`, `$secret`, `$code:`, `$env` references
