Publish a release gate
const url = 'https://example.com/v1/release-gates';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"id":"example","version":"example","agentId":"example","scope":{"kind":"tenant"},"spec":{"comparison":{"maxAgeHours":1,"testSet":{"id":"example","version":"example"}},"evidence":{"minCases":1,"minWeight":1},"metrics":[{"name":"passRate","k":1,"minCandidate":1,"maxDrop":1}],"replay":{"maxDiverged":1,"maxErrors":1,"maxRefusedWrites":1,"maxStopped":1},"cases":{"minPassing":1,"noNewFailures":true},"approvals":{"role":"standard","separateApprover":true},"onlyExpertGroups":true},"description":"example"}'};
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/release-gates \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "id": "example", "version": "example", "agentId": "example", "scope": { "kind": "tenant" }, "spec": { "comparison": { "maxAgeHours": 1, "testSet": { "id": "example", "version": "example" } }, "evidence": { "minCases": 1, "minWeight": 1 }, "metrics": [ { "name": "passRate", "k": 1, "minCandidate": 1, "maxDrop": 1 } ], "replay": { "maxDiverged": 1, "maxErrors": 1, "maxRefusedWrites": 1, "maxStopped": 1 }, "cases": { "minPassing": 1, "noNewFailures": true }, "approvals": { "role": "standard", "separateApprover": true }, "onlyExpertGroups": true }, "description": "example" }'Registers a release gate, or a new version of one: what a pin of agentId for scope must show. One gate per agent and scope: another gate id for a scope that has one is refused with 409 release-gate-scope-taken (details.heldBy names it; publish a new version of that one instead), and a new version can’t change the agent or scope (409 release-gate-scope-changed). The spec is checked strictly: an unknown key is refused (400 validation-failed, details.issues). Needs admin on the tenant: whoever may pin can’t loosen their own gate.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”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
Section titled “Request Bodyrequired”object
Yours to choose, e.g. acme.drafting-prod.
The whole tenant.
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.
What a pin must show. Every part is optional; an empty spec checks nothing. A pin must name a comparison (testRunId) exactly when the spec has comparison, evidence, metrics, replay, cases or onlyExpertGroups. Unknown keys are refused.
object
object
object
The evidence from corrections behind each gated metric; for a metric with maxDrop, the baseline’s too.
object
object
weightedPrecisionAtK only: the summary’s k must be this.
How far the candidate may fall below the baseline (the pinned version).
Each knob left out of the block is 0. No block: no replay checks.
object
At most this many tool calls the replay refused (the comparison summary’s refusedWrites). Reads count too, despite the name.
The cases the comparison graded: each correction it checked is a case, and it passes when every repetition did.
object
At least this many cases pass.
No case that passed before fails: none of the comparison’s corrections that are fixed now fails it. One the comparison didn’t grade isn’t counted; the check’s message says how many of the cases fixed now it graded.
A passing pin waits for a reviewer.
object
Default senior.
Default true.
Only expert groups count: the comparison must be weighted experts-only, so a group anyone may assert can’t move the gate.
Responses
Section titled “Responses”Release gate published.
object
The whole tenant.
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.
What a pin must show. Every part is optional; an empty spec checks nothing. A pin must name a comparison (testRunId) exactly when the spec has comparison, evidence, metrics, replay, cases or onlyExpertGroups. Unknown keys are refused.
object
object
object
The evidence from corrections behind each gated metric; for a metric with maxDrop, the baseline’s too.
object
object
weightedPrecisionAtK only: the summary’s k must be this.
How far the candidate may fall below the baseline (the pinned version).
Each knob left out of the block is 0. No block: no replay checks.
object
At most this many tool calls the replay refused (the comparison summary’s refusedWrites). Reads count too, despite the name.
The cases the comparison graded: each correction it checked is a case, and it passes when every repetition did.
object
At least this many cases pass.
No case that passed before fails: none of the comparison’s corrections that are fixed now fails it. One the comparison didn’t grade isn’t counted; the check’s message says how many of the cases fixed now it graded.
A passing pin waits for a reviewer.
object
Default senior.
Default true.
Only expert groups count: the comparison must be weighted experts-only, so a group anyone may assert can’t move the gate.
Present only on an unregistered version: a retired gate (every version unregistered) as GET /v1/release-gates?includeRetired=true lists it.
Example
{ "scope": { "kind": "tenant" }, "spec": { "metrics": [ { "name": "passRate" } ], "approvals": { "role": "standard" } }}Validation failed (see 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" }}release-gate-already-registered: that (id, version) exists. release-gate-scope-taken: another release gate gates the agent for the scope (details.heldBy). release-gate-scope-changed: the version would change the agent or scope. release-gate-scope-unpinned: the scope has no pinned version of its own; a gated scope holds its own pin (a pin above it is not enough, since a pin there would change this scope ungated), so pin a version for the scope first.
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" }}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" }}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" }}