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

# Versions

> Agent versions — create a snapshot of every primitive, list and diff versions, promote one to live, and delete old ones

An [agent version](/concepts/releases-and-versions) is a numbered snapshot of which version of every primitive an [agent](/concepts/agents) runs. These routes create, list, inspect, diff, promote, and delete them; promoting is also the rollback path. The CLI twin is [`lua version`](/reference/cli/version).

*Verified against lua-cli 3.33.0.*

## Base URL and authentication

Every route checks an `agents` scope on the agent in the path: `agents:read` reads and diffs, `agents:write` creates, promotes, and patches the commit hash, and `agents:manage` deletes. The host, the bearer header, and the error envelope are on the [REST API overview](/reference/rest/overview).

## Agent version object

<ResponseField name="id" type="string">Record ID.</ResponseField>
<ResponseField name="agentId" type="string">The agent.</ResponseField>
<ResponseField name="baseAgentId" type="string">The agent's base ID.</ResponseField>
<ResponseField name="orgId" type="string">Owning organization.</ResponseField>
<ResponseField name="version" type="number">Version number, increasing per agent.</ResponseField>
<ResponseField name="status" type="string">`staged` (created, not live), `active` (live), `superseded` (was live), or `deleted` (soft-deleted).</ResponseField>

<ResponseField name="snapshot" type="object">
  The pinned primitive versions.

  <Expandable title="properties">
    <ResponseField name="skills" type="object[]">Each `{ skillId, versionId, version, name? }`.</ResponseField>
    <ResponseField name="webhooks" type="object[]">Each `{ webhookId, versionId, version, name? }`.</ResponseField>
    <ResponseField name="jobs" type="object[]">Each `{ jobId, versionId, version, name? }`.</ResponseField>
    <ResponseField name="preprocessors" type="object[]">Each `{ id, versionId, version, name? }`.</ResponseField>
    <ResponseField name="postprocessors" type="object[]">Each `{ id, versionId, version, name? }`.</ResponseField>
    <ResponseField name="triggers" type="object[]">Each `{ triggerId, versionId, version, name? }`. Absent on agent versions that predate triggers.</ResponseField>
    <ResponseField name="workflows" type="object[]">Each `{ workflowId, versionId, version, name? }`, the latest pushed version of each workflow. Present only when the agent has a pushed workflow.</ResponseField>
    <ResponseField name="mcpServers" type="object[]">Each `{ id, configHash, name?, config? }`. MCP servers are unversioned, so the agent version pins a hash of their configuration.</ResponseField>
    <ResponseField name="persona" type="object">`{ versionId, version?, contentHash? }`.</ResponseField>
    <ResponseField name="model" type="string">Model code.</ResponseField>
    <ResponseField name="voice" type="object">`{ voiceId, versionId, version, name? }`, the agent's default voice. Absent when the agent has no default voice.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="sourceManifestVersion" type="number">Source backup version the agent version was taken from, when given.</ResponseField>
<ResponseField name="message" type="string">Description given at creation.</ResponseField>
<ResponseField name="commitHash" type="string">Git commit associated with the version.</ResponseField>
<ResponseField name="createdBy" type="string">User ID of the creator.</ResponseField>
<ResponseField name="createdByEmail" type="string">Creator email; added on list and read when the user is known.</ResponseField>
<ResponseField name="createdAt" type="string">ISO timestamp.</ResponseField>
<ResponseField name="promotedAt" type="string">ISO timestamp of the last promote.</ResponseField>
<ResponseField name="compensationFailed" type="boolean">Set when a promote had to roll back and the rollback did not complete.</ResponseField>
<ResponseField name="lastCompensationError" type="string">The rollback error, when any.</ResponseField>

## Endpoints

### GET /developer/agents/:agentId/versioning/mode

Reports whether agent versioning is enabled. Scope `agents:read`. Versioning is enabled for every agent; the route is kept so older CLIs that probe it keep working.

**Response**

`200` with `{ "enabled": true }`.

### POST /developer/agents/:agentId/versions

Snapshots the latest pushed version of every primitive into a new `staged` version. Scope `agents:write`. Equivalent: `lua version create -m "<message>"`; with `--commit-hash` or after its own git commit, the CLI also patches the hash through the commit-hash route.

