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

# App compatibility

> What your app declares compatibility against, what the Marketplace checks when it admits a release, and how each version moves.

Your app declares compatibility against the **contracts it consumes** — not
against a Voyant version, an image tag, or an npm package range.

That distinction matters because a Voyant operator runs as an image that
updates fleet-wide and automatically. There is no version number for you to
track, and there is nothing you can pin that would stop an operator upgrading.
What you pin instead are the individual contracts your app talks to, each of
which moves on its own schedule and gives you notice before it breaks.

## The axes

Every admitted release carries a compatibility block:

```json theme={null}
{
  "schemaVersion": "voyant.marketplace-release-compatibility.v1",
  "appApiVersions":         ["2026-07-01"],
  "manifestSchemaVersions": ["voyant.app-manifest.v1"],
  "adminExtensionVersions": ["1"],
  "eventSchemaVersions":    ["voyant.event-catalog.v1"],
  "artifactFormatVersions": ["voyant.marketplace-release-envelope.v1"],
  "runtimeVersions":        ["0.62.3"]
}
```

| Axis                     | Versioned as      | What it covers                                                    | You declare it if…               |
| ------------------------ | ----------------- | ----------------------------------------------------------------- | -------------------------------- |
| `appApiVersions`         | a date            | the [`/v1/app/*` API](/docs/api-specs/platform-app) your backend calls | your app has a backend           |
| `manifestSchemaVersions` | schema id         | the manifest you authored                                         | always                           |
| `adminExtensionVersions` | integer major     | the admin UI extension protocol your frame speaks                 | your app renders in the admin    |
| `eventSchemaVersions`    | schema id         | the webhook events you receive                                    | you subscribe to events          |
| `artifactFormatVersions` | schema id         | the release envelope                                              | always — the Marketplace sets it |
| `runtimeVersions`        | Framework version | the runtime that reviewed your release                            | never — see below                |

### `runtimeVersions` is a record, not something you pin

It captures the Framework version your release was reviewed against, for
provenance. **It does not gate anything**, and you should not reason about it.

It cannot: the Framework version moves with every Voyant release, and you have
no way to tell which of those releases touched anything your app depends on.
Treating it as a compatibility axis would mean every Voyant release
invalidating every app admitted before it — which is precisely why it does not.

### Why the app API is a date

Your backend calls `/v1/app/*` over HTTPS and installs no Voyant packages, so a
package version range would describe nothing you actually have. A date does.
The runtime serves several dated versions at once, so a new one appearing never
breaks you; you move when you choose to.

Send the version you built against:

```http theme={null}
GET /v1/app/self
Authorization: Bearer <installation access token>
voyant-app-api-version: 2026-07-01
```

### Why the extension protocol is a major

Your manifest declares a **range** against the admin UI extension API
(`"^1"`, `"1.x"`, or an exact `"1.2.3"`), which the admin evaluates when it
renders your frame. The compatibility axis is coarser — just the major —
because that is what a breaking protocol change moves.

## Where compatibility is checked

**At Marketplace admission.** Your release is reviewed against a pinned
`sourceRepository` and `sourceRevision`, and the compatibility block is recorded
with it. An operator installing your app gets a release already known to work
against the contracts their runtime serves.

Nothing is resolved at install time and nothing is negotiated at render time.
If an axis you declared is no longer served, the release stops being offered —
it does not fail halfway through an install, and it never blocks the operator's
own upgrade.

## When an axis moves

A breaking change to any axis ships as a **new version alongside the old one**.
The old version keeps being served while it retires, and the retirement window
is announced in the release notes — never in a way that requires you to redeploy
on Voyant's schedule.

To adopt a new version: build against it, submit a new release declaring it, and
the Marketplace admits it as a separate version of your app. Operators move when
they update.
