# API Versioning and Deprecation Policy: [Organisation or API programme]

> Policy that tells API consumers what stays stable, what counts as a
> breaking change, how and when an API version or feature is deprecated
> and retired, and how they will be told and helped to migrate. Uses the
> `Deprecation` HTTP response header (RFC 9745) and the `Sunset` HTTP
> response header (RFC 8594). Replace every `[bracketed]` value and every
> row marked *Example*; the example timelines are reasonable defaults, not
> requirements.

<!--
Guidance: How to use this template
- Decide the promises in sections 2 and 3 with product, legal and support
  before publishing. Consumers will plan against them, so they are hard to
  weaken later.
- Publish the policy in the developer portal and link it from every API's
  documentation and changelog.
- Timelines must be realistic for the slowest consumer you care about
  (mobile apps in the field, partners with quarterly releases).
- Record exceptions in section 9; an exception that is not written down
  becomes the new policy.
-->

| Field | Value |
|---|---|
| Policy ID | [ARCH-POL-API-002] |
| Version | [0.1] |
| Status | [Draft / Approved / Superseded] |
| Owner | [@api-platform-lead] |
| Applies to | [Public, partner and internal HTTP APIs, GraphQL, gRPC and event contracts] |
| Approved by | [@cto], [@head-of-partnerships] |
| Effective from | YYYY-MM-DD |
| Next review | YYYY-MM-DD |

## 1. Purpose and scope

<!-- Guidance: Why the policy exists and which interfaces it covers. Name interfaces excluded from it (for example experimental or private APIs). -->

This policy lets consumers build on our APIs with confidence and lets our
teams evolve them without surprise breakage. It covers [all documented
APIs listed in the developer portal]. It does not cover [undocumented
endpoints, APIs marked alpha or beta, and internal APIs used by one team
only].

## 2. Versioning scheme

<!-- Guidance: One scheme per API style. State where the version appears and what increments it. -->

| API style | Version carrier | Increments when | Example |
|---|---|---|---|
| REST / HTTP | Major version in the URL path | A breaking change is released | `/v2/orders` |
| GraphQL | No versions; continuous evolution with field deprecation | n/a | `@deprecated(reason: "...")` |
| gRPC | Major version in the package name | A breaking change is released | `payments.ledger.v2` |
| Events | Version in the event type or schema subject | A breaking schema change | `order.placed.v2` |
| SDKs | Semantic Versioning 2.0.0 | Major for breaking, minor for features, patch for fixes | `4.2.1` |

Non-breaking changes are released continuously without a new major version
and are listed in the changelog.

## 3. Compatibility guarantees

<!-- Guidance: State exactly what consumers can rely on and what they must tolerate. The second list protects your ability to add features. -->

Within one major version we guarantee that we will not:

- remove or rename an operation, field, parameter or enum value;
- change the type, format or meaning of an existing field;
- make an optional request field required or add a required parameter;
- change authentication methods or reduce granted scopes;
- change success status codes or documented error types.

Consumers must tolerate, without breaking:

- new response fields and new optional request fields;
- new operations, endpoints and event types;
- new enum values in responses (treat unknown values as a documented default);
- changes to the order of JSON properties and of unsorted lists;
- new HTTP headers and new error `type` URIs within documented status codes.

## 4. What is a breaking change

<!-- Guidance: A clear test so every team classifies changes the same way. When unsure, treat the change as breaking. -->

| Change | Breaking? | Notes |
|---|---|---|
| Remove a field or endpoint | Yes | Requires deprecation process |
| Rename a field | Yes | Add new field, deprecate old one |
| Add a required request field | Yes | Make it optional with a default instead |
| Tighten validation (shorter max length, narrower range) | Yes | Unless fixing a security defect |
| Change default page size or sort order | Yes | |
| Add an optional field | No | |
| Add an enum value in a response | No, if clients were told to tolerate unknown values | Announce in changelog |
| Change error message text | No | Clients must use `type` and status, not text |
| Lower a rate limit | Yes | Treat as breaking unless needed for abuse protection |
| Security fix that changes behaviour | Exception | See section 9 |

## 5. Deprecation process and timelines

<!-- Guidance: Deprecation means "still works, but will stop". Retirement (sunset) is when it stops. Set minimum periods per audience. -->

