# System

> The System operations of the Hub REST API (13 operations).

URL: https://docs.synap.live/docs/reference/hub-api/system · Generated from Synap-core/backend@c31e261ab (main)

Paths are relative to `/api/hub` on a pod.

## GET /health

Liveness probe

Public, no-auth health endpoint.

Responses:

- `200`: OK

## GET /manifest

AI agent capability manifest

Static orientation document for AI agents. Returns view types, bento block kinds, AI inline pattern syntax, browser-native cell keys, and pointers to live discovery endpoints. No DB queries — safe to cache for the duration of an agent session.

Responses:

- `200`: Manifest payload

## GET /discover

Runtime discovery — profiles + command tree

Returns entity profiles with property schemas and the CLI command tree. AI agents call this once at session start for ground-truth schema instead of relying on static skill descriptions. Pass ?summary=true for a lighter tier (no property schemas; rows ranked by usage with entityCount / lastActivityAt / rank / origin; ?sort=name for alphabetical; ?groups=origin adds top-level groups), then pass ?profileSlugs=task,person to load schemas only for the profiles you need.

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `userId` | query | string | yes |  |
| `workspaceId` | query | string |  |  |
| `summary` | query | "true" \| "false" |  |  |
| `profileSlugs` | query | string |  | Comma-separated profile slugs to return with property schemas, e.g. task,person. Omit to load every profile schema. |
| `sort` | query | "usage" \| "name" |  |  |
| `groups` | query | "origin" |  |  |
| `fill` | query | "true" \| "false" |  |  |

Responses:

- `200`: Discovery payload
- `400`: Invalid query param
- `403`: Forbidden
- `500`: Internal error

## GET /orient

Lens map — workspaces, projects, profile sample, identity

Canonical session-bootstrap orientation. Returns the user's workspaces (operational domains), projects (cross-cutting initiatives), a profile sample, and identity. Pass ?detail=full for descriptions + full onboarding + per-workspace profiles.

Responses:

- `200`: Lens map
- `403`: Forbidden
- `500`: Internal error

## GET /pod/config

Unified pod configuration — workspaces + capabilities/automations/playbooks + feeds graph

Read-only DTO powering the CLI's unified pod-configuration view: every workspace the caller belongs to (including unstamped orphans, with inferred provenance), pod-wide + workspace-scoped capabilities/automations/playbooks, and the workspace-to-workspace `feeds` graph.

Responses:

- `200`: Pod configuration
- `403`: Forbidden
- `500`: Internal error

## POST /pod/adopt

Adopt an orphan/existing workspace into a known template

Stamps the workspace's marketplace identity (packageSlug/packageVersion) and additively reconciles it to the template's definition — never destructive, never a second workspace. Governed: owner/editor floor + agent-governance ladder (grant or propose).

Request body (`application/json`): object

| Field | Type | Required | Description |
|---|---|---|---|
| `workspaceId` | string | yes |  |
| `templateSlug` | string | yes |  |

Responses:

- `200`: Adopted
- `202`: Proposed for review
- `403`: Forbidden
- `404`: Workspace or template not found
- `500`: Internal error

## POST /federation/oidc-config

Receive a CP→pod OIDC federation client push

Persists the control-plane-minted OIDC client (inside a CP-signed assertion) into pod_settings.settings.federationOidcClient so the pod's Kratos can federate sign-in through the CP. Authenticated by the CP's issuer signature (approved issuer, aud = this pod), not the Hub key. Idempotent.

Request body (`application/json`): object

| Field | Type | Required | Description |
|---|---|---|---|
| `assertion` | string | yes |  |

Responses:

- `200`: Stored
- `400`: Invalid body
- `401`: Assertion did not verify
- `403`: Assertion is for a different pod, or names an OIDC issuer other than its signer
- `500`: Internal error

## GET /resolve/{id}

Universal ID resolver — what type is this ID?

Given any UUID, returns the browser-routable type of the thing it names (see `type`), or `unknown` when nothing the caller can see has that id. AI agents use this before opening something in the browser — they don't need to know the type upfront.

Responses:

- `200`: Resolved type information
- `403`: Forbidden
- `500`: The lookup FAILED (distinct from a 200 `type: unknown`, which means it ran and nothing you can see matched)

## GET /resolve

Resolve an object by NAME (not id)

Find objects of a given kind by name — the navigable-by-name half of the graph. Returns every match (names aren't unique) + `unique`. Pass `subtype` to narrow (entity profileSlug, view type, tool/skill kind).

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `kind` | query | string | yes |  |
| `name` | query | string | yes |  |
| `subtype` | query | string |  |  |

Responses:

- `200`: Matches
- `400`: Bad request
- `403`: Forbidden

## GET /graph/{type}/{id}

Fetch an object + its typed neighbour graph

Returns ANY object (entity, view, channel, session, playbook, tool, skill, automation, project, document) plus everything it is linked to — typed as { kind, subtype, name } per neighbour, across the config (links) and data (relations/property/channel) graphs. Graph by default: never fetch a thing without its place in the pod.

Responses:

- `200`: Graph envelope
- `400`: Bad request
- `403`: Forbidden
- `404`: Object not found
- `500`: Internal error

## GET /centrality/status

PageRank centrality status for the caller

Reports whether the Phase-3 PageRank centrality has populated the `entity_centrality` table for the calling user: row count, freshness (last/oldest computedAt), and the top entities by score. Tolerates the table being absent (migration pending) — returns `computed:false` rather than erroring.

Responses:

- `200`: Centrality status
- `403`: Forbidden

## POST /centrality/recompute

Enqueue a PageRank centrality recompute

Enqueues the global PageRank job (same queue the Phase-3 worker + cron use). The job runs in the background and UPSERTs `entity_centrality`. Returns `triggered:true` on enqueue, or `triggered:false` with a note if the job queue is unavailable.

Responses:

- `200`: Recompute enqueue result
- `403`: Forbidden

## GET /health/dependencies

Dependency reachability probe (readiness)

Reports whether the pod's downstream dependencies are reachable — currently the Intelligence Service. Separate from GET /health, which is an unauthenticated liveness probe and deliberately performs no network I/O. The IS URL is resolved through the SAME resolution real requests use (capability-first → workspace → user → pod default → env fallback), so the probe cannot check a URL requests never touch. Always returns 200 with a reported state: an unreachable dependency is data, not an error. `state` distinguishes `unreachable` (URL known, probe failed) from `unresolved` (resolution itself failed).

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `workspaceId` | query | string |  | Resolve the IS as this workspace would — matches the routing a capture from that workspace gets. Omit for pod-level resolution. |

Responses:

- `200`: Dependency report. 200 even when a dependency is down — read `status`.
- `401`: Unauthorized
- `403`: Forbidden — hub-protocol.read required
