VitrinaAPI

Track versions and deprecations

What each operation promises at its tier, and how long a deprecated one lasts.

/api/v1 is the only version you'll see in the URL. There's no date header, no ?version=, and no two parallel routes for the same thing. What this API versions is each operation's tier.

The three tiers

TierWhat it promisesWhere you see it
estableA deprecated operation keeps answering for 90 days, with Deprecation and Sunset headers from the first day onwards. It leaves estable only by being deprecated: it never drops back to beta.In the Reference, next to the operation.
betaPublished and usable, and it can change at any time. Every breaking change is written down in the changelog, and we email whoever called that operation in the last 30 days.With the Beta label in the Reference.
internaNot part of the public API. It is not documented, it is not in the openapi.json we publish, and it can disappear without notice.You do not see it: that is why it is not here.

The tier travels inside the OpenAPI document itself, as the x-vitrina-tier extension.

No operation is estable today

Everything published is beta. Build on it anyway: beta doesn't mean unstable. It means notice of a change reaches you through the changelog and by email, rather than reaching you with 90 days on the clock.

The register of estable operations

An operation moves up with a date and a reason, and this table is that register. While it's empty, nothing has promised the 90 days. The promotion comes once an outside integrator depends on the operation, or once it has survived a whole stage unchanged.

DateOperationsPromoted byWhy
—none yet; everything published is beta——

What happens when something is deprecated

An estable operation on its way out answers like this, from the day it is announced until its last one:

HTTP/1.1 200 OK
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT

Sunset is the date it stops answering, 90 days later at the earliest. Deprecation says the clock has started. Both are standard headers (RFC 8594 and RFC 9745), so most HTTP clients already know how to read them. If your integration logs a warning when they show up, you'll find out without reading any email.

While that period runs the operation works exactly as before. It doesn't degrade, it doesn't answer more slowly, and it doesn't return fewer fields.

What counts as a breaking change

These break:

  • removing an operation, a response field or an enum value;
  • making a parameter required that was not;
  • changing a field's type, or the meaning of one that already existed;
  • changing the error code a case fails with.

These don't break, and can happen any day at any tier:

  • adding an operation, an optional response field or a new enum value;
  • adding an optional parameter;
  • changing the text of message in an error.

For example, livemode was added to API keys and to every webhook event to tell a sandbox apart from a real workspace (Sandbox). It is one more optional field, so it doesn't break anything.

To see the difference between code and message, both of these failures come back from the same API:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing bearer token",
    "requestId": "2bd4e229-842e-4e82-a0f2-4ff028c1e1fb"
  }
}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Missing required scope: webhooks:write",
    "requestId": "f7614421-ef08-4638-8524-2af320d1ab3c"
  }
}

code is the field you branch on, and changing it breaks your integration. message is written for whoever reads the logs, stays in English, and can be reworded any day (Errors).

Trap

A new field should not break you

We add fields to responses without notice, at every tier. A strict parser that rejects what it does not recognise falls over on that. Ignore the fields you did not expect instead of failing.

The first breaking change: the solicitudes:* scopes

POST /api-keys and POST /personal-tokens no longer accept solicitudes:read or solicitudes:write in scopes[]. The feature those permissions governed was retired from the product, so there is no replacement scope to ask for instead. Credentials that already exist keep working; what fails with a 400 is asking for those two values when minting a new one. Drop them from the call and the rest of the scopes are minted exactly as before.

How you find out

  • A change in beta is flagged as breaking in the changelog, generated from the spec. If it breaks, an email goes to whoever called that operation in the last 30 days. That list comes from each credential's real usage: if your key called the operation, you hear about it.
  • A change in estable arrives with Deprecation and Sunset in the response itself, the channel that doesn't depend on somebody reading the right email.
  • The openapi.json we publish is the same one the backend generates, so the most reliable way to know what changed is to diff it between two versions.

On this page