API versioning
Every Xident API version is a date, like 2026-08-13. Each of your
websites is pinned to one, and the shape of every response and every webhook
for that version never changes — not a renamed field, not a removed field, not
a changed type.
When we need to make a change that would break an existing integration, we publish a new dated version instead. Your pinned version keeps working exactly as it did, and you move when you choose to.
What counts as a breaking change
This distinction decides whether you need to do anything, so it is worth being precise about.
| Change | New version? | What you must do |
|---|---|---|
| A new optional response field | No | Nothing. New fields appear on every version. |
A new value in an existing set (a new reason, a new verification_type) | No | Handle a default. We document every set as open for exactly this reason. |
| A new optional request parameter | No | Nothing. |
| A field renamed or removed | Yes | Nothing until you move versions. |
| A field's type or meaning changes | Yes | Nothing until you move versions. |
| A request parameter becomes required | Yes | Nothing until you move versions. |
Your side of this: your integration must tolerate fields it does not recognise, and must handle an unrecognised value in any set by falling through to a default. Every official SDK already does both. If you parse our JSON strictly and reject unknown keys, an additive change will break you and a new version will not save you.
How your version is chosen
In order of precedence:
- The
X-API-Versionrequest header, if you send one. - The version pinned to the website in your dashboard, under Website Settings.
There is no "always use the latest" setting, on purpose. A website that floated onto each new version would suffer precisely the breakage this system exists to prevent.
curl https://api.xident.io/verify/v1/result/xtk_… \
-H "Authorization: Bearer sk_live_…" \
-H "X-API-Version: 2026-08-13"
The header is how you trial a new version before committing to it: send it on a few calls, confirm your code handles the new shape, then change the pin. It works in both directions — you can also send an older version than your pin.
latest is deliberately not accepted as a value. An alias
that floats would silently change your payloads the day we ship, which is the one thing
this whole mechanism is for.
Every response tells you its version
On both success and error, in a header and in the body:
{
"success": true,
"data": { "token": "xtk_…", "status": "success", "verified": true },
"meta": {
"request_id": "req_…",
"timestamp": "2026-08-13T10:02:31Z",
"api_version": "2026-08-13"
}
}
The same value comes back as an X-API-Version response header. When you
are debugging a shape that is not what you expected, this is the first thing to check.
Invalid versions
A version we cannot serve is rejected outright rather than quietly rounded to something near it — because rounding means our next release silently changes your payload.
| Code | When |
|---|---|
INVALID_API_VERSION | Not a YYYY-MM-DD date, or sent more than once |
UNKNOWN_API_VERSION | A valid date that is not a released version, or later than the newest |
API_VERSION_SUNSET | A version whose support has ended |
{
"success": false,
"error": {
"code": "UNKNOWN_API_VERSION",
"message": "X-API-Version 2026-09-01 is not a released API version",
"details": { "supported": ["2026-08-13"], "latest": "2026-08-13" }
},
"meta": { "request_id": "req_…", "timestamp": "2026-08-13T10:02:31Z" }
}
Webhooks
Each webhook payload carries the version that produced it in its api_version
field, and its data is byte-identical to what GET /result returns
at that version. A webhook endpoint uses its own pinned version if it has one, otherwise the
website's — so you can migrate one endpoint and verify it before moving the rest.
A redelivered webhook replays the original bytes, in the version that produced them. Changing your pin does not rewrite history.
Deprecation and support
A version is supported for at least 12 months after we deprecate it, and we do not deprecate a version until a newer one is available to move to.
A deprecated version keeps working and keeps serving its shape. What changes is that it starts telling you so, using the standard IETF headers (RFC 9745 and RFC 8594):
Deprecation: @1799971200
Sunset: Sat, 14 Aug 2027 00:00:00 GMT
Link: <https://docs.xident.io/api-versions#2026-08-13>; rel="deprecation"
Link: <https://docs.xident.io/api-versions>; rel="successor-version"
You will also see a notice in your dashboard. We will not move your pin for you, and we will not cut your integration off on a calendar date without talking to you first.
SDKs
Each official SDK sends the API version it was built against, so its response types always match the payload it receives. That means upgrading an SDK's major version is how you adopt a new API version — and an SDK patch or minor release never changes the shape you get.
Every SDK exposes an override if you want to pin explicitly:
| SDK | Override |
|---|---|
| Go | xident.WithAPIVersion("2026-08-13") |
| Node.js | new Xident(key, { apiVersion: '2026-08-13' }) |
| Python | Xident(api_key=…, api_version="2026-08-13") |
| PHP | new Config($key, apiVersion: '2026-08-13') |
Released versions and what changed in each are listed on the API versions page.