| Stage | What happens | Public APIs | Partner APIs | Internal APIs |
|---|---|---|---|---|
| 1. Decision | Deprecation approved by API owner and product; migration path ready | Day 0 | Day 0 | Day 0 |
| 2. Announcement | Changelog, docs banner, email to registered consumers, `Deprecation` header turned on | Day 0 | Day 0 | Day 0 |
| 3. Deprecation period | Feature works unchanged; no new consumers allowed to onboard | [12 months] minimum | [9 months] | [3 months] |
| 4. Sunset date fixed | `Sunset` header added; reminders at 90, 30 and 7 days | At least [6 months] before | [3 months] before | [1 month] before |
| 5. Brownouts (optional) | Short planned outages to reveal remaining callers | Last [8 weeks] | Last [4 weeks] | Not used |
| 6. Retirement | Requests return `410 Gone` with problem details linking the migration guide | Sunset date | Sunset date | Sunset date |

The diagram shows the lifecycle states of an API version.

```mermaid
stateDiagram-v2
  [*] --> Beta
  Beta --> Active : general availability
  Active --> Deprecated : deprecation announced
  Deprecated --> Retired : sunset date reached
  Retired --> [*]
```

## 6. Deprecation and Sunset headers

<!-- Guidance: Machine-readable signals let consumers detect deprecations in logs and tests. Send them on every response from a deprecated resource. -->

- `Deprecation` (RFC 9745) carries the date the resource was or will be
  deprecated, as a structured-field date (a Unix timestamp prefixed with
  `@`). It does not change how the resource behaves.
- `Sunset` (RFC 8594) carries the HTTP date after which the resource is
  expected to stop responding.
- `Link` headers point to documentation: relation `deprecation` (RFC 9745)
  for the deprecation notice and relation `sunset` (RFC 8594) for the
  sunset policy.

```http
HTTP/1.1 200 OK
Content-Type: application/json
Deprecation: @1767225599
Sunset: Thu, 31 Dec 2026 23:59:59 GMT
Link: <https://developer.example.com/migrations/orders-v1-to-v2>; rel="deprecation"; type="text/html"
Link: <https://developer.example.com/policies/api-deprecation>; rel="sunset"; type="text/html"
```

For GraphQL use `@deprecated(reason: "...")` with the removal date in the
reason; for gRPC use `option deprecated = true` plus the changelog; for
events add a `deprecated` note in the schema registry and the AsyncAPI
document.

## 7. Communication

<!-- Guidance: Consumers must hear about a deprecation through channels they actually watch. Track who has been reached. -->

| Channel | When | Audience | Owner |
|---|---|---|---|
| Changelog and docs banner | Announcement day | All | API team |
| Email to registered app owners | Announcement, then 90, 30 and 7 days before sunset | Consumers with recorded traffic | Developer relations |
| Developer portal dashboard | Continuous | Consumers using deprecated features | API platform |
| Partner account managers | Announcement and monthly | Strategic partners | Partnerships |
| Status page | Brownouts and retirement day | All | Operations |

## 8. Migration support

<!-- Guidance: Deprecation without a migration path is a breaking change in disguise. -->

For every deprecation we provide:

- a migration guide with side-by-side request and response examples;
- a mapping table from old fields or operations to new ones;
- SDK releases supporting the new version before the announcement;
- a usage report per consumer (calls to deprecated features in the last 30 days);
- office hours or a support channel [#api-migrations] during the deprecation period.

| Deprecation | Replacement | Guide | Consumers remaining | Sunset |
|---|---|---|---|---|
| *Example:* `GET /v1/orders` | `GET /v2/orders` (cursor pagination) | [Link] | 14 of 230 | YYYY-MM-DD |
| [Feature] | [Replacement] | [Link] | [N of M] | YYYY-MM-DD |

## 9. Exceptions

<!-- Guidance: When the normal timeline can be shortened or extended, and who decides. Record each one. -->

The timeline can be shortened only for: (a) a security vulnerability that
cannot be fixed compatibly; (b) a legal or regulatory requirement; (c) a
dependency outside our control (for example a provider shutting down).
Shortened timelines need approval from [@cto] and direct contact with
every affected consumer. Extensions may be granted to named consumers by
[@api-owner] for at most [3 months].

| ID | API / feature | Exception | Reason | Approved by | Date |
|---|---|---|---|---|---|
| EXC-01 | *Example:* `/v1/tokens` | Sunset after 30 days | Token format vulnerable to replay | @cto | YYYY-MM-DD |
| [EXC-NN] | [Feature] | [Exception] | [Reason] | [@name] | YYYY-MM-DD |

## Standard and licence note

Based on common industry practice for API lifecycle policies. The header
mechanics follow RFC 9745 (The Deprecation HTTP Response Header Field,
March 2025) and RFC 8594 (The Sunset HTTP Header Field); version numbering
for SDKs follows Semantic Versioning 2.0.0. No text from those documents is
reproduced; consult them for normative requirements. Template licence: MIT.
