> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcliq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Hub Control Plane

> Public Hub API, realms, daemon enroll, push dispatch.

<div className="cliq-kicker">control plane</div>

Humans use a **user PAT** (`cliq_tok_…`, including session-scoped login PATs) with a grant. Daemons use a **realm token** (`cliq_dt_…`).\
Interactive OpenAPI on this site: **Hub API** tab — one catalog covering `/v1`, `/internal` (BFF-only), and `/a2a`.

## Auth

| Credential | Who |
| - | - |
| `Bearer cliq_tok_…` | Console (via BFF `target_token`), `cliq login` → `hub.session`, CI (`cliq.api_key`) — limited by grant |
| `Bearer cliq_dt_…` | Daemon register, heartbeat, exclusive-run claim |
| BFF session cookie | Browser only — `POST /v1/session/*`; cookie never holds the PAT |
| BFF service grant | Private `/internal` only (authenticate, signup, delete user/org, …) |

User PATs are rejected for daemon enroll / claim. Grants: [Auth](/auth).

## Realms & tokens

Realm CRUD, members, desired teams (`add_team` / `remove_team`), and A2A admin (`POST /v1/realms/a2a` `{ action }`) are under `/v1/realms/*` — see [Realms](/realms). Roster: `POST /v1/teams/get` `{ realm_id }`. Invite search: `POST /v1/users/get` `{ realm_id, query }`. A2A bearer: `POST /v1/auth/generate_token` | `rotate_token` `{ type: "a2a", realm_id }`. Public card/send: **A2A** tab.

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
# Mint realm token (realm member)
curl -s https://api.cliqhub.io/v1/auth/generate_token \
  -H "Authorization: Bearer $CLIQ_TOK" \
  -H "Content-Type: application/json" \
  -d '{"type":"realm","realm_ids":["…"],"name":"laptop"}'
