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