Skip to content

HTTP API reference

Every endpoint of cloop's two HTTP surfaces, generated from the route tables that serve them. Do not edit this file: it is rendered by tests/docs/http_api_test.go from pkg/ui and pkg/apiserver, and a CI check fails when it stops matching them. Regenerate with make docs-api.

For the conceptual documentation see the documentation index; for the roles these permissions belong to see the security model.

Authentication

The hub accepts two credentials, and which one you use depends on whether a human is present:

  • API token — Authorization: Bearer cloop_pat_…. Minted by an admin (POST /api/tokens, or cloop hub token create), carrying an explicit set of roles and an optional project restriction. This is the credential for CI, scripts, and anything without a browser.
  • Session cookie — set by the OIDC login flow and used by the dashboard.

A third is accepted on /scim/v2 and nowhere else, and /scim/v2 accepts nothing else: the SCIM credential, Authorization: Bearer cloop_scim_…, minted for an identity provider with cloop hub scim credential create or under Settings → Provisioning (SCIM). Not an API token, not the static token, not a session. The SCIM routes answer in RFC 7644's own dialect — bodies as application/scim+json (or application/json), errors as SCIM error documents, 429 with Retry-After past the credential's rate limit — and only on a hub with single sign-on. See the SCIM provisioning guide.

On a hub with single sign-on every 401 carries X-Cloop-Sign-In: /auth/login: a browser answers it by sending the user through GET /auth/login?return=<path>, which comes back to that path on this origin. A token-only hub's 401 carries no such header. A privileged action refused because the session's claims are too old is a 403 whose error.details.renewable says whether re-asserting them from the browser — GET /auth/renew, loaded in a hidden frame, which runs a prompt=none round trip to the identity provider and posts its verdict to a same-origin parent — can clear it. GET /api/me reports the session's clocks: claim_age_seconds, max_claim_age_seconds, session_expires_in_seconds, and renew_in_seconds when the browser has to renew the claims itself because the hub holds no refresh token for the session. See silent renewal.

The deprecated static token (--token / CLOOP_UI_TOKEN) is a bearer credential too, and can be retired at runtime (POST /api/static-token/retire, or cloop hub token static retire). A request presenting a retired one — in the header or as ?token= — is answered 401 with code: static_token_retired, retired_at, retired_by and instead, what to present in its place; it does not count toward the per-address failure lockout.

cloop serve is different: it is a single-project daemon with one optional bearer token and no permission model, so its endpoints are either behind that token or exempt from it.

A hub with neither single sign-on nor a static token serves a request that presents no credential at all — an API token restricts only the caller that presents it — and so does cloop serve without its token. Each of them therefore listens on 127.0.0.1 only, and refuses to listen beyond loopback unless ui.allow_unauthenticated_network is set; see the configuration reference.

Reading the tables

Permission is what a caller must hold to reach the endpoint; it is the same string the audit trail records and /api/me reports. Scope is what that permission is evaluated against — and the distinction matters: a project-scoped grant does not satisfy a route scoped global, so a user who can administer one project still cannot read the fleet.

Two scopes are about the caller's own list of projects. project-list admits a caller who holds project.read hub-wide, or on at least one project — someone a project was shared with, on a hub whose default role is none — and lists such a caller only the projects they can read. stream is a realtime stream: scoped to the project ?project_idx names, or, with ?scope=global, to the caller's project list.

Endpoints marked public require no permission. On the hub that does not mean unauthenticated — the authentication middleware has already run, and only the login flow, the SPA shell and the static assets bypass it — it means no role is required, because the route either reports the caller's own identity or is scoped to it by construction.

A path marked (subtree) is registered as a prefix and matches everything beneath it.

The machine-readable form of the hub's table is served live at GET /api/openapi.json (requires project.read), and cloop serve publishes its own at GET /openapi.json.

Hub API (cloop ui)

The dashboard and everything an integrator can drive: projects, tasks, runs, the executor fleet, secrets and grants, audit, and the display-glasses surface. Generated from routeTable() in pkg/ui/routes.go.

372 endpoints, by the permission each one requires:

