<!-- Source: https://docs.geopera.com/api-reference/versioning · Markdown for LLMs -->

# Versioning

Geopera versions two things independently: the API surface itself, encoded in the request path as `/v1/op/...`, and each client package, which follows Semantic Versioning on its own release cadence. Understanding which axis a change lives on tells you whether you need to update your code, bump a dependency, or do nothing at all — the path version changes rarely and only for surface-level breaks, while the packages change often as the API evolves and as client ergonomics improve.

## Two version axes

| Axis              | Where it lives                            | Current value  | Cadence                                      |
| ----------------- | ----------------------------------------- | -------------- | -------------------------------------------- |
| API surface       | Request path: `/v1/op/{operation_id}`     | `v1`           | Changes only on a surface-incompatible break |
| API contract      | `info.version` in the OpenAPI document    | `2.0.0`        | Tracks the API contract                      |
| Python client     | `geopera` on PyPI                         | `2.0.0`        | SemVer, independent                          |
| TypeScript client | `@geopera/sdk` on npm                     | `2.0.1`        | SemVer, independent                          |
| CLI               | `geopera-cli` (console command `geopera`) | `0.1.0` (Beta) | SemVer, independent                          |

The path version and the package versions are deliberately decoupled. The `/v1` segment is a coarse, long-lived contract marker. The package numbers move much faster — they can advance through patch and minor releases many times while `/v1` stays put, because most contract changes are additive and do not break the surface.

## The API surface version (`/v1`)

Every one of the 227 operations is invoked the same way:

```http
POST /v1/op/{operation_id} HTTP/1.1
Host: api.geopera.com
Authorization: Bearer gpra_...
Content-Type: application/json

{ ...operation input... }
```

The `/v1` in that path is the API-surface version. It is the single most stable thing in the API. It does **not** appear as a header, a query parameter, or a body field — it is structural, baked into the URL of every request. See [Operations](/api-reference/operations) for how operation IDs encode list/get/delete intent in the name (there are no REST verbs or path parameters).

### What "breaking" means for the path version

The `/v1` segment changes only for a surface-incompatible break — a change to the calling convention shared by _all_ operations, such as:

- The request method or path shape (e.g. moving away from `POST /v1/op/{id}`).
- The authentication model (see [Authentication](/api-reference/authentication)).
- The error envelope (see [Errors](/api-reference/errors), which uses RFC 9457 `problem+json`).
- The pagination or idempotency contract shared across operations (see [Pagination](/api-reference/pagination) and [Idempotency](/api-reference/idempotency)).

If such a break ever ships, it arrives at a new path prefix (e.g. `/v2/op/...`) and `/v1` continues to serve existing clients in parallel. You will never have an existing `/v1` request silently change shape underneath you.

### What does _not_ bump the path version

Adding a new operation, adding an optional input field, adding a field to a response, or adding a new enum value are all **additive** changes. They are surfaced through a new OpenAPI document version and new package releases — they do not touch `/v1`. Your existing requests keep working unchanged.

## The OpenAPI document version

The spec is an OpenAPI 3.1.0 document whose `info.version` is currently `2.0.0`:

```json
{
	"openapi": "3.1.0",
	"info": {
		"title": "Geopera Operations",
		"description": "Official API for the Geopera geospatial data platform.",
		"version": "2.0.0"
	}
}
```

This `2.0.0` is the version of the _contract_ — the catalogue of operations, their inputs, and their outputs. Note that `info.version` (`2.0.0`) is distinct from the OpenAPI dialect version (`openapi: 3.1.0`) and from the path version (`/v1`); all three are unrelated numbers that happen to live in the same document.

## How the contract relates to the clients

The Python and TypeScript clients are fully typed against the API contract, so a new operation or field in the contract appears as a typed method or property in each client once you upgrade. When the contract gains or changes something, its `info.version` is bumped and the clients are re-released under their own SemVer numbers.

The client version numbers diverge because each package also carries client-only changes (bug fixes, ergonomics) that release on their own schedule. That is exactly why `@geopera/sdk` sits at `2.0.1` while `geopera` sits at `2.0.0` — the TypeScript package has shipped one patch beyond its Python sibling despite tracking the same contract. Both expose typed inputs, outputs, and errors (`Problem` / `HTTPValidationError`).

