# Make a version live for a scope

POST

/v1/agents/{agentId}/promotions

```sh
curl --request POST \
  --url https://example.com/v1/agents/example/promotions \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{ "version": "example", "scope": { "kind": "tenant" }, "reason": "example", "evalRunId": "example" }'
```

Pins `version` live for `scope`: runs in that scope that don’t name a version use it, from the next run. Open conversations keep their version. The version must be registered and active. Every promotion is recorded, with who asked and why. Needs `promote` on the agent.

With a gate policy for the scope (`GET …/gate-policy`), the promotion is checked first against the comparison named by `evalRunId`: `201` promoted; `202` the gate passed and the policy wants a reviewer’s approval (a HITL approval, subject `agent-promotion`; the live version moves once it’s approved, if nothing changed meanwhile); `422 gate-failed` with `details.promotionId`, `details.policy` and every check in `details.checks`. A refused promotion is recorded too.

A pin in place skips the gate: when the scope has no live version of its own and already serves exactly `version` (from a scope above, or as the latest), pinning it there changes nothing any run gets, so it is promoted (`201`) with one passing `pinInPlace` check, no comparison and no approval, its reason starting `pin-in-place`. It is re-checked as it is written; if the scope moved meanwhile, `409 promotion-superseded`: check again.

Under `kindgi dev`, nothing is pinned, since the pack’s files are what runs: making a version live writes the data blocks it changes into the pack’s own files (each block’s new version and content, and a prompt’s template into its template file), and the dev server reloads them. Only for the whole tenant, which the pack’s files serve. If one of those files changed since the version was made (the changed block’s file holds another version than the one its proposal changed, or another block’s file a version not older), or the block left the pack, `409 promotion-superseded`, and the message names the file; an approval’s decision settles it `superseded` the same way.

## Authorizations

- **[bearerAuth](https://docs.kindgi.com/v0.3.1/reference/api.md#bearerauth)**

## Parameters

### Path Parameters

**agentId**

required

string

AgentId — dotted namespace (`acme.drafting`).

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters

Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per `docs/API-ROUTE-CONVENTIONS.md` §3.1). A retry sent while the first request still runs is answered `409 idempotency-key-in-flight` with `Retry-After`, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets `409 idempotency-key-replay-withheld`, with the first request’s status and when it succeeded (from 0.1.5). A request whose key store can’t be read before it runs is answered `503 idempotency-store-unavailable` with `Retry-After`: nothing ran, so retry with the same key (from 0.3.0).

## Request Bodyrequired

application/json

object

**version**

required

The agent version to make live (registered, active).

string

**scope**

required

One of: discriminator: kind

- **object**

  The agent’s default for the whole tenant.

  object

  **kind**

  required

  string

  Allowed values: tenant

- **object**

  An org’s projects.

  object

  **kind**

  required

  string

  Allowed values: org

  **orgId**

  required

  string format: uuid

- **object**

  One project.

  object

  **kind**

  required

  string

  Allowed values: project

  **projectId**

  required

  string format: uuid

- **object**

  A segment path within a project: it covers every run whose path starts with it.

  object

  **kind**

  required

  string

  Allowed values: segment

  **projectId**

  required

  string format: uuid

  **path**

  required

  Array\<object>

  \>= 1 items <= 8 items

  object

  **key**

  required

  Lowercase, like an identifier: `company`, `contact-role`.

  string

  /^\[a-z]\[a-z0-9\_-]{0,63}$/

  **value**

  required

  string

  \>= 1 characters <= 256 characters

**reason**

string

<= 2000 characters

**evalRunId**

The comparison this promotion was judged on.

string

## Responses

### 201

Promoted.

application/json

One change of a scope’s live version, kept for good: what was live before, what is after, who asked and why.

object

**id**

required

string format: uuid

**agentId**

required

string

**scope**

required

One of: discriminator: kind

- **object**

  The agent’s default for the whole tenant.

  object

  **kind**

  required

  string

  Allowed values: tenant

- **object**

  An org’s projects.

  object

  **kind**

  required

  string

  Allowed values: org

  **orgId**

  required

  string format: uuid

- **object**

  One project.

  object

  **kind**

  required

  string

  Allowed values: project

  **projectId**

  required

  string format: uuid

