Skip to content

API stability, versioning, deprecation & sunset

This page is the stability contract for everything that programs against the control plane. It states what is stable, how breaking change is signalled, and how long a deprecated surface keeps working. The enforcement is in the codebase, not in prose: the deprecation table, the response headers, the OpenAPI markers and the window checks below are all driven from a single in-code declaration (core/api/stability.go), and a sunset scheduled earlier than the policy allows fails the build.

SurfaceVersioned byTier today
REST core contract — the paths in the served OpenAPI documentURL major (/v1/…)stable
gRPC mirror — ControlPlane in proto package olivares.api.v1proto package majorstable (frozen mirror)
Live-ingest / connector wire — proto package olivares.sdk.v1proto package major + plugin ProtocolVersionstable (frozen)
Connector SDK (Go) — modules sdk, sdk/plugin (author surface)module semver — tags sdk/v*, sdk/plugin/v* from the first public releasestable v1 (Go contract; wire row above)
Event bus contract (AsyncAPI 3.0) — its event types are also what the eventing platform delivers to external webhook subscriptions; the subscription-management routes are module routes (/v1/m/eventing/, in the beta document below), but each event type carries its own stability tier from the in-code cataloginfo.version (1.0.0-preview)beta (document); per-type tiers for event types
Module routes — /v1/m/<ns>/…, the product’s modules, in the beta module-route documentURL major (/v1/); reflected from the routes the modules registerbeta
Terraform providerits own semver (terraform-provider-v* tags)stable, MAJOR tracks API v1
Client SDKs (Go / Java / Python / TypeScript)their own semver; MAJOR tracks the API major from GAbeta (pre-1.0 packages)
Anything not listed — SCIM, federation, internalsout of contract

Tiers. A stable surface does not change incompatibly within its major version; removing or changing it requires the deprecation process below. A beta surface may still change shape, but gets the same signalling and a shorter window — the module routes (/v1/m/<ns>/…) are published as a separate beta document, kept distinct from the stable core contract so the stable surface stays identifiable. An out-of-contract surface (SCIM, federation, internals) carries no compatibility promise; its contracts live in the typed interfaces that ship with the product.

Every operation in both OpenAPI documents carries a machine-readable x-stability marker — stable in the core contract, beta in the module-route document — and each document links this page in info.x-stability-policy.

For a stable surface, all of these are breaking and gated on the process below:

  • removing or renaming a path, method, request field, response field or error code;
  • changing a field’s type or meaning, or making an optional request field required;
  • tightening authentication/authorization such that a previously valid call fails;
  • for gRPC/protobuf: anything buf breaking (FILE ruleset) rejects.

These are not breaking: adding endpoints, adding optional request parameters, adding response fields, adding new error codes for new failure modes, and adding response headers. Clients must tolerate unknown JSON fields.

  • REST is versioned in the URL: the entire stable contract lives under /v1/. An incompatible change ships under /v2/ and /v1/ enters deprecation — never an in-place break.
  • gRPC is versioned by proto package: olivares.api.v1 / olivares.sdk.v1. An incompatible change requires a new package major (…v2); both contracts are guarded by buf breaking against main (task proto:breaking).
  • The Terraform provider is released independently (terraform-provider-v* tags); its MAJOR tracks the API major it speaks.
  • Client SDKs embed API_VERSION (the contract major they were generated from) and SPEC_HASH (the exact OpenAPI snapshot) — APIVersion and SpecHash in Go; from GA their MAJOR tracks the API major.
  • The connector SDK (the Go contract third-party connectors build against) is versioned by per-module semver tags (sdk/vX.Y.Z, sdk/plugin/vX.Y.Z) and gated by the same buf breaking wall on its wire. Interfaces an author implements never gain methods within a major; new capability arrives as new optional interfaces. The full policy ships with the module (sdk/VERSIONING.md); the authoring lifecycle is in Build and ship a connector.

A deprecation is one declared entry in the in-code table plus a migration guide; everything else follows from it mechanically.

  1. Announce. The entry lands with its announcement date and the migration guide URL. From that moment every response of the deprecated route carries the RFC 9745 header and a link to the guide, and the OpenAPI operation gains deprecated: true, x-deprecated-at and x-migration-guide:

    Deprecation: @1780272000
    Link: <https://olivares.ai/docs/how-to/migrate-example/>; rel="deprecation"
  2. Schedule the sunset. When the retirement date is committed, responses add the RFC 8594 header (and the spec gains x-sunset-at):

    Sunset: Thu, 01 Jun 2028 00:00:00 GMT
    Link: <https://olivares.ai/docs/how-to/migrate-example/>; rel="sunset"
  3. Remove — at the earliest on the sunset date, normally with the next API major.

Minimum support windows (deprecation announcement → sunset):

TierMinimum window
stable24 months
beta12 months

These windows are enforced by tests against the declaration table: an entry whose sunset violates its tier’s window, or that points at a route that does not exist, does not build.

For gRPC, deprecation is expressed with the protobuf deprecated option (which surfaces in generated code) plus the same windows; the wire contracts are otherwise frozen and buf breaking rejects incompatible edits outright.

  • Terraform provider — emits a tflog WARN (method, endpoint, dates, guide) once per unique method and request path per run when a control-plane response carries a deprecation signal (a deprecated parameterized route warns once per resource it touches), and sends a versioned User-Agent so deprecated-client usage is attributable server-side.
  • Go SDK — surfaces a DeprecationNotice once per endpoint (default: an slog warning; override with WithDeprecationHandler). Deprecated operations carry Go // Deprecated: markers, so editors and staticcheck flag them at development time.
  • Python SDK — one DeprecationWarning per endpoint (or your on_deprecation callback); deprecated operations are marked in docstrings.
  • TypeScript SDK — one console.warn per endpoint (or your onDeprecation callback); deprecated operations carry @deprecated JSDoc.