Quickstart
This guide sets up Contractual in a new directory with linting, breaking change detection, a versioned changeset, and a bumped contract version. Estimated time: 5 minutes.
Step 1: Create a spec file
Section titled “Step 1: Create a spec file”Create a project directory and add a JSON Schema file. This is the contract Contractual will manage.
mkdir my-project && cd my-projectCreate schemas/order.schema.json:
{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://example.com/order.schema.json", "title": "Order", "description": "A customer order", "type": "object", "required": ["id", "status", "amount"], "properties": { "id": { "type": "string", "description": "Unique order identifier" }, "status": { "type": "string", "enum": ["pending", "confirmed", "shipped", "delivered"], "description": "Current order status" }, "amount": { "type": "string", "description": "Order total as a formatted string, e.g. '12.50'" }, "customer_id": { "type": "string", "description": "ID of the customer who placed the order" } }, "additionalProperties": false}mkdir schemas# paste the JSON above into schemas/order.schema.jsonStep 2: Initialize Contractual
Section titled “Step 2: Initialize Contractual”Run contractual init from the repository root. It scans for recognizable spec files and scaffolds the configuration.
contractual initExpected output:
Scanning for contracts...
Found schemas/order.schema.json → JSON Schema
Scaffolding contractual.yaml... doneScaffolding .contractual/ done
.contractual/snapshots/ versioned snapshots will live here .contractual/changesets/ pending changesets will live here .contractual/versions.json
Run `contractual lint` to validate contracts.This creates two items:
contractual.yaml: configuration file.contractual/: state directory that tracks versions, snapshots, and pending changesets
Open contractual.yaml to view the generated configuration:
# yaml-language-server: $schema=https://contractual.dev/schema.jsoncontracts: - name: order-schema type: json-schema path: schemas/order.schema.jsonStep 3: Run lint
Section titled “Step 3: Run lint”Validate the contract against the JSON Schema meta-schema.
contractual lintExpected output:
Linting contracts...
✓ order-schema schemas/order.schema.json
All contracts passed lint.If the schema has structural problems such as unknown keywords, invalid types, or malformed $ref, lint reports them here before any diff runs.
Step 4: Make a breaking change
Section titled “Step 4: Make a breaking change”Edit schemas/order.schema.json to change the type of the amount field from string to number. This simulates an accidental breaking change that Contractual is designed to catch.
Find this section:
"amount": { "type": "string", "description": "Order total as a formatted string, e.g. '12.50'"},Change it to:
"amount": { "type": "number", "description": "Order total as a numeric value, e.g. 12.50"},Save the file.
Step 5: Detect the breaking change
Section titled “Step 5: Detect the breaking change”contractual breakingExpected output:
Checking for breaking changes...
✗ order-schema schemas/order.schema.json
BREAKING /properties/amount/type Changed type: string → number Consumers expecting a string value will fail to validate.
1 breaking change detected.Exit code: 1Contractual compares the current schema against the snapshot saved during init (the baseline). It classifies the type change from string to number as breaking because existing consumers that expect a string value now fail validation.
Step 6: Create a changeset
Section titled “Step 6: Create a changeset”A changeset is a small markdown file that declares what changed and at what semver level. Contractual auto-generates one from its diff analysis.
contractual changeset --no-interactiveExpected output:
Generating changeset...
order-schema MAJOR (breaking change detected)
Created .contractual/changesets/lucky-tiger-runs.md
Review and edit the changeset before committing it.Open .contractual/changesets/lucky-tiger-runs.md to view the generated changeset:
---"order-schema": major---
Changed type of `amount` field from `string` to `number`.
Consumers that pass string values for `amount` must be updated to pass numeric values instead.The frontmatter declares the bump level (major) per contract. The body is the human-readable description that appears in the CHANGELOG. Both can be edited before committing.
Step 7: Bump the version
Section titled “Step 7: Bump the version”Once the changeset is ready, run contractual version to consume it.
contractual versionExpected output:
Consuming changesets...
order-schema 0.0.0 → 1.0.0 (major bump from lucky-tiger-runs.md)
Updated .contractual/versions.jsonUpdated .contractual/snapshots/order-schema/1.0.0.jsonDeleted .contractual/changesets/lucky-tiger-runs.mdUpdated schemas/CHANGELOG.md
Done. Commit the changes and push.Contractual has:
- Bumped
order-schemafrom0.0.0to1.0.0 - Saved a new snapshot of the schema at the new version
- Deleted the consumed changeset file
- Appended an entry to
schemas/CHANGELOG.md
For OpenAPI, AsyncAPI, and ODCS contracts, contractual version also writes the new version number into the spec file itself (for example, info.version in an OpenAPI document). JSON Schema has no standard version field, so this step is skipped here.
Check schemas/CHANGELOG.md:
# Changelog
## 1.0.0 - 2026-02-17
### Breaking Changes
- Changed type of `amount` field from `string` to `number`. Consumers that pass string values for `amount` must be updated to pass numeric values instead.That is the full CLI workflow. The result is a versioned contract with an auditable history.