`PUT /api/runners/license-pools/{name}` — declare or re-declare a pool.
const url = 'https://example.com/api/runners/license-pools/example';const options = { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: '{"description":"example","seats":1}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url https://example.com/api/runners/license-pools/example \ --header 'Content-Type: application/json' \ --data '{ "description": "example", "seats": 1 }'Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Pool name (normalised to lower case)
Request Bodyrequired
Section titled “Request Bodyrequired”The declaration body for PUT /api/runners/license-pools/{name}.
object
Concurrent seats the operator holds. Must be at least 1 — to retire a pool, DELETE it rather than setting it to zero (a zero-seat pool would wedge every task naming it, forever, with no signal).
Examplegenerated
{ "description": "example", "seats": 1}Responses
Section titled “Responses”The declared pool
A runner_license_pools row: how many CONCURRENT seats of one desktop tool the
operator actually holds. Instance-wide by design (a tool licence is a fact about
the operator’s machines, not a tenant preference — see migration 360).
Occupancy is deliberately NOT a column. It is derived at claim time from the open
runner_work_assignments whose stored payload names this pool, so every release
path — terminal report, runner crash + lease expiry, pickup timeout, cancel —
frees the seat with no ledger to reconcile.
object
The name a task references as config.runner.license_pool.
Concurrent seats. Always > 0 (a zero-seat pool would wedge its tasks
forever, so the DB CHECK refuses it — that is a deletion, not a setting).
Examplegenerated
{ "created_at": "2026-04-15T12:00:00Z", "created_by": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "description": "example", "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example", "seats": 1, "updated_at": "2026-04-15T12:00:00Z"}Blank name or non-positive seat count
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Authentication required
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Permission denied
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Structured server error
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}