```

Or: `cliq auth token laptop --realm <realm_id>`. Create response is **`{ token, name, id, realm_ids }`** (plaintext once). Revoke via `/auth/revoke_token` with `type: "realm"`.

Public invoke (host root, not `/v1`): `GET /a2a/o/{org}/r/{slug}/.well-known/agent-card.json`, `POST /a2a/o/{org}/r/{slug}/send`.

## Platform agents (catalog)

Hub SoT is **`cliq.agent_catalog`** (`AgentCatalogEntry`), exposed as **Hub API → Agents** (`/v1/agents/*`). Overview: [Agents](/hub-api/overview#agents).

| Endpoint | Purpose |
| - | - |
| `POST /v1/agents/get` | List system + org custom agents (`query`, `names`, `agent_type`, `include_manifest`) |
| `POST /v1/agents/get_by_id` | Single agent by **`name`** (+ optional `version`) |
| `POST /v1/agents/register` | Create/overwrite custom agent (`force` for same version); Hub generates `id` |
| `POST /v1/agents/deregister` | Soft-delete custom agent(s) by name (+ optional version); **409** if published teams still reference |
| `POST /v1/agents/get_settings` | Manifest settings schema + values (optional `realm_id`) |
| `POST /v1/agents/update_settings` | Upsert/clear settings at org or realm scope |

Built-ins (`is_system: true`) ship with cliqd — there is **no** `/v1/agent-pack/*`. Daemons resolve agents from the local agents path (`CLIQ_AGENTS_PATH` / `agents.path`); register custom agents with **`cliq agent register`**. Team install does not download agent binaries.

Realm-scoped agent **settings** use `/v1/agents/get_settings` and `update_settings` with optional `realm_id` (no `/v1/realms/agents/*`).

## Daemon enroll

Daemons join a realm with a **realm token** (`cliq_dt_…`), not with `/v1/realms/*` connect/disconnect.

On `cliq login` / daemon boot, the daemon orchestrates mint + key fetch itself
(no Hub shortcut endpoint):

1. `POST /v1/auth/generate_token` `{ type: "realm", realm_ids, permissions: { auto_enrolled: true, … } }`
2. `POST /v1/auth/get_dispatch_public_key` `{ realm_id }`
3. `POST /v1/daemons/register` with the minted `cliq_dt_…`
4. Heartbeat for liveness

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
# Manual mint (realm member) then register
curl -s https://api.cliqhub.io/v1/auth/generate_token \
  -H "Authorization: Bearer $CLIQ_TOK" -H "Content-Type: application/json" \
  -d '{"type":"realm","realm_ids":["…"],"name":"edge-1"}'
# → { token: "cliq_dt_…", … }

curl -s https://api.cliqhub.io/v1/daemons/register \
  -H "Authorization: Bearer $CLIQ_DT" -H "Content-Type: application/json" \
  -d '{"daemon_id":"edge-1","hostname":"…"}'

curl -s https://api.cliqhub.io/v1/daemons/heartbeat \
  -H "Authorization: Bearer $CLIQ_DT" -H "Content-Type: application/json" \
  -d '{"daemon_id":"edge-1"}'
```

`auto_enrolled: true` marks bootstrap tokens for bulk revoke on logout.

`POST /v1/realms/a2a` actions `mesh_connect` / `mesh_disconnect` talk to an **external A2A mesh** (e.g. Svantic) — they do not register daemons.

`POST /daemons/get` (user credential) returns daemons in your realm intersection — same data as CliqHub **Daemons**.

## Daemon → Hub durability

Every daemon buffers state changes (runs, teams, workspaces, events) in a
local SQLite `hub_outbox` and ships them to Hub with retry. Hub → daemon
commands use the separate `command_outbox`. There is **no** operator HTTP
admin surface for either queue (no `/daemons/outbox/*`, no daemon
`/v1/outbox/*`).

When the local dead count crosses zero, the daemon emits
`daemon.outbox.dead` / `daemon.outbox.recovered` catalog events — wire
notification rules on those types for paging.

For default-notify realm events (`run.failed`, `phase.input_required`,
…), Hub prefers matching **notification rules** over the built-in
`realm:all_users` in-app channel. Explicit `notify.channels` refs still
win; with no rules and no refs, delivery falls back to all-users.

## Dispatch

Exactly one of `daemon_ids` or `realm_id` for install. **Realm run** (Hub UI):

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
curl -s https://api.cliqhub.io/v1/runs/enqueue \
  -H "Authorization: Bearer $CLIQ_TOK" \
  -H "Content-Type: application/json" \
  -d '{"realm_id":"…","team_id":"@cliq/hello-world","inputs":{}}'
```

Pin to one daemon: `POST /runs/enqueue` with `daemon_id` + `workspace_id` + `team_id`.\
Enqueue is a **write** on `dispatch` / `runs`. Daemon claims via `POST /runs/claim`, then Hub calls local `/v1/execute`.

**Team install / uninstall** (fleet fan-out): `POST /v1/teams/install`, `/teams/uninstall` — `daemon_ids` **or** `realm_id`.

**Live daemon inventory:** `POST /v1/teams/get` / `/workspaces/get` `{ daemon_id }` (live pull + Hub cache).

**Run mirror:** daemon outboxes `POST /v1/runs/create` with `workspace_id` + `workspace_path` (+ optional name); Hub ensures the workspace row and team link in that call (no separate workspace upsert/add\_team routes).

**Dispatch wire keys:** `POST /v1/auth/get_dispatch_public_key`, `/auth/rotate_dispatch_key` `{ realm_id }`.\
**Daemon wire JWT:** `POST /v1/auth/generate_token` `{ type: "daemon_wire", realm_id }`.

**Run controls** (user → daemon command outbox): `POST /v1/runs/cancel`, `/runs/resume_from_phase`, `/runs/supply_inputs`, `/runs/force_terminate`. See [Concepts](/concepts).

## Internal vs public

| Need | Where |
| - | - |
| Session login / act-as / logout | BFF `POST /v1/session/*` (see [Auth](/auth)) |
| Publish, realms, tokens, dispatch | **Hub API** tab (`Bearer cliq_tok_…` / `cliq_dt_…`) |
| Create/delete users & orgs, signup | BFF → Hub `/internal` (not on public docs) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.