Spec0docsCLI 0.7.0
Platform

Versioning

How Spec0 assigns and stores API versions — and the recommended policy for evolving an API without breaking consumers.

Spec0 keeps versioning (assigning a tag to a published spec) deliberately separate from breaking-change detection (deciding whether a change is compatible). Versioning is mechanical and always succeeds; detecting and acting on breaking changes is a policy you opt into. This page covers both.

How a version is assigned on publish

When you publish a spec, Spec0 resolves the version tag in this order:

  1. An explicit --version <tag> you pass.
  2. The info.version field inside the OpenAPI spec.
  3. A default of 0.1.0 if neither is set.

Tags are arbitrary strings — semver (1.4.0), sha-prefixed (staging-abc1234), calendar (v2026-Q3), or anything else. Spec0 stores what you pass.

--semver: automatic patch bumps

Pass --semver and, if the API's previous version is a valid semver, Spec0 atomically increments the patch component under a lock (so concurrent publishes don't race):

latest = 1.4.0  →  next published = 1.4.1

If the previous version isn't valid semver, or this is the first publish, --semver is a no-op and the resolution order above applies (so a first publish lands at 0.1.0 unless you pass --version). --semver does no spec parsing and no diffing — it's purely mechanical.

spec0 publish ./openapi.yaml --name my-api --semver --visibility published

Versions are immutable and unique

Once published, a version is immutable. A version tag is unique per API:

  • Re-publishing the same spec content under an existing tag is idempotent — you get the existing version back, no error.
  • Publishing different content under an existing tag returns 409 Conflict — bump the version instead.

This is the convention Spec0 recommends — and dogfoods — but it is not enforced for you by the platform. You make it real in CI (below).

Keep every publish to an existing API backwards-compatible. When you need a breaking change, start a new API — typically my-api-v2 — and let consumers migrate on their own timeline. v2 lives alongside v1 for as long as both have users.

Why it's worth following:

  • Consumers can't be surprised. If an API is still accepting publishes, pulling its latest version won't break existing integrations.
  • Migration is opt-in. A v2 beside v1 means no coordinated cutover and no surprise SDK regenerations.
  • Versioning stays boring. With breaking changes off the table for a given API, every publish is a non-breaking improvement.

For a major redesign: pick a new slug (payments-api-v2), publish it, and deprecate the old API when you're ready to encourage migration. The old API stays discoverable for as long as it has consumers.

Enforcing it in CI

You can gate on breaking changes in your pipeline as well as on the platform. spec0 diff --breaking-only exits non-zero when it finds a breaking change — wire that into PR checks:

# On every PR that touches the spec — fail if it breaks consumers
- run: spec0 diff origin/main:openapi.yaml ./openapi.yaml --breaking-only

# On merge to main — publish (idempotent if content is unchanged)
- run: spec0 publish ./openapi.yaml --name my-api --semver --visibility published

See the block-on-breaking-changes recipe for a complete workflow. Spec0 runs this exact policy on its own public API.

Breaking-change detection on the platform

Independently of the CI gate, Spec0 classifies changes server-side using oasdiff. There are two distinct mechanisms, and they behave differently:

1. Recording (always on). After a publish, Spec0 records the result on the version — a breakingChangesRecorded flag plus a summary — surfaced in the version history and via the public API. This never blocks or delays a publish.

2. The publish-time gate (opt-in). The breaking-change section of your org governance policy is evaluated during the publish, before the spec is stored. Its behaviour depends on the mode you set:

ModeEffect on a breaking publish
OFF (default)Publish succeeds. The change is still recorded.
WARNPublish succeeds, violation surfaced.
BLOCKPublish is rejected — HTTP 422, CLI exit code 7.

So whether a breaking publish succeeds depends entirely on your org's configured mode. Out of the box the section is OFF, so publishes succeed and are simply marked — but an org that sets BLOCK gets server-side enforcement without any CI wiring.

The gate fails open: if the diff comparison can't run, the publish proceeds rather than being blocked by an infrastructure problem. Keep the CI check above as well — the two are complementary, not redundant.

What counts as breaking

This is what spec0 diff (and the server-side detector) classify — the same set the CI gate keys off:

CompatibleBreaking
Adding a new endpoint or operationRemoving a field, parameter, endpoint, or operation
Adding a new optional fieldChanging a field's type
Adding a value to an extensible enumAdding a required field or required parameter
Loosening a constraint (raising maxLength)Tightening a constraint (lowering maxLength, narrowing a regex)
Adding a new optional query parameterRemoving or renaming an enum value
Updating descriptions, summaries, examples, server URLsChanging authentication or required headers

Changelogs

Every published version carries its own changelog — one entry per version tag, living on the version, not the API.

Right after a successful publish, Spec0 diffs the new spec against its predecessor and asks an LLM to summarise the change in plain English (routed through your own LLM key if configured, otherwise Spec0-hosted). By the time the version appears under Apis → <api> → History, the changelog is attached with an AI ✨ chip. If credits or AI aren't available, it falls back to a structural summary of the diff.

The owning team can edit the text inline (the chip drops — the entry is now manually owned), or hit Regenerate to re-run the generator against the same diff and overwrite it. There's exactly one entry per version; editing and regenerating both replace, with no history.

Read the same data from the CLI:

spec0 api changelog acme/orders --to 1.4.1 --output markdown

See spec0 api changelog for the full flag set.

See also

  • APIs — the registry and how publishing works end-to-end.
  • spec0 publish--version and --semver.
  • spec0 diff--breaking-only for the CI gate.
  • Governance — org-level linting and the policy roadmap.
Was this helpful?

On this page