- **object**

  A segment path within a project: it covers every run whose path starts with it.

  object

  **kind**

  required

  string

  Allowed values: segment

  **projectId**

  required

  string format: uuid

  **path**

  required

  Array\<object>

  \>= 1 items <= 8 items

  object

  **key**

  required

  Lowercase, like an identifier: `company`, `contact-role`.

  string

  /^\[a-z]\[a-z0-9\_-]{0,63}$/

  **value**

  required

  string

  \>= 1 characters <= 256 characters

**action**

required

string

Allowed values: promote rollback unpin

**fromVersion**

required

The scope’s own pin before; null when it had none.

string | null

**toVersion**

required

The scope’s own pin after; null after an unpin.

string | null

**requestedBy**

required

object

**kind**

required

string

Allowed values: user service

**id**

required

string

**reason**

string

**evalRunId**

The comparison the change was judged on.

string

**createdAt**

required

string format: date-time

**status**

A `promote` row’s state: it changes once, from `pending-approval` to its final state. Absent on rollback and unpin rows, and on promotions made before gates (`promoted`).

string

Allowed values: promoted pending-approval refused superseded rejected expired

**policy**

One of:

- **object**

  object

  **id**

  required

  string

  **version**

  required

  string

- **null**

  null

**checks**

Array\<object>

One of a gate’s checks, as the promotion records it.

object

**name**

required

Stable: `comparison.required`, `comparison.status`, `comparison.freshness`, `comparison.suite`, `sameContents`, `baselineIsLive`, `scopeCovered`, `evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight`, `metric.<metric>.k|minCandidate|maxDrop`, `replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped`, `classWeights.restrictedOnly`, and `approval.recheck`, which an approval adds to a promotion it supersedes, to say what changed since the check.

string

**passed**

required

boolean

**message**

required

A plain sentence: what was found against what the policy asks.

string

**value**

number | string

**threshold**

number | string

**approvalId**

The approval a `pending-approval` promotion waits on (kept once decided).

string

**resolvedAt**

When a `pending-approval` promotion reached its final state.

string format: date-time

##### Example

```json
{
  "scope": {
    "kind": "tenant"
  },
  "action": "promote",
  "requestedBy": {
    "kind": "user"
  },
  "status": "promoted"
}
```

### 202

The gate passed; the promotion waits for an approval (`approvalId`).

application/json

One change of a scope’s live version, kept for good: what was live before, what is after, who asked and why.

object

**id**

required

string format: uuid

**agentId**

required

string

**scope**

required

One of: discriminator: kind

- **object**

  The agent’s default for the whole tenant.

  object

  **kind**

  required

  string

  Allowed values: tenant

- **object**

  An org’s projects.

  object

  **kind**

  required

  string

  Allowed values: org

  **orgId**

  required

  string format: uuid

- **object**

  One project.

  object

  **kind**

  required

  string

  Allowed values: project

  **projectId**

  required

  string format: uuid

- **object**

  A segment path within a project: it covers every run whose path starts with it.

  object

  **kind**

  required

  string

  Allowed values: segment

  **projectId**

  required

  string format: uuid

  **path**

  required

  Array\<object>

  \>= 1 items <= 8 items

  object

  **key**

  required

  Lowercase, like an identifier: `company`, `contact-role`.

  string

  /^\[a-z]\[a-z0-9\_-]{0,63}$/

  **value**

  required

  string

  \>= 1 characters <= 256 characters

**action**

required

string

Allowed values: promote rollback unpin

**fromVersion**

required

The scope’s own pin before; null when it had none.

string | null

**toVersion**

required

The scope’s own pin after; null after an unpin.

string | null

**requestedBy**

required

object

**kind**

required

string

Allowed values: user service

**id**

required

string

**reason**

string

**evalRunId**

The comparison the change was judged on.

string

**createdAt**

required

string format: date-time

**status**

A `promote` row’s state: it changes once, from `pending-approval` to its final state. Absent on rollback and unpin rows, and on promotions made before gates (`promoted`).

string

Allowed values: promoted pending-approval refused superseded rejected expired

**policy**

One of:

- **object**

  object

  **id**

  required

  string

  **version**

  required

  string

- **null**

  null

**checks**

Array\<object>

One of a gate’s checks, as the promotion records it.

object

**name**

required

Stable: `comparison.required`, `comparison.status`, `comparison.freshness`, `comparison.suite`, `sameContents`, `baselineIsLive`, `scopeCovered`, `evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight`, `metric.<metric>.k|minCandidate|maxDrop`, `replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped`, `classWeights.restrictedOnly`, and `approval.recheck`, which an approval adds to a promotion it supersedes, to say what changed since the check.

