> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.truvo.com/llms.txt.

# Versions and changes

The Truvo API has one path, `/v1`, and its contract has revisions. A revision is a date. The first revision is `2026-09-26`, and the [changelog](/changelog) lists each revision.

## The Truvo-Version header

Every answer from `/v1` names its revision in the `Truvo-Version` header:

```http
Truvo-Version: 2026-09-26
```

An answer to a REST request names the revision that the request named in its own `Truvo-Version` header, or else the revision your integration pins. A few answers always use the current revision. See [Answers in the current revision](#answers-in-the-current-revision).

## Your pinned revision

Your integration pins one revision in each environment. A REST request without a `Truvo-Version` header gets its answer in that revision.

When Truvo can store a pin, the first REST request that Truvo admits without a `Truvo-Version` header stores it, even if it then answers with an error such as a `422`:

* **Sandbox:** it pins the current revision. In the sandbox, your first webhook event also stores the pin if no request stored it first.
* **Live:** it pins the revision your integration pins in the sandbox, so going live keeps the revision you built against. If the sandbox has no pin, live pins the current revision.

A refusal that Truvo sends before it picks a revision stores no pin: a `401`, a `413`, a `429` for failed authentication, a `400` `invalid_version`, or a `503` when Truvo can't read your pin. Until Truvo can store a pin, a request without the header uses the current revision, and nothing is stored.

### Read your pin

Send a REST request without a `Truvo-Version` header, such as `GET /v1/capabilities`, and read the `Truvo-Version` header of the answer:

```bash
curl -s -D - -o /dev/null https://api.truvo.com/v1/capabilities \
  -H "Authorization: Bearer $TRUVO_API_KEY" | grep -i '^truvo-version:'
```

If your integration has no pin yet in that environment, this request stores one when Truvo can. Answers from `/v1/mcp`, `404` answers, and the refusals above name the current revision, so they don't show your pin.

## Use another revision for one request

To use another revision that Truvo serves for one request, send its date in the `Truvo-Version` request header:

```bash
curl https://api.truvo.com/v1/capabilities \
  -H "Authorization: Bearer $TRUVO_API_KEY" \
  -H "Truvo-Version: 2026-09-26"
```

The answer uses that revision, and your pin doesn't change. A revision that Truvo doesn't serve gets `400` with the code `invalid_version`, and its field error lists the revisions that Truvo serves. Leave the header out to use your pin.

## The MCP server uses the current revision

`/v1/mcp` always answers in the current revision, and each answer names it in `Truvo-Version`. The MCP server doesn't use your pin or the `Truvo-Version` request header. Code that needs a stable contract should call the REST API.

When a new revision makes a breaking change to a tool's input, `/v1/mcp` keeps accepting the old input for the 6-month notice period. After that, it answers with a field error that names the new field.

## Webhook events

Each webhook event, test events included, carries your integration's pin in `schema_version`, whatever revision the request that caused it named. The event's fields follow that revision.

The event feed, `GET /v1/events`, gives its events in the revision of the feed answer. A feed read without a `Truvo-Version` header gets your pinned revision, as webhooks do.

## What counts as a breaking change

A breaking change ships only in a new revision. These changes are breaking:

* A removal or a rename.
* A new required input.
* Narrower bounds.
* A new type or unit.
* A new `null` in an output.
* A new meaning for a status, a permission, a default, an error, an order, a cursor, or a key.

Statuses are closed, so a new status value is a breaking change too.

## What can change in any revision

New fields, new error codes, new lines, new carriers, and new event types can arrive in any revision, so read answers with a reader that accepts them. These values can grow without a new revision: event `type`, `line`, `resource.type`, `carrier.id`, `error.code`, `review.reason`, and fee codes.

Ignore a field that you don't know. When you branch on one of these values, add a default branch for a value that you don't know:

```ts
switch (error.code) {
  case "needs_information":
    // Ask the person for the facts in error.field_errors.
    break;
  default:
    show(error.message, error.doc_url);
}
```

## How long a revision keeps working

A revision keeps working for at least 6 months after notice. The [changelog](/changelog) and an email announce each new revision and the end date of the revision before it.

## Answers in the current revision

A `404`, and a refusal that Truvo sends before it knows your revision, use the current revision whatever your pin. Those refusals are a `401`, a `413`, a `429` for failed authentication, a `400` `invalid_version`, and a `503` when Truvo can't read your pin. Each of these answers names the current revision in `Truvo-Version`.

These answers can't follow your pin, so the top level of the error envelope stays compatible from one revision to the next: `error.code`, `error.message`, `error.retry_action`, `error.doc_url`, and `request_id`.