Versioning and deprecation¶
/v1 is the public contract of the HodeiShield API and it is frozen: what is published
under /v1 does not change in an incompatible way. This page summarises the policy as the
OpenAPI document itself publishes it (GET /v1/openapi.json), which prevails if they differ.
How the version is identified¶
- The path carries the major version:
/v1/…. info.versionof the OpenAPI document is the contract revision, shaped like a date. The current one is2026-10-29. It moves when the published contract gains an addition or a correction.- The Contract changes section of the OpenAPI document lists every addition and correction,
newest first, with the revision it arrived in. To know whether a capability is present, check
whether the operation exists (for example
GET /v1listslistPortfolioTenantswhen the portfolio contract is available) instead of comparing version strings.
Recent contract changes¶
- 2026-10-29, addition: a warning when a filter value is not a published one.
severityonGET /v1/alerts,domainandstatusonGET /v1/risksandstatusonGET /v1/compliance/controlsmatch exactly, so a mistyped value (severity=critcal) used to return an empty list that looked like "no results". The request is still served as sent, with200and the same items: no parameter accepts less than before. When the value is outside the parameter'sx-hs-known-values, the response also carries the optional keymeta.warnings[](param,value,known) and theX-HS-Unknown-Filterheader, which names the parameter. With no unknown value, neither appears.categoryonGET /v1/vendorsis free text and is not checked. - 2026-10-27, correction: a scope needs its module active. A tenant API key or a portfolio
key used to be served on its scope alone, even when the tenant no longer had that scope's
module active. Every credential now gets
403witherror.details.codeMODULE_NOT_ENTITLEDin that case, and acompliance.<framework>scope also needs the framework activated for the tenant. A request that used to answer200now answers403. The same revision addsGET /v1/changes(scopechanges:read, read-only). - 2026-10-26, change:
user.emailneeds theemailscope.GET /v1/mereturnsuser.emailonly to an OAuth access token that carries the newemailscope, and omits it otherwise; the call is never refused for lacking it. Treatuser.emailas optional.
Compatible changes (happen inside /v1, without prior notice)¶
- a new operation;
- a new optional query parameter;
- a new field in a response object;
- a new value in a field documented as an open vocabulary (for example
status,severity,evidence_type); - a new
error.code, always with the HTTP status its siblings already use; - anything under
error.details, which is diagnostic and not part of the contract, excepterror.details.codeand the keys the error table of each operation names.
Write your client so these do not break it: ignore unknown fields, do not switch exhaustively
on an open vocabulary, and treat an unknown error.code as "an error of this HTTP status".
Incompatible changes (do not happen in /v1)¶
They go to a new version (/v2), and /v1 keeps being served:
- removing or renaming an operation, a parameter or a field;
- changing the type, the unit or the meaning of a field;
- making an optional parameter required, or accepting less than it accepted;
- changing which scope an operation requires;
- changing the HTTP status an existing
error.codeis sent with.
Unknown filter values¶
A filter with a published vocabulary (x-hs-known-values) does not refuse an unknown value: the
request answers 200 with whatever matches, which is almost always nothing. So that this is not
mistaken for "no results", the response carries meta.warnings[] with one
{ "param", "value", "known" } element per unknown filter, and the X-HS-Unknown-Filter header
with the parameter names separated by commas. It applies to alert severity, risk domain and
status, and compliance control status; the retired Spanish severity values raise no
warning. The obsolete Warning header is not used.
Retired values¶
A value retired from a parameter is listed in the OpenAPI document with the extension
x-hs-deprecated-values, and the date until which the server is documented to accept it in
x-hs-deprecated-values-sunset. There is one case today: the Spanish values of the alert
severity filter (critico, alto, medio, bajo) are documented as accepted until
2027-01-14; move to the English values (critical, high, medium, low, info)
before that date.
What is not part of the frozen contract¶
- Rate limits may be raised without notice. See Rate limits.
- Webhook signature headers have their own lifecycle: the legacy
X-HodeiShield-Signaturewill be retired. See Webhooks.
What this policy does not establish¶
The contract does not state how long /v1 will keep being served once a /v2 exists, nor a
notice period for retiring it. Do not read this page as a commitment on either.