Skip to main content
Contract Auditor audits an HTTP API and reports every place the code, the OpenAPI spec and the published docs disagree. Each finding ships with a Go test that fails against the current code. If the test passes, the finding was wrong and gets dropped.

Nothing reaches the report unverified

Everything else in the design follows from that. See The verification gate.

The problem

An API ends up with three descriptions of itself: the handler code, the generated spec, and the prose your partners read. Different people edit each one, at different times. Code review asks whether the code is correct. It does not ask whether the code still matches what was promised, so the difference tends to show up at integration time, in someone else’s codebase.

The problem, second half: who can reach what

Drift is not only what an endpoint returns. It is also who is allowed to call it, and that half is invisible in code review. Most codebases register two APIs in one place. One is the public contract, held open by an API key an integrator stores in a config file for years. The other is a dashboard or admin console, reached with a session token that expires. They live in the same router files, and are told apart only by which middleware they sit behind. Once one guard accepts both credentials (an authenticate that takes x-api-key or a bearer token, which is a very ordinary thing to write), every route behind it is reachable with either. A machine key scoped for payments now also reaches account management, and nobody decided that. It was never written down, so nothing contradicts it. The auditor makes that surface visible two ways:
  • contract-middleware restricts the audit to routes behind the guard that reads the credential your spec promises integrators. What comes back is, precisely, the set of endpoints your API key can reach. Anything in that list you believed was dashboard-only is an architectural finding, and it is the reason this section exists. That is how the payments API above discovered its own session endpoints answered to a merchant key.
  • auth_guard_missing and auth_guard_undocumented compare each route’s guard against the security its spec declares, in both directions. Documented as protected but registered open is critical. Guarded but documented public answers an integrator 401 for following the documentation.
Neither needs the application to run. Both read which middleware guards which route, which is why ordering matters: router.use(mw) protects only the routes registered after it, and a route above that line is open however protected the rest of the file looks.

Measured on a real payments API

841 vs 170 is not 671 missing docs. Public documentation is for the API key holders you integrate with, so anything an API key is not meant to reach belongs out of it: admin, internal, and every JWT route your own UI calls on behalf of a signed-in user. The line is the credential, not the feature. The real problem is that nobody knows which of the 671 should have been published.

Who it is for

Teams publishing an API

Run it in CI, and drift gets caught at the commit that caused it.

Fintech and payments

Knows about money precision, auth scope, idempotency, webhook signing.

Teams integrating

Where the code is readable to you: another team’s service in your own monorepo, an open source API, or a vendor repo you were given. Check the docs you were handed against it.

Platform and DevEx

A number for doc drift, instead of a hunch.
The audit reads source. Stage 1 parses the route table out of the handlers, and the verification gate compiles and runs a test against them. So it runs where the code is: in the provider’s repository, or in yours if the code was shared with you. It cannot audit an API you can only reach over the network.

Quickstart

Running in five minutes, no API key.

How it works

The pipeline in one diagram.