Skip to main content


Optic diffs your OpenAPI documents and lets you write tests about the changes. This playground simulates the kind of diffs Optic runs in CI (between the HEAD and BASE versions of an OpenAPI):

optic diff openapi.yaml --base main

Limitations: the playground can only load internal $ref files for security reasons. The actual CLI can resolve references from the file system and over http.

Examples of OpenAPI changes to make
  • Remove a required property (Optic will show a breaking change)
  • Remove an operation (Optic will show a breaking change)
  • Add a new operation (Optic will show added)
  • Add a new query parameter (Optic will show added, and breaking if you made it required)
  • Change the order of parameters, properties, or operations (Optic will show no changes)

How the diff works​

Optic computes a semantic API diff that highlights how the API has changed, not how the lines have changed (git) or the JSON has changed (json diff). Instead of being grouped by files/lines, our changelog shows you changes grouped by operation and body. If a $ref is updated, the changelog shows you everywhere that $ref is being used.

To compute its semantic diffs, Optic traverses the OpenAPI document and emits facts about your API. Each fact describes a part of your API and is keyed with a deterministic ID. There are facts every property, parameter, operation, body, status code, etc (see code here). When Optic runs a diff it collects facts from both versions of the specification and compares them to one another.

  • Facts that appear in both versions are compared to one another. Those with changes are marked Changed
  • Facts that only appear in the latest version are marked Added
  • Facts that only appear in the previous version are marked Removed

Testing changes with Optic​

Optic makes it easy to lint your API design and test your changes. Our SDK lets you test for breaking changes and ensure your versioning policy is followed. You can also use the tests to make sure a style guide is followed whenever your team is building something new.

This test flags removed operations as breaking:

operations.removed(() => {
throw new RuleError({
message: "cannot remove an operation. This is a breaking change.",

This test flags adding a required query parameter as breaking:

queryParameter.added((parameter, ruleContext) => {
// allow required query parameters when operation is also being added
if (ruleContext.operation.change === "added") return;
if (parameter.value.required) {
throw new RuleError({
message: `cannot add required query parameter ${} to an existing operation. This is a breaking change.`,

This test runs all the time and makes sure operationId is set:

operations.requirement((operation) => {
if (!operation.value.operationId) {
throw new RuleError({
message: `operations must have an operationId. Missing for ${operation.method} ${operation.path}`,