> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenexio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Versioning

> Know what can change under your integration, how each contract is versioned, and how to verify a published contract.

Four things carry versions: the HTTP API, webhook payloads, engines and conversation instances. A fifth, the supported contract registry, is a published record of exact engine contracts that you can verify byte for byte.

| What                        | How it is versioned                         | Where you see it                                                       |
| --------------------------- | ------------------------------------------- | ---------------------------------------------------------------------- |
| HTTP API                    | Path prefix `/api/v1/`                      | Every route                                                            |
| Webhook payload             | A date, `2026-03-22` today                  | `webhook_version` in the body and the `X-Nexio-Webhook-Version` header |
| Engine                      | `major.minor` releases of its configuration | `engine_version` and `engine_config_version_hash` on a run             |
| Conversation instance       | Released versions of its configuration      | The instance's versions list                                           |
| Supported contract registry | Content-addressed files, append-only        | `/contracts/supported-versions/index.json`                             |

## The HTTP API

There is one API version, `v1`, and every public route is under `/api/v1/` except the unauthenticated `GET /health` and `GET /robots.txt`. Within `v1`, Nexio adds new optional response fields and new routes. Build clients that ignore response fields they do not recognize.

Requests are stricter than responses: run submissions and many write routes reject unknown fields with `400 invalid_request`. Send only documented fields.

The OpenAPI description of the API is published with these docs and reports `info.version: 1.0`.

## Webhook payloads

Every webhook carries its payload version in the `X-Nexio-Webhook-Version` header. A run webhook also carries it as `webhook_version` in the JSON body; a platform event delivered by an automation has only the header. The current version is `2026-03-22`. Check it before you parse the body. See [Webhooks](/api-reference/webhooks/overview).

## Engine versions

An engine's configuration is a mutable draft until you publish it as a release numbered `major.minor`. Releases never change. A run request can pin a release:

| Pin     | Meaning                                                                                                     |
| ------- | ----------------------------------------------------------------------------------------------------------- |
| omitted | The latest release, unless the engine's pin policy is `exact_required`, which refuses a request with no pin |
| `1.3`   | Exactly release 1.3                                                                                         |
| `1.x`   | The latest release with major version 1                                                                     |
| `draft` | The current unreleased configuration. Sandbox keys only                                                     |

A run records the release it used in `engine_version` (`draft` for a draft run) and the configuration it ran in `engine_config_version_hash`, so a result can be traced to the configuration that produced it. The run status response omits either field when the run has none. Some engines require an exact pin. See [Versions and releases](/engines/versions) for pin policies, what counts as a breaking change, and how publishing works.

## Conversation instance versions

A conversation instance is also configured as a draft and released as immutable versions, and a new turn runs the latest released version. A resumed turn keeps the version it started on. Publishing passes an evaluation gate. See [Versions and publish](/conversations/versions-and-publish).

## Supported contract registry

The registry publishes exact, immutable engine contracts: for each entry, the request schema, the response schema, and a fixture set with example requests and terminal outputs. Today it holds one engine, the fixture engine `generic_fixture_engine` (engine type `entity_analysis`), at versions 1.0, 1.1, 1.3 and 2.0. It is what [sandbox fixtures](/reference/sandbox-fixtures) run against, and it is the reference for contract tests of your client.

The files are served from this site:

| File                                                                                   | Purpose                                                                                                                                                              |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`/contracts/supported-versions/index.json`](/contracts/supported-versions/index.json) | The list of entries: `engine_kind`, `version`, `path` and `sha256` of each entry                                                                                     |
| `/contracts/supported-versions/entries/sha256-<hash>.json`                             | One entry: `runtime_engine_type`, `config_version_hash`, `released_at`, `changelog`, and the `path` and `sha256` of its request schema, response schema and fixtures |
| `/contracts/supported-versions/schemas/sha256-<hash>.json`                             | A request or response JSON Schema                                                                                                                                    |
| `/contracts/supported-versions/fixtures/sha256-<hash>.json`                            | Example requests and the output of each terminal state                                                                                                               |

Every file is named by the SHA-256 of its own bytes, and the file that points to it records the same hash. The registry is append-only: an entry is never edited, only added.

### Verify a contract

<Steps>
  <Step title="Download the index and pick an entry">
    <CodeGroup>
      ```bash curl theme={null}
      curl -s https://docs.usenexio.com/contracts/supported-versions/index.json
      ```

      ```python Python theme={null}
      import requests

      index = requests.get(
          "https://docs.usenexio.com/contracts/supported-versions/index.json",
          timeout=30,
      ).json()
      print(index)
      ```

      ```typescript TypeScript theme={null}
      const resp = await fetch(
        "https://docs.usenexio.com/contracts/supported-versions/index.json",
      );
      if (!resp.ok) throw new Error(`${resp.status} ${await resp.text()}`);
      console.log(await resp.json());
      ```
    </CodeGroup>

    Each entry has a `path` and a `sha256`.
  </Step>

  <Step title="Download the entry and hash it">
    <CodeGroup>
      ```bash curl theme={null}
      curl -s -o entry.json \
        https://docs.usenexio.com/contracts/supported-versions/entries/sha256-5513c93b72bd6d03be71f14ca5e0bb193fac8cbdcd29eec67d00a3cd6a9c0b33.json
      shasum -a 256 entry.json
      ```

      ```python Python theme={null}
      import hashlib
      import requests

      url = (
          "https://docs.usenexio.com/contracts/supported-versions/entries/"
          "sha256-5513c93b72bd6d03be71f14ca5e0bb193fac8cbdcd29eec67d00a3cd6a9c0b33.json"
      )
      body = requests.get(url, timeout=30).content
      print(hashlib.sha256(body).hexdigest())
      ```

      ```typescript TypeScript theme={null}
      import { createHash } from "node:crypto";

      const url =
        "https://docs.usenexio.com/contracts/supported-versions/entries/" +
        "sha256-5513c93b72bd6d03be71f14ca5e0bb193fac8cbdcd29eec67d00a3cd6a9c0b33.json";
      const resp = await fetch(url);
      if (!resp.ok) throw new Error(`${resp.status} ${await resp.text()}`);
      const body = Buffer.from(await resp.arrayBuffer());
      console.log(createHash("sha256").update(body).digest("hex"));
      ```
    </CodeGroup>

    The output must equal the `sha256` in the index and the hash in the file name, here `5513c93b72bd6d03be71f14ca5e0bb193fac8cbdcd29eec67d00a3cd6a9c0b33`.
  </Step>

  <Step title="Verify the files the entry points to">
    Repeat for `request_schema`, `response_schema` and `fixtures` in the entry. Each file's SHA-256 must equal the `sha256` recorded beside its `path`.
  </Step>

  <Step title="Match a run to the entry">
    A run of that release reports `engine_version` equal to the entry's `version` and `engine_config_version_hash` equal to its `config_version_hash`. For version 1.0 that hash is `f96a86a609a2b1ea`.
  </Step>
</Steps>

Hash the bytes exactly as downloaded. Reformatting the JSON changes the hash.