Permission Endpoints
public (no permission) 71
audit.read 5
config.write 19
executor.manage 48
executor.read 6
project.read 66
project.share 3
project.write 16
run.start 6
run.stop 3
sandbox.attach 2
secret.grant 15
secret.own 22
secret.request 5
secret.revoke 3
session.admin 2
task.mutate 42
token.admin 6
user.manage 30
view.prefs 2
Method Path Permission Scope
GET / public global
GET /_anthropic/ (subtree) public global
POST /_anthropic/ (subtree) public global
GET /api/analytics project.read project
GET /api/approvals public global
GET /api/approvals/count public global
GET /api/audit audit.read global
GET /api/audit/verify audit.read global
GET /api/budget project.read project
PUT /api/budget/global config.write global
PUT /api/budget/project config.write project
POST /api/chat task.mutate project
GET /api/chat/history project.read project
POST /api/chat/plan task.mutate project
GET /api/ci/anthropic/ (subtree) public global
POST /api/ci/anthropic/ (subtree) public global
GET /api/ci/config secret.grant global
PUT /api/ci/config config.write global
GET /api/ci/exchanges secret.grant global
GET /api/ci/rules secret.grant global
POST /api/ci/rules secret.grant global
POST /api/ci/rules/test secret.grant global
PUT /api/ci/rules/{id} secret.grant global
DELETE /api/ci/rules/{id} secret.grant global
GET /api/ci/sessions secret.grant global
DELETE /api/ci/sessions/{id} secret.revoke global
POST /api/ci/token public global
GET /api/claude-usage project.read global
GET /api/claudecode-limits project.read project
PUT /api/claudecode-limits config.write project
POST /api/claudecode/auth/login secret.own global
POST /api/claudecode/auth/login/cancel secret.own global
POST /api/claudecode/auth/login/code secret.own global
POST /api/claudecode/auth/logout secret.own global
GET /api/claudecode/auth/status project.read global
POST /api/client-error public global
GET /api/cluster executor.manage global
GET /api/config project.read project
GET /api/config/disk user.manage global
PUT /api/config/disk user.manage global
GET /api/config/notifications user.manage global
PUT /api/config/notifications user.manage global
POST /api/config/notifications/test user.manage global
GET /api/config/oidc user.manage global
PUT /api/config/oidc user.manage global
POST /api/config/oidc/enforce user.manage global
POST /api/config/oidc/test user.manage global
POST /api/config/set config.write project
GET /api/config/stt project.read global
PUT /api/config/stt config.write global
DELETE /api/config/stt config.write global
GET /api/config/telemetry user.manage global
PUT /api/config/telemetry user.manage global
GET /api/cost/identities project.read global
POST /api/csp-report public global
GET /api/deps project.read project
POST /api/device/approve public global
POST /api/device/code public global
POST /api/device/deny public global
POST /api/device/lookup public global
POST /api/device/token public global
GET /api/dictate project.read project
GET /api/disk-usage project.read project
GET /api/epics project.read project
GET /api/event-history project.read project
GET /api/events project.read stream
GET /api/executor-pools executor.manage global
POST /api/executor-pools executor.manage global
GET /api/executor-pools/{id} executor.manage global
PUT /api/executor-pools/{id} executor.manage global
GET /api/executor-pools/{id}/audience executor.manage global
POST /api/executor-pools/{id}/audience executor.manage global
DELETE /api/executor-pools/{id}/audience executor.manage global
POST /api/executor-pools/{id}/cordon executor.manage global
POST /api/executor-pools/{id}/credentials executor.manage global
DELETE /api/executor-pools/{id}/credentials/{cid} executor.manage global
POST /api/executor-pools/{id}/revoke executor.manage global
POST /api/executor-pools/{id}/uncordon executor.manage global
GET /api/executors executor.read global
POST /api/executors/enroll executor.manage global
GET /api/executors/{id} executor.read executor
DELETE /api/executors/{id} executor.manage executor
GET /api/executors/{id}/audience executor.manage executor
POST /api/executors/{id}/audience executor.manage executor
DELETE /api/executors/{id}/audience executor.manage executor
POST /api/executors/{id}/cordon executor.manage executor
POST /api/executors/{id}/drain executor.manage executor
GET /api/executors/{id}/firewall executor.manage executor
POST /api/executors/{id}/firewall executor.manage executor
PUT /api/executors/{id}/firewall executor.manage executor
POST /api/executors/{id}/isolation-probe executor.manage executor
GET /api/executors/{id}/labels executor.manage executor
PUT /api/executors/{id}/labels executor.manage executor
GET /api/executors/{id}/limits executor.manage executor
POST /api/executors/{id}/limits executor.manage executor
PUT /api/executors/{id}/limits executor.manage executor
GET /api/executors/{id}/sandbox executor.manage executor
POST /api/executors/{id}/sandbox executor.manage executor
PUT /api/executors/{id}/sandbox executor.manage executor
POST /api/executors/{id}/uncordon executor.manage executor
POST /api/executors/{id}/upgrade executor.manage executor
GET /api/executors/{id}/virtual executor.manage executor
PUT /api/executors/{id}/virtual executor.manage executor
DELETE /api/executors/{id}/virtual executor.manage executor
GET /api/executors/{id}/virtuals executor.manage executor
POST /api/executors/{id}/virtuals executor.manage executor
GET /api/firewall project.read project
POST /api/firewall config.write project
PUT /api/firewall config.write project
GET /api/firewall/blocked project.read project
GET /api/fleet/autoupdate executor.read global
PUT /api/fleet/autoupdate executor.manage global
GET /api/forwarders executor.read global
GET /api/forwarders/bindings executor.read global
DELETE /api/forwarders/bindings/{bid} executor.manage global
POST /api/forwarders/enroll executor.manage global
GET /api/forwarders/requests secret.request global
DELETE /api/forwarders/{id} executor.manage global
POST /api/forwarders/{id}/bindings executor.manage global
POST /api/forwarders/{id}/expose executor.manage global
POST /api/github-app/installations secret.grant global
GET /api/glasses/dictate project.read project
GET /api/glasses/link public global
POST /api/glasses/link public global
DELETE /api/glasses/link public global
GET /api/glasses/projects project.read project-list
GET /api/glasses/tasks project.read project
POST /api/glasses/tasks task.mutate project
GET /api/glasses/tasks/{id} project.read project
POST /api/glasses/telemetry public global
GET /api/glasses/telemetry/config public global
POST /api/glasses/transcribe task.mutate project
GET /api/goal project.read project
POST /api/goal project.write project
PUT /api/goal project.write project
GET /api/grant-requests secret.request global
POST /api/grant-requests secret.request global
POST /api/grant-requests/{id}/approve secret.grant global
POST /api/grant-requests/{id}/deny secret.grant global
GET /api/grant-requests/{id}/uses secret.grant global
POST /api/grant-requests/{id}/withdraw secret.request global
GET /api/grants secret.own global
POST /api/grants secret.own global
GET /api/grants/matches secret.own global
DELETE /api/grants/{id} secret.own global
GET /api/grants/{id}/holders secret.own global
POST /api/grants/{id}/unguarded user.manage global
DELETE /api/grants/{id}/unguarded user.manage global
GET /api/hub/ca config.write global
POST /api/hub/ca/rotate config.write global
GET /api/hub/health config.write global
POST /api/init project.write project
GET /api/instructions project.read project
POST /api/instructions project.write project
PUT /api/instructions project.write project
GET /api/kb project.read project
POST /api/kb task.mutate project
GET /api/kb/search project.read project
DELETE /api/kb/{id} task.mutate project
GET /api/knowledge/sources secret.own global
POST /api/knowledge/sources secret.own global
GET /api/knowledge/sources/{id} secret.own global
PATCH /api/knowledge/sources/{id} secret.own global
DELETE /api/knowledge/sources/{id} secret.own global
POST /api/knowledge/sources/{id}/sync secret.own global
POST /api/knowledge/test-connection secret.own global
GET /api/leases secret.grant global
POST /api/leases/{id}/revoke secret.revoke global
GET /api/livelog project.read project
GET /api/me public global
GET /api/openapi.json project.read global
POST /api/options/max-parallel config.write project
POST /api/options/provider config.write project
POST /api/options/requirements-gate config.write project
GET /api/options/review-gate project.read project
POST /api/options/review-gate config.write project
POST /api/options/step-timeout config.write project
POST /api/options/task-timeout config.write project
POST /api/options/toggle config.write project
GET /api/pool-controller/feed public global
POST /api/pool-controller/runners public global
POST /api/pool-controller/runners/retire public global
POST /api/pool-controller/status public global
GET /api/projects project.read project-list
GET /api/projects/events project.read project-list
POST /api/projects/new project.write global
DELETE /api/projects/{idx} project.write project-index
POST /api/projects/{idx}/executor executor.manage project-index
GET /api/projects/{idx}/features project.read project-index
POST /api/projects/{idx}/features project.write project-index
DELETE /api/projects/{idx}/features/{slug} project.write project-index
POST /api/projects/{idx}/features/{slug}/pr project.write project-index
POST /api/projects/{idx}/features/{slug}/pr/refresh project.write project-index
GET /api/projects/{idx}/forwarders executor.read project-index
GET /api/projects/{idx}/harness-credential project.read project-index
POST /api/projects/{idx}/harness-credential secret.own project-index
POST /api/projects/{idx}/hidden view.prefs project-index
POST /api/projects/{idx}/knowledge/bindings project.write project-index
DELETE /api/projects/{idx}/knowledge/bindings/{bid} project.write project-index
GET /api/projects/{idx}/knowledge/retrieval project.read project-index
PUT /api/projects/{idx}/knowledge/retrieval project.write project-index
GET /api/projects/{idx}/knowledge/search project.read project-index
GET /api/projects/{idx}/knowledge/sources project.read project-index
GET /api/projects/{idx}/members project.read project-index
POST /api/projects/{idx}/members project.share project-index
PATCH /api/projects/{idx}/members project.share project-index
DELETE /api/projects/{idx}/members project.share project-index
DELETE /api/projects/{idx}/members/self view.prefs project-index
PUT /api/projects/{idx}/model-relay project.write project-index
GET /api/projects/{idx}/repositories project.read project-index
POST /api/projects/{idx}/repositories secret.grant project-index
DELETE /api/projects/{idx}/repositories secret.revoke project-index
GET /api/projects/{idx}/repositories/available secret.grant project-index
POST /api/projects/{idx}/run run.start project-index
POST /api/projects/{idx}/stop run.stop project-index
GET /api/provider-calls project.read project
GET /api/provider-calls/{id} project.read project
POST /api/provider-calls/{id}/replay task.mutate project
GET /api/queue project.read project
GET /api/queue/stats project.read project
GET /api/quota/me public global
GET /api/quotas user.manage global
PUT /api/quotas/{identity} user.manage global
DELETE /api/quotas/{identity} user.manage global
GET /api/ratelimits project.read global
GET /api/replay-runs project.read project
POST /api/replay-runs task.mutate project
GET /api/replay-runs/{id} project.read project
GET /api/reproductions/{id} project.read project
POST /api/requirement-runs run.start project
GET /api/requirement-runs/{run} project.read project
POST /api/requirement-runs/{run}/cancel run.stop project
GET /api/requirements project.read project
POST /api/requirements task.mutate project
POST /api/requirements/approve task.mutate project
POST /api/requirements/draft task.mutate project
GET /api/requirements/export project.read project
POST /api/requirements/import task.mutate project
GET /api/requirements/lint project.read project
POST /api/requirements/lint task.mutate project
POST /api/requirements/plan task.mutate project
GET /api/requirements/spec project.read project
POST /api/requirements/spec/read task.mutate project
GET /api/requirements/trace project.read project
PUT /api/requirements/{id} task.mutate project
DELETE /api/requirements/{id} task.mutate project
GET /api/requirements/{id}/checks project.read project
POST /api/requirements/{id}/prove run.start project
POST /api/requirements/{id}/task task.mutate project
POST /api/requirements/{id}/verdict task.mutate project
POST /api/reset project.write project
GET /api/risk-matrix project.read project
POST /api/run run.start project
POST /api/run/adopt-build run.start project
POST /api/run/knowledge/citations public global
POST /api/run/knowledge/get public global
POST /api/run/knowledge/search public global
GET /api/scim user.manage global
POST /api/scim/credentials user.manage global
POST /api/scim/credentials/{id}/revoke user.manage global
POST /api/scim/credentials/{id}/rotate user.manage global
GET /api/secrets secret.own global
POST /api/secrets secret.own global
GET /api/secrets/catalog secret.request global
DELETE /api/secrets/{id} secret.own global
GET /api/secrets/{id}/authorize secret.own global
POST /api/secrets/{id}/renew secret.own global
POST /api/session/logout-all public global
GET /api/sessions session.admin global
DELETE /api/sessions/{id} session.admin global
GET /api/state project.read project
GET /api/static-token token.admin global
POST /api/static-token/retire token.admin global
GET /api/steps project.read project
POST /api/stop run.stop project
POST /api/suggest/add task.mutate project
POST /api/suggest/generate task.mutate project
GET /api/suggest/status project.read project
POST /api/task/add task.mutate project
POST /api/task/edit task.mutate project
POST /api/task/move task.mutate project
POST /api/task/remove task.mutate project
POST /api/task/status task.mutate project
GET /api/tasks project.read project
POST /api/tasks task.mutate project
POST /api/tasks/gate task.mutate project
POST /api/tasks/reorder task.mutate project
PUT /api/tasks/{id} task.mutate project
PATCH /api/tasks/{id} task.mutate project
DELETE /api/tasks/{id} task.mutate project
POST /api/tasks/{id}/approve task.mutate project
GET /api/tasks/{id}/attach sandbox.attach project
GET /api/tasks/{id}/attach/info sandbox.attach project
GET /api/tasks/{id}/attachments project.read project
GET /api/tasks/{id}/attachments/{aid} project.read project
DELETE /api/tasks/{id}/attachments/{aid} task.mutate project
GET /api/tasks/{id}/blocker project.read project
POST /api/tasks/{id}/blocker task.mutate project
POST /api/tasks/{id}/clear-aborted task.mutate project
POST /api/tasks/{id}/decompose task.mutate project
POST /api/tasks/{id}/decompose/apply task.mutate project
GET /api/tasks/{id}/details project.read project
GET /api/tasks/{id}/network project.read project
GET /api/tasks/{id}/provenance project.read project
POST /api/tasks/{id}/reopen-aborted task.mutate project
POST /api/tasks/{id}/reproduce run.start project
GET /api/tasks/{id}/reproductions project.read project
POST /api/tasks/{id}/revise task.mutate project
GET /api/teams user.manage global
POST /api/teams user.manage global
PATCH /api/teams/{slug} user.manage global
DELETE /api/teams/{slug} user.manage global
POST /api/teams/{slug}/members user.manage global
DELETE /api/teams/{slug}/members user.manage global
GET /api/telemetry audit.read global
POST /api/telemetry public global
GET /api/telemetry/config public global
GET /api/telemetry/sessions audit.read global
GET /api/timeline project.read project
GET /api/tokens token.admin global
POST /api/tokens token.admin global
DELETE /api/tokens/{id} token.admin global
POST /api/tokens/{id}/approve token.admin global
POST /api/transcribe task.mutate project
GET /api/users/holds user.manage global
POST /api/users/holds user.manage global
POST /api/users/holds/release user.manage global
POST /api/users/offboard user.manage global
GET /api/version public global
POST /api/voice task.mutate project
GET /api/ws project.read stream
GET /apple-touch-icon.png public global
GET /assets/ (subtree) public global
GET /auth/callback public global
GET /auth/login public global
POST /auth/logout public global
GET /auth/renew public global
GET /auth/sign-in.js public global
GET /charts/cloop-executor-controller.tgz public global
HEAD /charts/cloop-executor-controller.tgz public global
GET /device public global
GET /favicon.ico public global
GET /glasses public global
GET /icon-192.png public global
GET /icon-512.png public global
GET /icon.svg public global
GET /install.sh executor.manage global
HEAD /install.sh executor.manage global
GET /manifest.webmanifest public global
GET /metrics audit.read global
GET /scim/v2/ (subtree) public global
POST /scim/v2/ (subtree) public global
PUT /scim/v2/ (subtree) public global
PATCH /scim/v2/ (subtree) public global
DELETE /scim/v2/ (subtree) public global
GET /scim/v2/Groups public global
POST /scim/v2/Groups public global
GET /scim/v2/Groups/{id} public global
PUT /scim/v2/Groups/{id} public global
PATCH /scim/v2/Groups/{id} public global
DELETE /scim/v2/Groups/{id} public global
GET /scim/v2/ResourceTypes public global
GET /scim/v2/ResourceTypes/{id} public global
GET /scim/v2/Schemas public global
GET /scim/v2/Schemas/{id} public global
GET /scim/v2/ServiceProviderConfig public global
GET /scim/v2/Users public global
POST /scim/v2/Users public global
GET /scim/v2/Users/{id} public global
PUT /scim/v2/Users/{id} public global
PATCH /scim/v2/Users/{id} public global
DELETE /scim/v2/Users/{id} public global

REST daemon API (cloop serve)

A single-project REST daemon, separate from the hub. It has no permission model: every endpoint except the OpenAPI document is behind the bearer token when one is configured. Generated from routeTable() in pkg/apiserver/routes.go; the OpenAPI document it serves is hand-written and gated against the same table.

8 endpoints. When the server is started with a token, every one requires it except the 1 marked none.

Method Path Auth Description
GET /artifacts/{taskId} token The recorded output of a completed task
GET /metrics token Run metrics, as JSON or Prometheus text
GET /openapi.json none OpenAPI 3.0 description of this API
GET /plan token The project's goal and its full task list
POST /run/start token Start a run
POST /run/stop token Stop the running plan
GET /status token Lightweight run status and task counts
PATCH /tasks/{id} token Update a task's status, title, priority or tags