docs/experimental-api-endpoints.md
Developer guide. For how the mechanism is built, see experimental-api-endpoints-plan.md.
Experimental endpoints live under /api/experimental/... and carry an explicit
stability contract:
Endpoints under
/api/experimental/...are unstable. They may change shape or be removed in any release — without notice and without a deprecation cycle. No semver promise attaches to this namespace.
Compare with /api/v1/..., which is the official, stable surface: its endpoints follow
semver, breaking changes require a major-version bump, and removals go through a
deprecation cycle.
We sometimes need to ship something to production before its API shape has settled:
Without an experimental lane, the only choices are bad ones: either freeze a design we
are not confident in onto /v1 (and then carry it forever, or break it with a major
bump), or keep the feature out of production until the API is perfect. Experimental
endpoints give a third path — ship now, iterate freely, commit later.
Use an experimental endpoint when:
/v1 for now and fix it later."Do NOT use an experimental endpoint when:
/v1.experimental as a permanent home to avoid the discipline of
a stable API. It is a staging area, not a dumping ground (see below)./v1 once its contract stabilizes, or be removed if it does not
pan out. An endpoint that sits in experimental indefinitely is a smell — it means a
decision is overdue.x-experimental: true — that is the supported signal to detect and surface in client
code. Responses also carry a Warning: 299 ... header, kept only for legacy tooling
that still reads it: RFC 9111 obsoletes the Warning header and its warn codes, so do
not build new client logic on it.ExperimentalEndpoints type, not in the main Endpoints union, so the stable SDK
surface stays honest. Consumers import them deliberately..get() / .post() / .put() /
.delete() and AJV validators. Do not use .addRoute(): it is already deprecated
across the whole API, and a namespace created to iterate on new contracts is the last
place that should add to the legacy path. stabilizes
experimental ───────────────▶ v1 (official, semver-stable)
(/api/experimental/x) (/api/v1/x)
│
│ does not pan out
▼
removed (no deprecation cycle needed)
Elevating to /v1:
/v1: register it on API.v1 and declare its types in the
appropriate *Endpoints type that is part of the Endpoints union./v1 path for a
transition window so existing callers are not broken on the day of promotion.Removing an experimental endpoint needs no deprecation cycle — that freedom is the whole point of the namespace. Still, log the removal and give a heads-up to any known consumers as a courtesy.
*Endpoints type, not leaving a copy behind. Nothing enforces this — the
/experimental/ path prefix keeps the two unions from overlapping in practice.Endpoints union, which experimental paths are
deliberately kept out of, so they do not appear in public API docs. This is
by design: an unstable surface should not be advertised as part of the
documented contract. The runtime x-experimental / Warning headers and
this guide are how the namespace is surfaced instead.version=experimental, so real usage can
inform whether an endpoint is ready to graduate to /v1 or should be removed.Experimental endpoints let you ship an API to production while its shape is still in
flux, without locking yourself into semver. They are a staging area, not a permanent
home: every one is expected to graduate to /v1 or be removed. If you need stability
guarantees, use /v1. If a third party will depend on it, use /v1.