Spaces

Operations of the Space Registry API. Paths are relative to your deployment's API host.

GET /api/space-registry-api/spaces

List the caller org's Spaces (newest first). Org-less System Spaces are not enumerated here.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
limitquerynumbernoMax rows to return (1–500, default 100).
StatusDescriptionBody
200application/json SpaceDtoDataArrayEnvelope

POST /api/space-registry-api/spaces

Declare a Space (idempotent on its canonical refUri). Requires the org admin role.

Nothing about the ancestry needs to exist first — a declaration on a project ref whose org ref has no row is valid, because ancestry is derived from the ref grammar rather than stored.

Authentication: bearer token (Authorization: Bearer <token>)

Request body: application/json CreateSpaceDto

StatusDescriptionBody
201application/json SpaceDtoDataEnvelope

GET /api/space-registry-api/spaces/ancestors

Persisted ancestor chain (closest-first, self at index 0) up to System.

Same tenant rule as /spaces/resolve: the ref is fenced against the request-context organization. The org-less System tail of the chain is returned for every caller — it is what carries the inherited classification floor.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
refquerystringyes
StatusDescriptionBody
200application/json SpaceAncestorsDtoDataEnvelope

GET /api/space-registry-api/spaces/classification

Resolve the effective classification over the ancestry, with its source.

The monotone JOIN over every declaration on the ref and its ancestors — NOT most-specific-wins, which is the whole safety property: a project declaring public cannot escape an org that declared secret. The response carries the ANSWER as well as the value — configured, source, the full inheritancePath (silent rungs included) and clampedByRefUri, the ancestor that overrode a nearer declaration. Same tenant rule as /spaces/resolve. A data-classification label is exactly the kind of metadata a cross-tenant read must not expose, so a ref naming another organization is refused with 403.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
refquerystringyes
StatusDescriptionBody
200application/json SpaceClassificationDtoDataEnvelope

GET /api/space-registry-api/spaces/experience

Read the caller org's shell-presentation policy.

Presentation only. A member whose shell is portal-focused still reaches any surface they are authorized for; the fences are the per-surface guards and the PDP, for every member equally.

Authentication: bearer token (Authorization: Bearer <token>)

StatusDescriptionBody
200application/json OrgExperienceSettingsDtoDataEnvelope

PUT /api/space-registry-api/spaces/experience

Write the caller org's shell-presentation policy. Requires the org admin role.

Administrators are never confined. This decides what the shell leads with, and grants or withholds nothing.

Authentication: bearer token (Authorization: Bearer <token>)

Request body: application/json UpdateOrgExperienceSettingsDto

StatusDescriptionBody
200application/json OrgExperienceSettingsDtoDataEnvelope

GET /api/space-registry-api/spaces/experience/app-theme

Read the caller org's app theme. Any organization member.

An organization that states no theme returns 200 with configured: false, with appTheme: null, locked: false: the frontend applies its default theme. configured: true, appTheme: null, locked: true locks members to the org brand with no built-in pick. The id is opaque here; the web host checks it against its own theme registry.

Authentication: bearer token (Authorization: Bearer <token>)

StatusDescriptionBody
200application/json OrgAppThemeDtoDataEnvelope

PUT /api/space-registry-api/spaces/experience/app-theme

State or clear the caller org's app theme. Requires the capability space:space.experience.app-theme.update@1 (org admins by default).

A kebab-case theme id (1..64 characters) states the theme; locked: true also stops members overriding it (omitted means false). appTheme: null, locked: true locks members to the org brand with no built-in pick; appTheme: null unlocked returns to the default. Only the shape is checked: a bad shape is refused (400), never normalised. Every change is audited. Presentation only; it grants and withholds nothing.

Authentication: bearer token (Authorization: Bearer <token>)

Request body: application/json UpdateOrgAppThemeDto

StatusDescriptionBody
200application/json OrgAppThemeDtoDataEnvelope

GET /api/space-registry-api/spaces/governance

Read the caller org's classification-governance controls (separation of duties on downgrades).

An organization that has never armed anything returns 200 with configured: false — never a 404. Un-configured is an answer.

Authentication: bearer token (Authorization: Bearer <token>)

StatusDescriptionBody
200application/json OrgGovernanceSettingsDtoDataEnvelope

PUT /api/space-registry-api/spaces/governance

Arm or disarm approval-on-downgrade for the caller org. Requires the org admin role.

OFF by default and on a fresh install. Arming requires a data-governance group — an armed control with nobody to ask refuses every downgrade and can approve none.

