Every endpoint lives under `/api/v1`. This page is the whole policy: what `/api/v1` promises never to do, what counts as a breaking change, how one operation is deprecated without breaking anyone still calling it, and the changelog of every contract-level correction we've ever made.

## /api/v1 stability

Inside `/api/v1` we only ever make **additive** changes:

- A new endpoint.
- A new optional request field.
- A new response field.
- A new, distinct error `code` for a genuinely new failure condition.

We never, inside `/api/v1`:

- Remove a field from a response.
- Narrow a field's type (e.g. a nullable field becoming non-nullable would be fine; the reverse — or widening `string` to `string | number` — is not).
- Add a *required* request field.
- Change the HTTP status code an operation returns for a given outcome.
- Change which `error.code` an existing failure condition returns.

If your integration only ever reads fields it knows about and tolerates new ones showing up, it will keep working on `/api/v1` indefinitely — that's the point of the rule.

## What counts as breaking

Anything in the "never" list above is breaking, and it never lands inside `/api/v1`. A breaking change instead means `/api/v2`, announced at least **180 days** before `/api/v1` begins its own deprecation. Both versions run side by side for the whole notice period — nothing is ever pulled out from under an integrator without warning.

## Deprecating an operation

A single operation can be deprecated inside `/api/v1` — still fully functional, but marked for eventual removal (typically because `/api/v2` or a newer operation replaces it). For its entire remaining life it carries three headers and `"deprecated": true` in the OpenAPI document:

```http
HTTP/1.1 200 OK
Deprecation: @1735689600
Sunset: Wed, 01 Jul 2026 00:00:00 GMT
Link: <https://docs.institflow.com/en/guides/versioning#deprecations>; rel="deprecation"
```

- `Deprecation` — when the deprecation started, as `@` followed by Unix seconds ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)). The example above, `@1735689600`, is `2025-01-01T00:00:00Z`.
- `Sunset` — the earliest date the operation may be removed, as an IMF-fixdate ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594)), always at least 180 days after `Deprecation`.
- `Link` — points back to this page's deprecations table below, so a client that only logs headers still has a way to find out why.

`replacedBy`, when there is a direct successor, and a short human-readable `note`, appear in the table below and in the generated API reference for that operation — never in the headers themselves, which stay small and machine-parseable.

## Rate-limit headers

Every response — deprecated or not, successful or not — carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`. Every `429 RATE_LIMITED` response — from the per-IP/per-user limiter that guards every route, or from a secret API key's own per-key budget — additionally carries `Retry-After`, in seconds, so a well-behaved client always knows exactly how long to wait without guessing from `RateLimit-Reset` alone.

> **Currently deprecated operations**
>
> None. No operation in the current API is deprecated.

## Changelog

Contract-level corrections and deprecations, newest first.

| Date | Change |
| --- | --- |
| 2026-09-07 | Documentation correction, not a breaking change: `nextCursor` was published in the OpenAPI document as `string[]`; the wire format was always `string \| null`. The spec now matches what the API has always sent — no client that reads `nextCursor` as a nullable string was ever wrong. |
