Skip to main content

Public API Versioning

Internal APIs vs Public APIs

  • Internal APIs are backend endpoints used by the Legion web app, browser extension, and worker. They are not treated as long-term external contracts and can evolve faster, including coordinated breaking changes when needed.
  • Public APIs are customer-facing endpoints with strict contracts because customers integrate against them. They are exposed under a versioned prefix (for example: /api/v1).
  • Public APIs are governed by contract checks in CI (OpenAPI snapshots + diff validation) and are implemented in dedicated public API modules (separate routes/schemas from internal APIs) to avoid unintended changes.

Types of Public API Versions

The goal is to collect incremental, non-breaking additions in a single latest public API version to avoid frequent new API versions and needing to maintain a large number of API versions.

Public API versions are divided into two types:

  • Active version:
    • Exactly one active version at a time.
    • This is the only version allowed to receive API changes.
    • Changes must be non-breaking. For example, adding endpoints or response fields is allowed; removing endpoints or adding new required request fields is not.
  • Frozen versions:
    • Zero or more older versions (previously active versions).
    • Fully immutable: no route, schema, or contract changes are allowed.

In backend code, server/public_api/version_registry.py holds the mapping of the current ACTIVE_VERSION and the list of previously-active FROZEN_VERSIONS.

Contract Enforcement Rules (CI)

Each public API version must have a committed OpenAPI snapshot file.

scripts/public_api_contract_check.py runs on every pull request and enforces:

  • Frozen versions: must match snapshot exactly (any change fails).
  • Active version:
    • breaking changes fail
    • endpoint removals fail
    • non-breaking changes require a snapshot update in the PR

Process: Creating a New Public API Version

  1. Choose the next version name using incremental numbering (for example, v2 after v1).
  2. Create a new version folder (server/public_api/v2) and add the routes/schemas for that version.
  3. Register the new router in server/api.py with prefix /api/v2.
  4. Update server/public_api/version_registry.py:
    • move the old active version into FROZEN_VERSIONS
    • set ACTIVE_VERSION to the new version
  5. Generate an OpenAPI snapshot for the new version:
    • poetry run python scripts/public_api_generate_openapi_schema.py --version v2
  6. Commit the new snapshot file under server/public_api/snapshots/.
  7. Run contract validation:
    • poetry run python scripts/public_api_contract_check.py --base-ref origin/main
  8. Verify tests for version registry and route/snapshot presence pass.
  9. Add relevant tests for the new version to prevent future regressions.