> 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`](https://www.walkeros.io/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 contract describes what events should look like; the validate transformer enforces it where you place it in the pipeline.
* **Composable with the rest of the config.** Reference fragments anywhere via `$contract.<name>.<path>` (see [Reference syntax](https://www.walkeros.io/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         |

```
{

  "version": 4,

  "contract": {

    "default": {

      "tagging": 1,

      "schema": {

        "type": "object",

        "properties": {

          "globals": {

            "required": ["country"],

            "properties": {

              "country": { "type": "string" }

            }

          },

          "consent": {

            "required": ["analytics"],

            "properties": {

              "analytics": { "type": "boolean", "const": true }

            }

          }

        }

      }

    },

    "web": {

      "extend": "default",

      "events": {

        "product": {

          "add": {

            "properties": {

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

            }

          }

        }

      }

    }

  }

}
```

### 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`.

### 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)).

```
"events": {

  "product": {

    "*": {

      "description": "A product in the catalog",

      "properties": {

        "data": {

          "type": "object",

          "required": ["id", "name"],

          "properties": {

            "id": { "type": "string", "description": "Product SKU" },

            "name": { "type": "string", "description": "Display name" }

          }

        }

      }

    },

    "add": {

      "description": "Product added to cart",

      "properties": {

        "data": {

          "type": "object",

          "required": ["quantity"],

          "properties": {

            "quantity": { "type": "integer", "minimum": 1 }

          }

        }

      }

    }

  }

}
```

## 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`.

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

Contracts support four wildcard levels in `events` that all 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)
```

Contracts vs mapping wildcards

Contract wildcards use **additive merging**: 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`                                                 | Deep merge                           |
| Scalar keywords (`minimum`, `pattern`, ...)                  | Child overrides parent               |
| Annotations (`description`, `examples`, `title`, `$comment`) | Stripped from resolved event schemas |

Additive deep-merge applies to `properties` (deep-merge) and `required` arrays (union/dedup). Other JSON Schema keywords (`oneOf`, `enum`, `type` arrays, `allOf`) follow child-wins. 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), but the runtime schema seen by validators is the stripped form.

## `$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

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`.

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

A canonical, tested flow with a multi-entry contract lives at [`packages/cli/examples/flow-complete.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.json).

A shorter end-to-end illustration:

```
{

  "version": 4,

  "contract": {

    "default": {

      "tagging": 1,

      "description": "Web shop tracking contract",

      "schema": {

        "type": "object",

        "properties": {

          "globals": {

            "required": ["country", "currency"],

            "properties": {

              "country":  { "type": "string", "pattern": "^[A-Z]{2}$" },

              "currency": { "type": "string", "pattern": "^[A-Z]{3}$" }

            }

          },

          "consent": {

            "required": ["analytics"],

            "properties": {

              "analytics": { "type": "boolean", "const": true }

            }

          }

        }

      },

      "events": {

        "product": {

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

          "add": { "properties": { "data": { "required": ["quantity"], "properties": { "quantity": { "type": "integer", "minimum": 1 } } } } }

        },

        "order": {

          "complete": { "properties": { "data": { "required": ["total"], "properties": { "total": { "type": "number", "minimum": 0 } } } } }

        }

      }

    },

    "web_loggedin": {

      "extend": "default",

      "schema": {

        "properties": {

          "user": {

            "required": ["id", "email"],

            "properties": {

              "id":    { "type": "string" },

              "email": { "type": "string", "format": "email" }

            }

          }

        }

      }

    }

  },

  "flows": {

    "web-shop": {

      "config": { "platform": "web" },

      "transformers": {

        "validate": {

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

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

        }

      }

    }

  }

}
```

For `product add`, a validate transformer that references `$contract.web_loggedin` sees these rules (all from the merged shape):

| Source                                | Rule                                               |
| ------------------------------------- | -------------------------------------------------- |
| `default.schema.properties.globals`   | `country`, `currency` required, both match pattern |
| `default.schema.properties.consent`   | `analytics` required and `true`                    |
| `default.events.product.*`            | `data.id`, `data.name` required                    |
| `default.events.product.add`          | `data.quantity` required, integer, minimum 1       |
| `web_loggedin.schema.properties.user` | `user.id`, `user.email` required                   |

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

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