Pin a version for a scope
const url = 'https://example.com/v1/agents/example/pinned/pin';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"version":"example","scope":{"kind":"tenant"},"reason":"example","testRunId":"example","stopFollowingLatest":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/agents/example/pinned/pin \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "version": "example", "scope": { "kind": "tenant" }, "reason": "example", "testRunId": "example", "stopFollowingLatest": true }'Pins version 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 pin is recorded, with who asked and why. Needs pin on the agent.
The pin checks the version in each project whose running version it changes and that has active cases, on the project’s agent test set (testRuns): with the run testRunId names when that run completed there on the test set as it stands, whose outcome changes the corrections’ statuses at once (fixed, reopened); otherwise with a run it starts, correlation id pin:<changeId>, whose outcome changes them when it ends. A pin that waits for approval starts its runs at once, and its statuses change once it’s approved.
A pin that would take a project that follows Latest now off it (a project or segment pin there, or a workspace or org pin over it) is refused with 409 pin-stops-latest before any gate, whether or not its version changes, unless the body has stopFollowingLatest: true. The pin then records stoppedFollowingLatest, and the project runs the pinned version from then on, not each new Latest.
With a release gate for the scope (GET …/release-gate), the pin is checked first against the comparison named by testRunId: 201 pinned; 202 the gate passed and wants a reviewer’s approval (a HITL approval, subject agent-pin; the pinned version moves once it’s approved, if nothing changed meanwhile); 422 gate-failed with details.changeId, details.releaseGate and every check in details.checks. A refused pin is recorded too.
A pin in place skips the gate: when the scope has no pinned 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 pinned (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 pin-superseded: check again.
Under kindgi dev, nothing is pinned, since the pack’s files are what runs: pinning a version 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 workspace (the tenant scope), 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 suggested change changed, or other content than its suggested change was drafted from, even at that version, or another block’s file a version not older), or the block left the pack, 409 pin-superseded, and the message names the file; an approval’s decision settles it superseded the same way. Once the dev server reloads the files, the pinned version is what runs, and the pin’s test runs check it in the agent’s project (the default one) and change the corrections’ statuses as anywhere else.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”AgentId — dotted namespace (acme.drafting).
Header Parameters
Section titled “Header Parameters”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
Section titled “Request Bodyrequired”object
The agent version to pin (registered, active).
The whole workspace.
object
An org’s projects.
object
One project.
object
A segment path within a project: it covers every run whose path starts with it.
object
object
Lowercase, like an identifier: company, contact-role.
The comparison behind this pin.
Pin even when it takes a project that follows Latest now off it: the project then runs the pinned version, not each new Latest. Without it, such a pin is refused (409 pin-stops-latest). POST …/pinned/check ignores it.
Responses
Section titled “Responses”Pinned, with the test runs that check it (testRuns), and the corrections a reused run changed.
A pin, and the test runs that check it.
object
One change of a scope’s pinned version, kept for good: what was pinned before, what is after, who asked and why.
object
The whole workspace.
object
An org’s projects.
object
One project.
object
A segment path within a project: it covers every run whose path starts with it.
object
object
Lowercase, like an identifier: company, contact-role.
The scope’s own pin before; null when it had none.
The scope’s own pin after; null after an unpin.
object
The comparison behind the change.
A pin row’s state: it changes once, from pending-approval to its final state. Absent on unpin rows, and on pins made before gates (pinned).
One of a gate’s checks, as the pin records it.
object
Stable: comparison.required, comparison.status, comparison.freshness, comparison.testSet, sameContents, baselineIsPinned, scopeCovered, evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight, metric.<metric>.k|minCandidate|maxDrop, replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped, cases.minPassing|noNewFailures, groupWeights.expertsOnly, and approval.recheck, which an approval adds to a pin it supersedes, to say what changed since the check.
A plain sentence: what was found against what the gate asks.
The cases it names: for cases.noNewFailures, the ones that passed before and fail now (empty when none does). Absent when it could not tell, and on other checks.
The approval a pending-approval pin waits on (kept once decided).
When a pending-approval pin reached its final state.
A pin’s: true when it took a project that followed Latest off it, as its request asked (stopFollowingLatest) or as a promotion’s pin does. Absent otherwise.
One per project whose running version the pin changes and that has active cases: a project pin has one, a workspace or org pin one per such project, a segment pin none. While the pin waits for approval, its runs are already started.
A test run a pin checks a project with: the one the pin named, reused, or one it started, with correlation id pin:<changeId>.
object
The corrections a reused run made fixed at pin time. Empty when the runs were started: their outcome changes the statuses when each ends.
The corrections a reused run reopened at pin time, as fixed’s.
Example
{ "pin": { "scope": { "kind": "tenant" }, "action": "pin", "requestedBy": { "kind": "user" }, "status": "pinned" }}The gate passed; the pin waits for an approval (pin.approvalId). Its test runs are started, and fixed and reopened are empty.
A pin, and the test runs that check it.
object
One change of a scope’s pinned version, kept for good: what was pinned before, what is after, who asked and why.
object
The whole workspace.
object
An org’s projects.
object
One project.
object
A segment path within a project: it covers every run whose path starts with it.
object
object
Lowercase, like an identifier: company, contact-role.
The scope’s own pin before; null when it had none.
The scope’s own pin after; null after an unpin.
object
The comparison behind the change.
A pin row’s state: it changes once, from pending-approval to its final state. Absent on unpin rows, and on pins made before gates (pinned).
One of a gate’s checks, as the pin records it.
object
Stable: comparison.required, comparison.status, comparison.freshness, comparison.testSet, sameContents, baselineIsPinned, scopeCovered, evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight, metric.<metric>.k|minCandidate|maxDrop, replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped, cases.minPassing|noNewFailures, groupWeights.expertsOnly, and approval.recheck, which an approval adds to a pin it supersedes, to say what changed since the check.
A plain sentence: what was found against what the gate asks.
The cases it names: for cases.noNewFailures, the ones that passed before and fail now (empty when none does). Absent when it could not tell, and on other checks.
The approval a pending-approval pin waits on (kept once decided).
When a pending-approval pin reached its final state.
A pin’s: true when it took a project that followed Latest off it, as its request asked (stopFollowingLatest) or as a promotion’s pin does. Absent otherwise.
One per project whose running version the pin changes and that has active cases: a project pin has one, a workspace or org pin one per such project, a segment pin none. While the pin waits for approval, its runs are already started.
A test run a pin checks a project with: the one the pin named, reused, or one it started, with correlation id pin:<changeId>.
object
The corrections a reused run made fixed at pin time. Empty when the runs were started: their outcome changes the statuses when each ends.
The corrections a reused run reopened at pin time, as fixed’s.
Example
{ "pin": { "scope": { "kind": "tenant" }, "action": "pin", "requestedBy": { "kind": "user" }, "status": "pinned" }}Malformed request body, or the test run isn’t a completed comparison: a cancelled or failed one is refused, even with the summary of the cases it got through. Or reference-out-of-scope: a project scope whose project doesn’t reach the agent (details.issues).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The version is not registered, or was unregistered; or the test run is not found.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}pin-stops-latest: the pin would take a project that follows Latest now off it, and the body doesn’t have stopFollowingLatest: true; details.projects lists each one, { projectId, version }, with the version it runs now. Nothing is recorded. nothing-runs-in-scope: no version of the agent runs in the scope yet (no pin covers it, and it has no Latest), so there’s nothing to measure the version against; deploy or publish a version into the project first (into a later environment, promote a build that ships one). project-suspended: nothing runs there because the scope’s project is suspended. pin-superseded: the scope’s pinned version changed while the gate ran; check again. release-gate-descendant-unpinned: the change would also move a narrower scope a release gate applies to, which has no pinned version of its own and so follows this scope, without its gate; the message names each one: pin it at its current version first. pin-scope-spans-environments: the agent 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 a project (or a segment of it) instead. Under kindgi dev, pin-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 workspace, since the pack’s files serve every scope.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}gate-failed: the gate refused it. details.checks has every check; the refusal is recorded (details.changeId). With the gate’s cases rule, details.passing and details.needs come with minPassing, and details.newlyFailing (the cases that passed before and fail now) with noNewFailures.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}approvals-pending-limit: the gate wants an approval, and the workspace’s approvals waiting for a decision are at its limit (set by the runtime’s operator). Nothing is recorded.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}pin-gate-unsupported: a release gate applies, and the deployment can’t record a gated pin.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}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.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}See also
GET /v1/agents/{agentId}: Fetch the highest version of an agentGET /v1/agents/{agentId}/projects: List the projects an agent runs in, and what runs in eachGET /v1/agents/{agentId}/versions: List versions of an agentGET /v1/agents/{agentId}/versions/{version}: Fetch a specific agent version- Kindgi 0.4.0
- Error code
approvals-pending-limit - Error code
gate-failed - Error code
idempotency-store-unavailable - Error code
nothing-runs-in-scope - Error code
pin-gate-unsupported - Error code
pin-scope-spans-environments - Error code
pin-stops-latest - Error code
pin-superseded - Error code
project-suspended - Error code
reference-out-of-scope - Error code
registry-read-only - Error code
release-gate-descendant-unpinned - Error code
request-too-large - Every error code