> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.truvo.com/guides/versioning/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`. > The path stays /v1. A breaking change ships only in a new dated revision, and the older revision keeps working for at least 6 months after notice.