## Package versions and SemVer

Each client package follows [Semantic Versioning](https://semver.org): `MAJOR.MINOR.PATCH`.

- **MAJOR** — a breaking change to the _package's own API_ (a renamed method, a changed signature, a dropped export), or adoption of a breaking contract change. Bumping a major may or may not coincide with a `/v1 → /v2` surface break.
- **MINOR** — backward-compatible additions, typically new operations or new optional fields flowing in from an updated contract.
- **PATCH** — backward-compatible fixes with no surface change (build fixes, typing corrections, dependency bumps).

A package major bump and a path version bump are independent events. The Python client reached `2.0.0` as its first public release; that major number reflects the package's own line, not the `/v1` surface. Conversely, an additive contract change that leaves `/v1` untouched can still produce a minor package release on both clients.

Pin clients the way you pin any dependency:

```bash
# Python — install the published client (PyPI package: geopera)
pip install "geopera>=2.0.0,<3.0.0"
```

```bash
# TypeScript — npm package: @geopera/sdk
npm install @geopera/sdk@^2.0.1
```

```bash
# CLI — console command is `geopera`
pip install "geopera-cli>=0.1.0"
```

The CLI is at `0.1.0` and is marked Beta (`Development Status :: 4 - Beta`). It is a thin auth and dispatch shell over the published `geopera` SDK — all real API calls go through the SDK's `AuthenticatedClient`. Treat pre-`1.0.0` CLI releases as still-stabilizing: minor bumps below `1.0.0` may carry breaking CLI changes per SemVer's zero-version rule.

## Worked example: tracking a contract change end to end

Suppose a new operation `catalog.search.v2` is added to the API. Here is how each version axis responds.

```http
POST /v1/op/catalog.search.v2 HTTP/1.1
Host: api.geopera.com
Authorization: Bearer gpra_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{ "collections": ["sentinel-2-l2a"], "limit": 50 }
```

1. **Path version (`/v1`)** — unchanged. The new operation is invoked at `/v1/op/...` exactly like every existing one. Adding an operation is additive, so `/v1` does not move.
2. **OpenAPI `info.version`** — bumped (e.g. `2.0.0 → 2.1.0`) because the contract gained an operation.
3. **`geopera` (Python)** — released as a minor bump (e.g. `2.0.0 → 2.1.0`); the new operation appears as a typed method.
4. **`@geopera/sdk` (TypeScript)** — released as a minor bump from its current line (e.g. `2.0.1 → 2.1.0`); the new operation appears as a typed method.
5. **`geopera-cli`** — picks up the new operation transparently the next time it depends on the updated `geopera` SDK; its own version moves only if the CLI surface itself changes.

Your existing code keeps working throughout. To _use_ the new operation, you upgrade the relevant package — you never change your base URL or path prefix.

## Changelogs

Each package maintains its own changelog; consult the one for the package you depend on:

- Python `geopera` — changelog shipped with the [PyPI package](https://pypi.org/project/geopera/) and the [`geo-pera/geopera-python`](https://github.com/geo-pera/geopera-python) repository.
- TypeScript `@geopera/sdk` — release notes in the [`geo-pera/geopera-typescript`](https://github.com/geo-pera/geopera-typescript) repository and on [npm](https://www.npmjs.com/package/@geopera/sdk).
- CLI `geopera-cli` — release notes in the [`geo-pera/geopera-cli`](https://github.com/geo-pera/geopera-cli) repository.

## Guidance

- Build against `/v1` and treat it as permanent for the life of your integration; a successor surface would ship at a new prefix, not replace it in place.
- Pin client packages with a SemVer range that allows minors and patches but caps the major (`>=2.0.0,<3.0.0`), then upgrade deliberately.
- Watch the relevant package changelog rather than the spec's `info.version` — the changelog is where breaking-vs-additive is called out for _your_ client.
- Remember the three numbers are independent: `/v1` (surface), `2.0.0` (contract / `info.version`), and your package's SemVer can each move without forcing the others.
