APIBreak

CI patterns

Detect breaking API changes: four CI checks for API drift

Your own OpenAPI spec, the API examples in your docs, the vendor specs you depend on, and the mocks your tests trust: one check for each, with the workflow and the failure it catches.

Published 1 November 2026 · 5 min read

An API contract can drift in four directions: you change your own spec, your docs stop matching it, a vendor changes theirs, or your tests keep running against a copy of a vendor that no longer exists. Unit tests catch none of these reliably, because each one is a disagreement between two documents rather than a bug in code. This article gives one CI check per direction: GitHub Actions workflows for the first three and a rule for the fourth. The examples use the apibreak CLI, which is made by the publisher of this site; where another tool does the same job, it is named.

Check 1: your OpenAPI spec against the base branch

The first check answers one question on every pull request: does this change to the spec break a client? It compares the spec in the branch with the same file on the branch the pull request targets.

name: apibreak diff
on:
  pull_request:
    paths: [openapi.yaml]
jobs:
  diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - run: npx apibreak diff --base-ref origin/${{ github.base_ref }} openapi.yaml

apibreak diff reads the base version with git show, so the checkout needs the base branch's history; that is what fetch-depth: 0 is for. Exit codes: 0 when nothing reaches the --fail-on threshold, 1 on a finding at or above it (breaking by default; any also fails on deprecations, advisories and unknowns), 2 on a usage error or a spec that could not be read. If you would rather have inline annotations on the diff, oasdiff-action does the same job with oasdiff/oasdiff-action/breaking and fail-on: ERR or WARN; note that its review link uploads an encrypted copy of both specs unless you set review: false. The OpenAPI diff tools comparison covers the alternatives in detail.

Check 2: your docs against your spec

The second check answers: do the API calls written in our docs still match the spec we publish? An example that sends a field the API does not accept teaches every reader the wrong call, and few CI jobs check API calls written in Markdown. apibreak docs reads fenced curl commands, raw HTTP blocks, JavaScript fetch() calls, Python requests and httpx calls, and inline METHOD /path references in prose, then checks each against the spec.

name: apibreak docs
on:
  pull_request:
    paths: ['docs/**', 'README.md', 'openapi.yaml']
jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: npx apibreak docs --spec openapi.yaml --summary "$GITHUB_STEP_SUMMARY"

It reports unknown endpoints, wrong methods, body fields and query parameters the operation does not declare, and complete examples that leave out a required field as errors, and deprecated operations or fields as warnings, each with file:line and a suggested fix. It exits 0 when nothing reaches --fail-on (error by default), 1 on a finding, 2 on a usage error or an unreadable spec. Run on the API docs of listmonk (commit 82db22c, 4 October 2026) against listmonk's own spec, it found 14 errors in 10 files from 129 API references, including a GET example that sends body fields the spec defines as query parameters and a POST where the route is PUT. The tool states its limit: it reports that docs and spec disagree, not which one is wrong. Checked against listmonk's router, most of those 14 were the spec lagging the server, not the docs. The check finds the disagreement; a person decides the fix.

Check 3: the vendor specs you call, on a schedule

The third check cannot run on pull requests, because the change does not come from a pull request: the vendor changed their published spec. It runs weekly (and on demand) over a manifest of the vendor endpoints your code calls, with a pinned baseline revision of each vendor's spec.

name: apibreak
on:
  schedule: [{ cron: '0 13 * * 1' }]
  workflow_dispatch:
jobs:
  apibreak:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v5
      - uses: Skyblueballykid/apibreak@v1
        with:
          manifest: apibreak.json
          fail-on: breaking

The Action compares the vendor's published OpenAPI spec at your baseline with the current one, keeps only the endpoints in apibreak.json, emits each breaking finding as a workflow error annotation and writes the full table to the job summary. It supports GitHub and Stripe today. On the example manifest on apibreak.dev, ten endpoints across both vendors, a run reports 7 breaking, 1 deprecation, 9 advisory and 19 not compared, with 210 additive changes counted but not listed. A spec that could not be fetched or read is an unknown finding that fails the run at every threshold except never, so an outage never passes as a quiet week. Set timeout-minutes as well, as the tool's README recommends. Note that this command predates the other two and uses its own exit codes: 0 clean, 2 a finding at or above --fail-on, 1 when the tool could not run at all.

Check 4: the freshness of your vendor mocks

The fourth check is a rule rather than a command. If your tests run against a recorded fixture or a stub generated from a vendor spec, that mock is a copy of a contract, and it ages. The failure mode is a green suite against a response shape the vendor retired months ago. Pact's own documentation says consumer-driven contract testing is not a good fit for third-party providers whose team does not also use Pact, so for vendor APIs this rule is usually all you have:

  • Record which spec revision (commit sha or date) each mock or fixture was generated from, in the repository next to it.
  • When check 3 reports a finding on an operation you mock, regenerate or re-record that mock as part of the same fix.
  • Fail CI when a mock's recorded revision is older than the baseline in apibreak.json, so moving the baseline forces the mock forward too.

The four checks at a glance

  1. Spec diff on pull requests: catches your own breaking changes before merge.
  2. Docs check on pull requests: catches examples that no longer match the spec.
  3. Vendor spec diff on a schedule: catches vendor changes to the endpoints you call, with both spec revisions named.
  4. Mock freshness: keeps test doubles tied to a known vendor revision.

Two of the checks are one step each on pull requests, one is a short scheduled workflow, and one is a recorded revision plus a comparison. Together they cover each direction a contract can drift: you changing it, your docs contradicting it, the vendor changing it, and your tests remembering an older version of it.

Check the endpoints you actually call

APIBreak reads the endpoints you declare, compares the vendor's own OpenAPI specification against the revision you pinned, and fails the build only when a change touches one of yours. It runs in your CI with no credentials and nothing uploaded, and the check is free.

Install the check · read a real report · what changed at each vendor

What changed at the vendors in this article

Keep reading