API Versioning
The IdentityMesh Admin API has two path prefixes that return the same responses:
| Prefix | Status | Example |
|---|---|---|
/api/v1/ | Stable — forward-compatible | GET /api/v1/connectors |
/api/ | Legacy — in place for the Angular UI, the relay agent, and pre-v1 integrations. Kept working indefinitely for now. | GET /api/connectors |
A path-rewrite middleware strips the /v1/ segment before endpoint
routing, so both forms resolve to the same handler and produce the
same response — bodies, status codes, headers, rate-limiting
partition keys, admin-audit rows. There is no separate registration
for the v1 prefix; one handler serves both.
When to use which prefix
- External clients, CLI tools, SDK consumers, scripts that will
outlive this major version: use
/api/v1/. The guarantee is that a future v2 will coexist under/api/v2/rather than redefining/api/v1/semantics. - In-repo callers that redeploy with the service (the Angular
UI, relay agents, DbSetup, installer scripts): either prefix is
fine. The plain
/api/form is currently used and will keep working; new call sites should prefer/api/v1/so a grep for “legacy API path” finds a clean list when v2 ships.
What v1 is committed to
While the v1 designation is in effect, we treat these as breaking
changes requiring a v2 (i.e. a second prefix plus a deprecation
window on v1):
- Removing an endpoint, or changing its HTTP method.
- Removing a field from a response body, or renaming one.
- Tightening an input contract in a way that rejects previously accepted payloads (e.g. a new required field on a request DTO, or a narrower max length).
- Changing an authorization policy on an endpoint from a
.readto a.writescope.
These are fair game within v1 and don’t require a new prefix:
- Adding a new endpoint.
- Adding an optional response field, provided existing clients that ignore unknown keys keep working.
- Adding an optional request field with a sensible default.
- Widening an input contract (e.g. accepting an additional value on an enum-like string).
- Changing internal error shape within the
ProblemDetailsenvelope — callers are expected to key offstatusandtype, not the free-textdetail.
Operational notes
- Request logging (
UseSerilogRequestLogging) captures the path after rewrite, so/api/connectorsis what ends up in the log stream regardless of which prefix the caller used. The original path is stored onHttpContext.Items["Original-Path"]for any downstream piece that needs to tell v1 adoption from legacy traffic. IM_AdminAudit.Actionrecords the route template, which is the same for both prefixes. If you want to answer “how many callers are still on the legacy prefix”, readHttpContext.Itemsin a middleware positioned after the rewrite and before the audit write, or add a response header.- Swagger UI (Development only) shows the canonical
/api/...paths. Both prefixes serve valid requests against those routes. - Health probes (
/health/live,/health/ready) and SignalR hubs (/hubs/*) are outside the/api/namespace and are not affected by this rewrite.
Future v2
When a breaking change is ready:
- Register the new route shape under
/api/v2/...(either with a second set ofMapXxxcalls, or by route-grouping the v2 handlers in aMapGroup("/api/v2")). - Keep the v1 handlers in place — don’t remove them in the same release that ships v2. A typical deprecation window is two minor versions; operators get at least one release to migrate.
- Stamp a
Deprecationresponse header (RFC 9745) on v1 calls during the window so callers discover the cutover without reading changelogs. - At the end of the deprecation window, delete v1 handlers and retire the path-rewrite middleware’s v1-to-legacy mapping in the same release.
Related
authentication.md— auth schemes are orthogonal to the path prefix;/api/v1/...and/api/...both go through the sameSmartAuth/ Negotiate / JwtBearer path.