Authentication: bearer token (Authorization: Bearer <token>)

Request body: application/json UpdateOrgGovernanceSettingsDto

StatusDescriptionBody
200application/json OrgGovernanceSettingsDtoDataEnvelope

GET /api/space-registry-api/spaces/growth

Read the caller org's growth policy (who may build and publish).

An organization that has never written one returns 200 with configured: false and permissive defaults — never a 404, and never a fence nobody armed. Un-configured is an answer.

Authentication: bearer token (Authorization: Bearer <token>)

StatusDescriptionBody
200application/json OrgGrowthSettingsDtoDataEnvelope

PUT /api/space-registry-api/spaces/growth

Write the caller org's growth policy. Requires the org admin role.

Configuration TIGHTENS. Authoring and internal publishing are policy toggles that move in either direction; external publishing is a floor (always a Decision, never below org admin) and a looser value is refused. A field omitted here is CLEARED, not left standing.

Authentication: bearer token (Authorization: Bearer <token>)

Request body: application/json UpdateOrgGrowthSettingsDto

StatusDescriptionBody
200application/json OrgGrowthSettingsDtoDataEnvelope

GET /api/space-registry-api/spaces/resolve

Resolve a Space by its xema:// URI.

The organization is selected through the X-Xema-Org-Id request context, NOT through the ref. A ref naming a different organization is refused with 403 SPACE_CROSS_ORG_ACCESS_FORBIDDEN — never silently preferred. Org-less refs (xema://system, xema://biomes/<id>, xema://users/<id>, xema://sessions/<id>) are platform-global and remain readable by every authenticated caller.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
refquerystringyes
StatusDescriptionBody
200application/json SpaceDtoDataEnvelope

GET /api/space-registry-api/spaces/worker-egress

Resolve a Space's worker internet egress over its ancestry, with its source.

The most restrictive declaration on the ref and its ancestors — NOT most-specific-wins: a project declaring open cannot widen an org that declared governed. A chain in which nobody declares anything returns 200 with configured: false and a null value, never a 404 and never a default. This is the SPACE tier only: the pod's effective mode also joins the deployment's posture and the agent's and the session's narrowings. Same tenant rule as /spaces/resolve.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
refquerystringyes
StatusDescriptionBody
200application/json SpaceWorkerEgressDtoDataEnvelope

GET /api/space-registry-api/spaces/{id}

Fetch a Space by opaque id.

A Space owned by another organization returns the SAME 404 as an unknown id — an opaque id names no tenant, so a distinct 403 would make this route a cross-tenant existence oracle.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
idpathstringyes
StatusDescriptionBody
200application/json SpaceDtoDataEnvelope

DELETE /api/space-registry-api/spaces/{id}

Delete a Space override by id. Requires the org admin role. Refuses system-managed Spaces (fail-fast).

A row is an override, not a node, so a delete orphans nothing: every descendant ref keeps resolving and simply loses one contribution to the classification join.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
idpathstringyes
StatusDescriptionBody
204Space deleted.

PATCH /api/space-registry-api/spaces/{id}/classification

Set/change a Space's own classification. Requires the org admin role. Downgrades require allowDowngrade, are audited, and may require approval.

RAISING sensitivity takes no extra path. LOWERING it requires allowDowngrade and writes a durable downgrade audit carrying the blast radius as measured at that moment. If the organization has armed separation of duties, the write does NOT happen: a Decision is opened for the data-governance group and this responds 409 SPACE_DOWNGRADE_AWAITING_DECISION with the decision id.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
idpathstringyes

Request body: application/json UpdateClassificationDto

StatusDescriptionBody
200application/json SpaceDtoDataEnvelope

PUT /api/space-registry-api/spaces/{id}/worker-egress

Set, change or clear a Space's own worker internet egress. Requires the org admin role.

Declared on an ORG or a PROJECT space only; any other tier is refused with 422 SPACE_WORKER_EGRESS_TIER_NOT_ADMISSIBLE. null clears the declaration. Every change is recorded in the same transaction and announced as space.worker-egress.changed; a write that changes nothing records and announces nothing. A declaration can only ever TIGHTEN what the deployment allows — open here widens nothing.

Authentication: bearer token (Authorization: Bearer <token>)

ParameterInTypeRequiredDescription
idpathstringyes

Request body: application/json UpdateWorkerEgressDto

StatusDescriptionBody
200application/json SpaceDtoDataEnvelope