string

**passed**

required

boolean

**message**

required

A plain sentence: what was found against what the policy asks.

string

**value**

number | string

**threshold**

number | string

**approvalId**

The approval a `pending-approval` promotion waits on (kept once decided).

string

**resolvedAt**

When a `pending-approval` promotion reached its final state.

string format: date-time

##### Example

```json
{
  "scope": {
    "kind": "tenant"
  },
  "action": "promote",
  "requestedBy": {
    "kind": "user"
  },
  "status": "promoted"
}
```

### 400

Malformed request body. Or `reference-out-of-scope`: a project scope whose project doesn’t reach the agent (`details.issues`).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 401

Missing / malformed / expired / revoked bearer token.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 404

The version is not registered, or was unregistered; or the eval run is not found.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 409

`promotion-superseded`: the scope’s live version changed while the gate ran; check again. `gate-policy-descendant-unpinned`: the change would also move a narrower scope a gate policy applies to, which has no live version of its own and so follows this scope, without its gate; the message names each one: pin it at its current version first. `live-scope-spans-environments`: the agent has a project in an environment order, and a tenant- or org-wide pin would act in every environment of the order at once; pin a project (or a segment of it) instead. Under `kindgi dev`, `promotion-superseded` also when a pack file the version’s data blocks go into changed since the version was made, or the block left the pack; the message names the file. Or `registry-read-only`: under `kindgi dev`, a scope narrower than the whole tenant, since the pack’s files serve every scope.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 413

The request body is over the API’s limit (`request-too-large`; `details.maxBytes` says how much).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 422

`gate-failed`: the gate refused it. `details.checks` has every check; the refusal is recorded (`details.promotionId`).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 429

`approvals-pending-limit`: the gate wants an approval, and the tenant’s approvals waiting for a decision are at its limit (set by the runtime’s operator). Nothing is recorded.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 500

Server error (unmapped domain code or framework crash).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 501

`promotion-gate-unsupported`: a gate policy applies, and the deployment can’t record a gated promotion.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 503

The `Idempotency-Key` store couldn’t be read before the request ran (`idempotency-store-unavailable`), so nothing was changed; `Retry-After` (whole seconds) says when to try again with the same key.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

## See also

- [`GET /v1/agents/{agentId}/promotions`: List an agent's promotions](https://docs.kindgi.com/v0.3.1/reference/api/operations/agentspromotionslist.md)
- [`GET /v1/agents/{agentId}`: Fetch the latest version of an agent](https://docs.kindgi.com/v0.3.1/reference/api/operations/agentsget.md)
- [`GET /v1/agents/{agentId}/projects`: List the projects an agent runs in](https://docs.kindgi.com/v0.3.1/reference/api/operations/agentsprojects.md)
- [`GET /v1/agents/{agentId}/versions`: List versions of an agent](https://docs.kindgi.com/v0.3.1/reference/api/operations/agentslistversions.md)
- [Error code `approvals-pending-limit`](https://docs.kindgi.com/v0.3.1/reference/errors.md#approvals-pending-limit)
- [Error code `gate-failed`](https://docs.kindgi.com/v0.3.1/reference/errors.md#gate-failed)
- [Error code `gate-policy-descendant-unpinned`](https://docs.kindgi.com/v0.3.1/reference/errors.md#gate-policy-descendant-unpinned)
- [Error code `idempotency-store-unavailable`](https://docs.kindgi.com/v0.3.1/reference/errors.md#idempotency-store-unavailable)
- [Error code `live-scope-spans-environments`](https://docs.kindgi.com/v0.3.1/reference/errors.md#live-scope-spans-environments)
- [Error code `promotion-gate-unsupported`](https://docs.kindgi.com/v0.3.1/reference/errors.md#promotion-gate-unsupported)
- [Error code `promotion-superseded`](https://docs.kindgi.com/v0.3.1/reference/errors.md#promotion-superseded)
- [Error code `reference-out-of-scope`](https://docs.kindgi.com/v0.3.1/reference/errors.md#reference-out-of-scope)
- [Error code `registry-read-only`](https://docs.kindgi.com/v0.3.1/reference/errors.md#registry-read-only)
- [Error code `request-too-large`](https://docs.kindgi.com/v0.3.1/reference/errors.md#request-too-large)
- [Every error code](https://docs.kindgi.com/v0.3.1/reference/errors.md)
