Versioning and deprecation
What you can build against, what can change under you, and how much warning you get before anything is removed.
Versioning
The major version is in the URL path. There is no version header, and no date-pinned version to choose.
https://api.stack0.dev/v1/mail/emails
Within v1, only additive changes ship. A breaking change requires a new major version at a new path prefix, and both run side by side through the deprecation window below.
What can change within v1
Ships any time
- A new endpoint.
- A new optional request parameter or body field.
- A new field in a response body.
- A new value in an enum that only appears in a response.
- A new error code, alongside the existing HTTP status.
- A new response header.
Needs a new version
- Removing an endpoint, a field, or a parameter.
- Renaming an endpoint, a field, or a parameter.
- Narrowing a type, or making an optional request field required.
- Changing the HTTP status an existing condition returns.
- Changing the meaning of an existing field.
- Removing a value from an enum that appears in a request.
Because new response fields can appear at any time, parse responses tolerantly: ignore fields you do not recognize rather than rejecting the payload.
How deprecation works
1. Announced
The endpoint is marked deprecated in the OpenAPI document and in these docs, and the replacement is named. Behaviour does not change.
2. Signalled on the wire
Every response to the endpoint carries a Deprecation header, a Link to the migration note, and a Sunset header with the removal date.
3. Sunset
On the date in the Sunset header, the endpoint stops responding and returns 410 Gone. The replacement stays available.
Notice period
| What is being removed | Minimum notice |
|---|---|
| A whole major version, such as v1 | 12 months |
| A stable endpoint or field | 12 months |
| An endpoint documented as beta or preview | 30 days |
| A change forced by a security issue or a legal obligation | As much as is safe; announced by email to affected accounts |
Headers on a deprecated endpoint
You do not have to read a changelog to notice a deprecation. It is on every response.
HTTP/1.1 200 OKDeprecation: @1780272000Sunset: Wed, 01 Jul 2026 00:00:00 GMTLink: <https://www.stack0.dev/docs/versioning>; rel="deprecation"; type="text/html"Link: <https://www.stack0.dev/docs/versioning>; rel="sunset"; type="text/html"
| Header | Specification | Meaning |
|---|---|---|
| Deprecation | RFC 9745 | When the endpoint was deprecated, as a structured-field Date: @ followed by Unix seconds. |
| Sunset | RFC 8594 | When it stops responding, as an HTTP-date. Never earlier than the Deprecation date. |
| Link; rel="deprecation" | RFC 9745 | The migration note for this endpoint. |
| Link; rel="sunset" | RFC 8594 | The same note, at the relation the sunset specification registers. |
Detecting it from code
Log the header once per deployment and you will hear about a deprecation the day it is announced, not the day the endpoint stops working.
const response = await fetch("https://api.stack0.dev/v1/mail/emails", {headers: { Authorization: `Bearer ${process.env.STACK0_API_KEY}` },});const deprecation = response.headers.get("Deprecation");if (deprecation) {// Structured-field Date: "@1780272000"const deprecatedAt = new Date(Number(deprecation.slice(1)) * 1000);const sunset = response.headers.get("Sunset");console.warn(`Stack0 endpoint deprecated ${deprecatedAt.toISOString()}` +(sunset ? `, removed ${sunset}` : "") +`. See ${response.headers.get("Link")}`,);}
Currently deprecated
Nothing. Every endpoint in v1 is current. When that changes, the endpoint will be listed here and will carry the headers above.
SDK versioning
The client libraries follow semantic versioning independently of the API version. A major SDK release can change the client surface without the API changing, and a new API version does not force a major SDK release. Pin the SDK the way you pin any other dependency.
The machine-readable contract is at /openapi.json. Related: Rate limits and Error handling.