Back to Rocket Chat

Experimental REST API endpoints

docs/experimental-api-endpoints.md

8.8.05.8 KB
Original Source

Experimental REST API endpoints

Developer guide. For how the mechanism is built, see experimental-api-endpoints-plan.md.

What they are

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.

Why they exist

We sometimes need to ship something to production before its API shape has settled:

  • A new feature whose request/response contract is still being learned from real usage.
  • An endpoint built for a specific client (e.g. our own UI) where we are not yet ready to commit to it as a public, supported interface.
  • Something we want behind a clear "use at your own risk" sign while it matures.

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.

Should I use one? — decision guide

Use an experimental endpoint when:

  • The request/response shape is likely to change as the feature matures.
  • You want production traffic / real feedback before committing to a contract.
  • The consumer is internal or opted-in, and can tolerate breaking changes between releases.
  • You would otherwise be tempted to "just put it on /v1 for now and fix it later."

Do NOT use an experimental endpoint when:

  • The endpoint is meant for third-party integrators who expect stability. They should not have to track breakage release-to-release.
  • The contract is already well understood and unlikely to change — put it on /v1.
  • You are tempted to use experimental as a permanent home to avoid the discipline of a stable API. It is a staging area, not a dumping ground (see below).

Expectations if you publish one

  • It is not forever. Every experimental endpoint is expected to either be elevated to /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.
  • Callers are warned at runtime. Every experimental response carries 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.
  • Typed clients must opt in. Experimental endpoints are declared in a separate ExperimentalEndpoints type, not in the main Endpoints union, so the stable SDK surface stays honest. Consumers import them deliberately.
  • Use the typed API only. Register with .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.

Lifecycle: experimental → official

text
                 stabilizes
   experimental ───────────────▶  v1 (official, semver-stable)
   (/api/experimental/x)           (/api/v1/x)
        │
        │ does not pan out
        ▼
     removed (no deprecation cycle needed)

Elevating to /v1:

  1. Confirm the contract is stable and you are ready to support it under semver.
  2. Add the endpoint under /v1: register it on API.v1 and declare its types in the appropriate *Endpoints type that is part of the Endpoints union.
  3. Optionally keep the experimental path forwarding to the new /v1 path for a transition window so existing callers are not broken on the day of promotion.
  4. Remove the experimental declaration once the transition window closes.

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.

Guardrails & tooling

  • No path lives in both unions. A duplicate key would silently attach a semver obligation to a path advertised as unstable, so promotion means moving the declaration to a stable *Endpoints type, not leaving a copy behind. Nothing enforces this — the /experimental/ path prefix keeps the two unions from overlapping in practice.
  • Generated API docs intentionally skip experimental endpoints. OpenAPI / doc generation scans the 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.
  • Metrics are the promotion signal. Experimental traffic is recorded in the REST API Prometheus metrics under version=experimental, so real usage can inform whether an endpoint is ready to graduate to /v1 or should be removed.

TL;DR

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.