<ParamField body="message" type="string">Description. Maximum 500 characters.</ParamField>
<ParamField body="sourceManifestVersion" type="number">Source backup version to record.</ParamField>
<ParamField body="commitHash" type="string">Git commit to record.</ParamField>

**Response**

`201` with the created [agent version object](#agent-version-object). `409` `NO_STAGED_CHANGES` when nothing changed since the last version; `400` `AGENT_VERSION_LIMIT_REACHED` when the agent already has 100 versions.

### GET /developer/agents/:agentId/versions

Lists versions, newest first. Scope `agents:read`. Equivalent: `lua version list`.

<ParamField query="all" type="boolean" default={false}>Return every version instead of the first `limit`.</ParamField>
<ParamField query="limit" type="number" default={50}>Maximum entries.</ParamField>
<ParamField query="status" type="string" default="all">`active`, `staged`, `superseded`, `deleted`, or `all`.</ParamField>

**Response**

`200` with an array of [agent version objects](#agent-version-object) without `snapshot`.

### GET /developer/agents/:agentId/versions/diff

Compares two versions. Scope `agents:read`. Equivalent: `lua version diff <from> <to>`.

<ParamField query="from" type="number" required>Older version number.</ParamField>
<ParamField query="to" type="number" required>Newer version number.</ParamField>

**Response**

<ResponseField name="fromVersion, toVersion" type="number">The compared versions.</ResponseField>
<ResponseField name="skills, webhooks, jobs, preprocessors, postprocessors, triggers, workflows" type="object">Each `{ added, removed, changed }`; entries carry the primitive's ID field, `versionId`, `version`, and `name?`, and `changed` entries carry `from` and `to` as `{ versionId, version }`.</ResponseField>
<ResponseField name="mcpServers" type="object">`{ added, removed, changed }` keyed by `id` and `configHash`; `changed` entries carry `from`, `to`, and `changedFields?`.</ResponseField>
<ResponseField name="persona" type="object | null">`{ from, to, fromVersion?, toVersion? }` when the persona changed.</ResponseField>
<ResponseField name="model" type="object | null">`{ from, to }` when the model changed.</ResponseField>
<ResponseField name="voice" type="object | null">`{ voiceId, from, to }` when the default voice changed.</ResponseField>

### GET /developer/agents/:agentId/versions/:version

Returns one [agent version object](#agent-version-object) with its full snapshot. Scope `agents:read`. Equivalent: `lua version show <n>`.

### POST /developer/agents/:agentId/versions/:version/promote

Makes a version live for every primitive at once; the previously active version becomes `superseded`. Scope `agents:write`. Send an empty JSON object as the body. Equivalent: `lua version promote <n>`. Promoting an older version is the rollback.

**Response**

`200` with `{ promoted, previousActive }`: the now-active version number and the one it replaced (`null` when none was active).

### DELETE /developer/agents/:agentId/versions/:version

Soft-deletes a version. Scope `agents:manage`. The active version and the last remaining version cannot be deleted. Equivalent: `lua version delete <n>`.

**Response**

`200` with `{ deleted: true, version }`.

### PATCH /developer/agents/:agentId/versions/:version/commit-hash

Records the git commit for a version. Scope `agents:write`. Idempotent. `lua version create` calls it after committing and tagging.

<ParamField body="commitHash" type="string" required>The commit SHA.</ParamField>

**Response**

`200` with `{ "ok": true }`.

## Scoped promotes from single-primitive deploys

Publishing a version of a webhook, job, preprocessor, postprocessor, trigger, or workflow through that primitive's own publish route, which is what `lua deploy <type>` calls, also creates and promotes an agent version scoped to that change: the platform clones the active agent version with the one primitive repointed and makes the clone active, so the live agent runs the code it published without a separate promote. The publish response carries the new agent version number. When the code was published but the promote failed, the route answers `503` `SCOPED_PROMOTE_FAILED` with the message `Code published, but the live agent version could not be updated … Retry the deploy to complete it.`; at the version cap it answers `409` `SCOPED_PROMOTE_CAP_EXHAUSTED` (`Agent version limit reached. Prune older versions with lua version delete.`). Skill and persona publishes create no agent version. A skill publish sets the skill's active version, which the agent serves from its next turn; promoting an agent version rewrites every skill's active version from the promoted agent version, so a skill published without a new agent version is reverted by the next promote. A persona publish switches the served persona at once, and a promote records which persona version was current but does not change it. See [Releasing](/ship/releasing).

## Errors

Errors carry `{ "error": { "code", "message" } }`.

| Status | Code or message                      | Meaning                                                                                                     | Fix                                                                                                                    |
| ------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `400`  | `AGENT_VERSION_LIMIT_REACHED`        | The agent has 100 versions: `Agent version limit of 100 reached. Delete older versions to create new ones.` | Delete old versions with `lua version delete <n>`.                                                                     |
| `403`  | `Insufficient permissions`           | The key lacks the `agents` scope on this agent.                                                             | Grant the scope.                                                                                                       |
| `404`  | `AGENT_VERSION_NOT_FOUND`            | Unknown version number.                                                                                     | List versions.                                                                                                         |
| `404`  | `AGENT_VERSIONING_NOT_ENABLED`       | Versioning is off for the agent. Not expected; versioning is on for every agent.                            | Contact support.                                                                                                       |
| `409`  | `NO_STAGED_CHANGES`                  | Nothing changed since the last version.                                                                     | Push a change first, or promote an existing version.                                                                   |
| `409`  | `VERSION_ALREADY_ACTIVE`             | Promote of the active version.                                                                              | Nothing to do.                                                                                                         |
| `409`  | `CANNOT_PROMOTE_DELETED_VERSION`     | Promote of a soft-deleted version.                                                                          | Promote a live version.                                                                                                |
| `409`  | `CANNOT_DELETE_ACTIVE_VERSION`       | Delete of the active version.                                                                               | Promote another version first.                                                                                         |
| `409`  | `CANNOT_DELETE_LAST_VERSION`         | Delete of the only version.                                                                                 | Create another version first.                                                                                          |
| `409`  | `SKILL_SLUG_CONFLICT`                | Two skills in the target snapshot resolve to the same name.                                                 | Rename one skill, push, and create a new version.                                                                      |
| `500`  | `PROMOTE_INCOMPLETE`                 | The promote failed part-way and could not be rolled back fully.                                             | Read the version: `compensationFailed` and `lastCompensationError` say what remains. Promote again or contact support. |
| `500`  | `SUB_AGENT_NOT_FOUND_DURING_PROMOTE` | The agent record vanished while the promote ran.                                                            | Contact support.                                                                                                       |

## Example

Promote version 4, the rollback after a bad version 5.

<CodeGroup>
  ```bash CLI theme={null}
  lua version promote 4
  ```

  ```ts TypeScript theme={null}
  interface PromoteResult {
    promoted: number;
    previousActive: number | null;
  }

  interface ApiError {
    error: { code: string; message: string };
  }

  async function promoteVersion(apiKey: string, agentId: string, version: number): Promise<PromoteResult> {
    const response = await fetch(`https://api.heylua.ai/developer/agents/${agentId}/versions/${version}/promote`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
      body: '{}',
    });
    if (!response.ok) {
      const failure = (await response.json()) as ApiError;
      throw new Error(`${failure.error.code}: ${failure.error.message}`);
    }
    return (await response.json()) as PromoteResult;
  }
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.heylua.ai/developer/agents/<<YOUR_AGENT_ID>>/versions/4/promote" \
    -H "Authorization: Bearer <<YOUR_API_KEY>>" \
    -H "Content-Type: application/json" \
    -d '{}'
  ```
</CodeGroup>

## See also

* [About releases and versions](/concepts/releases-and-versions) — primitive versions, agent versions, rollback
* [Releasing](/ship/releasing) — push, version create, promote
* [`lua version`](/reference/cli/version) — the CLI twin
* [`lua deploy`](/reference/cli/deploy) — the single-primitive shortcut
* [Persona](/reference/rest/persona) — persona versions, pinned by the agent version
