Files
upstream-rocketchat/docs/experimental-api-endpoints.md
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

117 lines
5.8 KiB
Markdown

# Experimental REST API endpoints
> Developer guide. For how the mechanism is built, see
> [experimental-api-endpoints-plan.md](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`.