# Pin a version for a scope

POST

/v1/tools/{toolId}/pinned/pin

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

Pins `version` for `scope` (the workspace, an org or a project), or moves the scope’s pin to it. An agent or flow version made in a project the pin covers (the project’s own pin, then its org’s, then the workspace’s) locks the tool at the pinned version: a flow’s tool step always, an agent’s tool reference when the pinned version is in its range. A version already made keeps its lock; a pin reaches an agent or flow through its next version. The version must be registered and active. Pinning the version the scope already pins changes nothing and answers the change that set it. Every change is recorded, with who asked and why. Needs `pin` on the tool.

## Authorizations

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

## Parameters

### Path Parameters

**toolId**

required

string

ToolId — dotted namespace (`acme.lookup-order`).

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters

Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the workspace 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 version to pin (registered, active).

string

**scope**

required

One of: discriminator: kind

- **object**

  The whole workspace.

  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

**reason**

string

<= 2000 characters

## Responses

### 200

Pinned.

application/json

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

object

**id**

required

string format: uuid

**itemId**

required

string

**scope**

required

One of: discriminator: kind

- **object**

  The whole workspace.

  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

**action**

required

string

Allowed values: pin 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

Who pinned or unpinned: a user, or a service token.

object

**kind**

required

string

Allowed values: user service

**id**

required

string

**reason**

string

**createdAt**

required

string format: date-time

##### Example

```json
{
  "scope": {
    "kind": "tenant"
  },
  "action": "pin",
  "requestedBy": {
    "kind": "user"
  }
}
```

### 400

Malformed request body. Or `scope-invalid`: a segment scope, since tools pin per project, org or workspace. Or `reference-out-of-scope`: a project scope whose project doesn’t reach the tool (`details.issues`). Or `tool-not-pinnable`: the tool follows its server (an MCP endpoint’s catalog lists it; its metadata has the catalog’s `kindgi.mcp` key) or the runtime (a tool the runtime ships), so a pin couldn’t keep it still; reference it with a range.

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

`tool-not-found`: no tool with that id, or the version isn’t registered or was unregistered.

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

Idempotency-Key was reused with a different body. Or `pin-scope-spans-environments`: the tool has a project in an environment order, and a workspace- or org-wide pin would act in every environment of the order at once; pin it per project instead. Or `environment-takes-promotions`: the project is a later environment of an environment order, where no agent or flow version is made, so a pin there would never act; pin the tool in the order’s first environment (`details.firstProjectId`). Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack’s files are the source); the message says what to do instead.

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"
  }
}
```

### 410

`tool-gone`: the tool has no active version left.

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"
  }
}
```

### 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"
  }
}
```

### 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/tools/{toolId}`: Fetch a tool manifest (latest active version)](https://docs.kindgi.com/v0.5.0/reference/api/operations/toolsget.md)
- [`GET /v1/tools/{toolId}/versions`: List all versions of a tool](https://docs.kindgi.com/v0.5.0/reference/api/operations/toolslistversions.md)
- [`GET /v1/tools/{toolId}/versions/{version}`: Fetch a specific tool version](https://docs.kindgi.com/v0.5.0/reference/api/operations/toolsgetversion.md)
- [`POST /v1/tools/{toolId}/versions/{version}/unregister`: Unregister a specific tool version](https://docs.kindgi.com/v0.5.0/reference/api/operations/toolsunregister.md)
- [Error code `environment-takes-promotions`](https://docs.kindgi.com/v0.5.0/reference/errors.md#environment-takes-promotions)
- [Error code `idempotency-store-unavailable`](https://docs.kindgi.com/v0.5.0/reference/errors.md#idempotency-store-unavailable)
- [Error code `pin-scope-spans-environments`](https://docs.kindgi.com/v0.5.0/reference/errors.md#pin-scope-spans-environments)
- [Error code `reference-out-of-scope`](https://docs.kindgi.com/v0.5.0/reference/errors.md#reference-out-of-scope)
- [Error code `registry-read-only`](https://docs.kindgi.com/v0.5.0/reference/errors.md#registry-read-only)
- [Error code `request-too-large`](https://docs.kindgi.com/v0.5.0/reference/errors.md#request-too-large)
- [Error code `scope-invalid`](https://docs.kindgi.com/v0.5.0/reference/errors.md#scope-invalid)
- [Error code `tool-gone`](https://docs.kindgi.com/v0.5.0/reference/errors.md#tool-gone)
- [Error code `tool-not-found`](https://docs.kindgi.com/v0.5.0/reference/errors.md#tool-not-found)
- [Error code `tool-not-pinnable`](https://docs.kindgi.com/v0.5.0/reference/errors.md#tool-not-pinnable)
- [Every error code](https://docs.kindgi.com/v0.5.0/reference/errors.md)
