Detect breaking changes in your OpenAPI spec on every pull request. Posts a summary comment. Fails the check on any breaking change (configurable). Powered by the same schema-aware diff engine that runs at monit.rs against production APIs.
- ⚡ Fast — composite action, no
node_modules, no docker pull, ~2 second cold-start - 🎯 Focused — classifies every change as
breaking,non-breaking, orinfo - 💬 Automatic PR comment — reviewers see the diff impact without leaving GitHub
- 🚫 Fails the check on breaking changes by default (configurable)
- 🧠 Same engine as monit.rs — the tool that runs against live APIs 24/7
### 🔍 OpenAPI diff — `openapi.yaml`
| Severity | Count |
| --- | ---: |
| 🔴 Breaking | 2 |
| 🟡 Non-breaking | 5 |
| 🔵 Info | 1 |
<details><summary>Full change list (8 changes)</summary>
| Severity | Path | Change |
| --- | --- | --- |
| 🔴 Breaking | `/v1/users/{id} GET → 200.email` | Field removed: email |
| 🔴 Breaking | `/v1/users/{id} GET → id` | Parameter type changed: string → integer |
| 🟡 Non-breaking | `/v1/users POST → 200` | Field added: created_at |
| … | … | … |
</details>
Powered by [monit.rs](https://monit.rs) · [Get alerted when your live API drifts →](https://monit.rs/login?utm_source=github_action)Add .github/workflows/openapi-diff.yml to any repo that ships an OpenAPI spec:
name: OpenAPI diff
on:
pull_request:
paths:
- 'openapi.yaml' # or wherever your spec lives
- 'openapi.json'
jobs:
diff:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # required to post the comment
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so we can `git show origin/<base>:...`
- uses: Monitrs/openapi-diff-action@v1
with:
spec: openapi.yamlThat's the whole thing. The next PR touching openapi.yaml will get an inline diff summary comment, and the check will fail if any breaking change is detected.
| Name | Required | Default | Description |
|---|---|---|---|
spec |
✅ | — | Path to the OpenAPI spec (JSON or YAML) in the PR head. |
base-spec |
same as spec |
Path to the spec on the base branch (when the file moved between branches). | |
fail-on-breaking |
true |
Fail the check when any breaking change is detected. Set to false to only report. |
|
comment |
true |
Post the diff summary as a PR comment. Set to false to only annotate the run. |
|
api-url |
https://monit.rs |
Override the diff API. Only needed for self-hosted or staging. |
| Name | Description |
|---|---|
breaking |
Number of breaking changes detected |
non-breaking |
Number of non-breaking changes detected |
info |
Number of info-level changes detected |
Wire outputs into follow-up steps:
- id: diff
uses: Monitrs/openapi-diff-action@v1
with:
spec: openapi.yaml
fail-on-breaking: false
- if: steps.diff.outputs.breaking > 0
run: echo "PR contains ${{ steps.diff.outputs.breaking }} breaking change(s)"on:
pull_request:
paths: ['openapi.yaml']- uses: Monitrs/openapi-diff-action@v1
with:
spec: openapi.yaml
fail-on-breaking: false- uses: Monitrs/openapi-diff-action@v1
with:
spec: docs/api/openapi.yaml
base-spec: docs/openapi.yaml # path on the base branchstrategy:
matrix:
spec: [public-api.yaml, admin-api.yaml, webhooks.yaml]
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: Monitrs/openapi-diff-action@v1
with:
spec: ${{ matrix.spec }}The diff engine walks both specs and flags:
Breaking:
- Endpoint (path or method) removed
- Required parameter added
- Parameter type changed (e.g.
string→integer) - Response status code removed
- Response field removed
- Response field type changed
- Response field required-list expanded
- Request body schema tightened (added
required, changed type)
Non-breaking:
- New endpoint
- New optional parameter
- New response field (not required)
- Added tags, descriptions, examples
Info:
- OpenAPI version bump
infoblock changes (title, version)- Description-only edits
- Server URL additions
See the full engine source (~300 lines of Python you can read end-to-end).
The engineering team ships an OpenAPI spec change. It looks harmless in code review — one property added, one renamed. Turns out the rename broke every mobile-app version below 4.2, and a PagerDuty alert fires at 3am.
This action catches those in the PR window instead of at 3am.
The same schema-aware engine runs against live production APIs at monit.rs every 30 seconds — against real traffic, not just the spec. If you like what this Action does in CI, the same engine watching your production is free for 3 endpoints, full feature set, no credit card.
- ✅ Ubuntu, macOS, Windows GitHub-hosted runners (composite action, no OS-specific deps)
- ✅ Self-hosted runners with
jqandghinstalled - ✅ OpenAPI 3.0 and 3.1 (Swagger 2.0 spec parses but with less-informative diffs)
- ✅ JSON and YAML specs
jqandghon the runner (both pre-installed on GitHub-hosted runners)pull-requests: writepermission ifcomment: truefetch-depth: 0on theactions/checkoutstep sogit show origin/<base>:...works
The Action sends both spec versions to https://monit.rs/api/v1/tools/openapi-diff for classification. If your OpenAPI spec contains secrets (it shouldn't, but check), point api-url at a self-hosted deployment.
MIT. See LICENSE. Not affiliated with any listed API vendor.
- monit.rs/changelog — live changelog of 28 public APIs (Stripe, GitHub, Vercel, Cloudflare, Supabase, OpenAI, …)
- monit.rs/tools/openapi-diff — the diff engine as a free web tool (no signup)
- monit.rs/blog — data-driven writing on API stability
- Bluesky @monit.rs — API drift signals in real time