Skip to content

API versioning

Sencai’s backend API is versioned under the /api/v1/ namespace. This page explains what counts as a breaking change, how long deprecated paths stay alive, and how to migrate existing integrations.

PathStatusNotes
/api/v1/*Current, recommendedCanonical versioned path. Identical behaviour to /api/* today — it is a transparent alias in front of the same routes/controllers, not a separate implementation.
/api/*Deprecated, still supportedKept as a backward-compatible alias so existing integrations are not broken. Every response carries a Warning: 299 header. Not redirected (no 301) and not removed — this is additive.

Every response from either path carries:

X-API-Version: 1
Deprecation: false

Deprecation: false refers to the current API version (v1) itself — v1 is not deprecated. Only the un-versioned /api/* path is deprecated in favour of /api/v1/*, which is why the Warning header is scoped to that alias specifically.

Example response from the deprecated alias:

HTTP/1.1 200 OK
X-API-Version: 1
Deprecation: false
Warning: 299 - "Deprecated path /api, use /api/v1"
Sunset: Wed, 01 Oct 2026 00:00:00 GMT

The same pattern applies to the API Manager service, which issues Strapi API tokens to other microservices:

PathStatus
POST /v1/token/:service, /v1/admin/*Current, recommended
/api/token/:service, /api/admin/*Deprecated alias — still works, carries a Sunset header

Deprecation-header behaviour is not identical between the two services. Strapi’s legacy alias middleware sends X-API-Version / Deprecation / Warning / Sunset only — it does not send a Link header. API Manager’s versioning middleware, when a deprecated version policy applies, additionally sends a Link: <migration_guide_url>; rel="deprecation" header pointing at the migration guide, sourced from the matching api-version-policy record. If you build tooling that parses deprecation headers generically across both services, do not assume a Link header is present on every deprecated response — check per-service.

Non-breaking (safe to ship without a version bump):

  • Adding a new optional field to a response body
  • Adding a new endpoint
  • Adding a new optional query parameter
  • Relaxing a validation rule (accepting previously-rejected input)
  • Adding new enum values to a field that is documented as “open” / extensible

Breaking (requires a new version, e.g. /api/v2/):

  • Removing or renaming a response field
  • Removing or renaming an endpoint
  • Changing a field’s type (e.g. stringnumber)
  • Making a previously-optional request field required
  • Changing the meaning/semantics of an existing field
  • Changing authentication/authorization requirements on an existing endpoint
  • Changing default pagination/sort behaviour

If a change is breaking, it ships under a new version prefix (/api/v2/) while /api/v1/ continues to serve the old behaviour until its own sunset date — the same policy documented here applies to every future version.

  • The un-versioned /api/* alias is deprecated but has no fixed removal date announced yet — it is tracked via the Sunset response header, which is computed from the SUNSET_DATE environment variable (ISO date, e.g. 2026-10-01) on both the Strapi backend and API Manager.

  • If SUNSET_DATE is not configured, it defaults to 90 days from process start — i.e. the minimum guaranteed notice period before an alias could be sunset is 90 days.

  • Reaching the Sunset date does not mean the alias stops working automatically — sunset dates are operational targets, communicated in advance via this header, release notes, and a direct email notification.

  • The email notification is sent automatically the moment an admin sets or changes a policy’s deprecated_at or sunset_at date (not only once the date has already passed) — every active Owner/Admin across every organisation on the platform receives it, including the effective date and a link to the migration guide when one is configured. This is a best-effort side channel on top of the Sunset/Warning headers, not a replacement for checking them — a failed email dispatch never blocks the policy change itself.

  • Organisations with a customer webhook subscribed to api.version.deprecated or api.version.sunset also receive the same lifecycle change over that channel.

  • Actual removal is a deliberate, separately-announced change.

  • Check the live Sunset value at any time:

    Terminal window
    curl -sI https://api.sencai.space/api/organisations | grep -i sunset

Migrating from /api/ to /api/v1/ requires no functional changes — only a base URL update:

  1. Find every place your integration builds a request URL against the Strapi backend (https://api.sencai.space/api/... or the local dev equivalent).
  2. Replace the /api/ prefix with /api/v1/ — request/response shapes, authentication (Authorization: Bearer <JWT>), and status codes are unchanged.
  3. Re-run your test suite / smoke tests against /api/v1/* to confirm parity.
  4. Once migrated, you can safely ignore the Warning/Sunset headers, since your integration no longer hits the deprecated alias.

Example:

GET https://api.sencai.space/api/organisations
GET https://api.sencai.space/api/v1/organisations

For services using API Manager to fetch Strapi tokens:

GET http://api-manager:3200/api/token/git-connector
GET http://api-manager:3200/v1/token/git-connector

No other change is required — the /api/v1/* namespace is a transparent alias, not a rewrite of the underlying implementation, so behaviour is identical to what /api/* already returns today.

This versioning scheme was implemented as F3.API.01:

  • sencai.space: src/middlewares/api-version.ts (+ src/middlewares/__tests__/api-version.test.ts)
  • api-manager: src/middleware/api-versioning.ts