# API Versioning Policy

GoSmarter versions its API **per endpoint**, not as a whole. Most endpoints never need to change; when one does need a breaking change, only that endpoint gets a new version — everything else keeps working exactly as it does today.

:::info

There is no concrete v2 endpoint yet. This page documents the policy and mechanism ahead of the first real breaking change, so that when one is needed it follows an established, low-friction path instead of becoming an ad hoc incident.

:::

## How versioning works

- Every endpoint that has never had a breaking change is implicitly **v1** — its URL is unversioned (e.g. `/api/company/{companyId}/orders-slim`).
- A breaking change to a specific endpoint ships as a **new route** for that endpoint only, prefixed `v2/` (e.g. `/api/v2/company/{companyId}/orders-slim`). The v1 route keeps working, unchanged, alongside it.
- There is no whole-API version switch — different endpoints can be at different version generations independently. Check each endpoint's own page in the [API Reference](/api) for its current version.

## What counts as a breaking change

This table is the single source of truth for classifying a change. An automated CI job checks it on pull requests that touch the API (pull requests from forks are skipped). The job compares the OpenAPI document the API serves on the pull request against the one served on the current tip of the branch it targets, using GitHub's merge result of the pull request, and flags the breaking changes that are visible in the OpenAPI contract (removed or renamed fields, type changes, new required inputs, removed routes). Changes that cannot be seen in the contract, such as status-code semantics, tighter authorization or pagination behaviour, still need reviewer judgement. New `v2/` routes are new paths, so they never trip it. A new required response field is reported too, but the check cannot tell whether the field has a safe default: if it genuinely does, say so in the pull request description for the reviewer to accept. There is no automatic suppression yet; one will be designed before the check becomes blocking. During rollout the check reports findings as warnings only; it will become blocking once false positives have been ruled out.

| Change | Breaking? |
|---|---|
| Add optional response field / request field / query param | No |
| Add a new endpoint | No |
| Add a member to an enum, when the field is documented as open-ended | No |
| Remove or rename a response/request field | **Yes** |
| Change a field's type or semantic meaning | **Yes** |
| Change pagination shape, response envelope, or error format | **Yes** |
| Make an optional request field required | **Yes** |
| Add a new required response field with no safe default | **Yes** |
| Change status code semantics | **Yes** |
| Tighten authorization on an existing route | **Yes** |
| Add an enum member to a field with no open-ended contract | **Yes** (default assumption) |

Any change marked "Yes" gets a new `v2/` route rather than being applied to the existing endpoint. This applies to stable endpoints; Preview endpoints are covered in the next section.

## Preview APIs

Some endpoints are released as **Preview** so you can try new capabilities early and tell us what works for you. A Preview endpoint is fully functional and supported, but its contract is not final yet.

- **How to recognise one.** Preview endpoints are listed under **Preview** in the [API Reference](/api), their summary ends in "(Preview)", and their description starts with a Preview note. In the OpenAPI document they carry the `Preview` tag and the `x-stability-level: beta` extension, so tooling can detect them too.
- **What can change.** Until it is announced as stable, a Preview endpoint may change in ways this policy otherwise treats as breaking, or be removed, **without a new `v2/` route and without the deprecation notice period** above. We avoid unnecessary churn, and every change to a Preview endpoint is listed in the [API Changelog](/api-changelog) under **Preview**.
- **SDKs and generated clients.** Preview endpoints are left out of GoSmarter SDKs, client kits and code samples unless you opt in to Preview endpoints, so a stable integration does not pick one up by accident. If you generate your own client from the OpenAPI document, filter on `x-stability-level` to do the same.
- **Graduation.** When a Preview endpoint is ready, we announce its graduation to stable in the [API Changelog](/api-changelog). From that announcement on, the endpoint is a frozen v1 route like any other: later breaking changes need a new `v2/` route and follow the deprecation notice period.
- **No downgrades.** A stable endpoint never becomes Preview. If a stable endpoint needs a reworked contract, the new contract ships as a `v2/` route (which may itself start as Preview) and the stable route follows the deprecation process.

The automated check described above skips Preview endpoints, still checks every stable endpoint as before, and reports any attempt to mark an existing stable endpoint as Preview.

## Deprecation notice period

Once a v2 route ships and the corresponding v1 route is scheduled for retirement:

- **Minimum 90 days' notice** before a v(n-1) route is retired.
- **Minimum 180 days' notice** for high-traffic or high-integration-cost endpoints.
- The deprecated route remains **fully supported** — not just reachable — for the entire notice window. It receives the same operational care as any current endpoint until the day it is retired.

## Security exception

The only exception to the notice period above is a change required for security or compliance reasons (for example, closing a vulnerability or complying with a new regulatory requirement). In that case GoSmarter may retire or change an endpoint faster than the standard window, with as much notice as the situation safely allows.

## How you'll know a route is deprecated

Deprecated routes carry machine-readable signals on every response, in addition to being called out in the [API Changelog](/api-changelog):

| Header | Meaning |
|---|---|
| `Deprecation: true` | This route is deprecated and has a replacement. |
| `Sunset` | The date (RFC 8594 / RFC 1123 format) after which the route will stop working. |
| `Link: <...>; rel="deprecation"` | A link to that route's migration guide. |

The same information is in the OpenAPI document: a deprecated operation has `deprecated: true` and an `x-sunset` extension holding the sunset date (`YYYY-MM-DD`; the route stops being guaranteed from the start of that UTC day, matching the `Sunset` header), so tooling that reads the spec, such as `oasdiff`, can detect a pending retirement without calling the API.

:::tip

If you're building an integration against GoSmarter, check for the `Deprecation` response header rather than relying on reading the changelog manually — this lets your own monitoring flag a pending migration automatically.

:::

## Where to find migration guidance

Every breaking change ships with a dedicated migration guide (before/after request and response shapes, a working `curl` example, and the sunset date) linked from both the [API Changelog](/api-changelog) and the deprecated route's own reference page.

## Related

- [API Changelog](/api-changelog) — dated log of every `Added`/`Changed`/`Deprecated`/`Removed` change to the public API, with Preview endpoint changes listed separately under `Preview`
- [ADR-1426: API Versioning Strategy](https://github.com/GoSmarter-ai/gosmarter-core-platform/blob/main/engineering/decisions/1426-api-versioning-strategy.md) — the internal decision record this policy implements
