Appearance
Labels HTTP API
This is the reference for the Label-Registry HTTP surface. It maps each operation to its OpenAPI schema, the per-call ReBAC permission gate, the audit-row taxonomy, and the closed Problem.code set the handlers emit. The wire-contract origin is api/openapi/plexsphere-v1.yaml; this doc is a map, not a duplicate contract — for the bounded-context narrative (Definition vs Assignment, scope hierarchy, selector grammar, on-delete policies, immutability invariant, 64-cardinality ceiling) see ../../contexts/labels/index.md; for the cursor-paginated list idiom and the PermissionDenied problem shape see ../api/projects.md.
Operations
| Method | Path | Operation ID | ReBAC gate | Audit relation | Body cap |
|---|---|---|---|---|---|
| GET | /v1/label-definitions | ListLabelDefinitions | read on the scope object (per-row filter) | label_definition.list | n/a |
| POST | /v1/label-definitions | CreateLabelDefinition | manage on the scope object | label_definition.create | 8 KiB |
| GET | /v1/label-definitions/{id} | GetLabelDefinition | (none — repo-layer visibility, invisible Definitions surface as 404) | label_definition.read | n/a |
| PATCH | /v1/label-definitions/{id} | UpdateLabelDefinition | manage on the Definition's scope | label_definition.update | 8 KiB |
| DELETE | /v1/label-definitions/{id} | DeleteLabelDefinition | manage on the Definition's scope | label_definition.delete | n/a |
| POST | /v1/labels/selectors/preview | PreviewLabelSelector | platform-level read on the selector surface | label_selector.preview | 16 KiB |
| GET | /v1/objects/{kind}/{id}/labels | ListObjectLabels | maintainer on the target object | label_assignment.list | n/a |
| PUT | /v1/objects/{kind}/{id}/labels | PutObjectLabel | dual: assign on the Definition AND maintainer on the target object | label_assignment.upsert | 8 KiB |
| DELETE | /v1/objects/{kind}/{id}/labels | DeleteObjectLabel | maintainer on the target object | label_assignment.delete | n/a |
ListLabelDefinitions.limitis clamped at the handler to[1, 200]with default50.ListLabelDefinitions.cursoris opaque, HMAC-signed by the server; a tampered cursor surfaces as400 invalid_cursor.PreviewLabelSelectorreturns200even when the selector fails to parse — the response body carries anerrorsarray with byte offsets so clients can render inline squiggles without distinguishing status codes. A400is reserved for malformed bodies (the request is not aLabelSelectorPreviewRequest).DeleteObjectLabelis idempotent: a delete of an already-absent Assignment surfaces as204, not404. The endpoint is addressed byqualified_keyrather thandefinition_idso a client can issue the delete without first resolving the Definition.
Scope discriminator
ListLabelDefinitions.scope and CreateLabelDefinition.scope accept the same three forms:
| Form | Meaning | Example |
|---|---|---|
platform | Definitions visible to every Domain on the deployment. | platform |
domain:<uuid> | Definitions scoped to a single Domain. | domain:0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a000 |
project:<uuid> | Definitions scoped to a single Project. | project:0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a042 |
The discriminator is a single string rather than a pair of query params so the SpiceDB scope-object derivation can treat the three forms as a single coordinate. The pattern is enforced at the schema level (^(platform|domain:[0-9a-fA-F-]{36}|project:[0-9a-fA-F-]{36})$); malformed input surfaces as 400 invalid_scope.
Path & query parameters
| Operation | Parameter | Type | Required | Notes |
|---|---|---|---|---|
| GetLabelDefinition / UpdateLabelDefinition / DeleteLabelDefinition | id (path) | string (uuid) | yes | UUIDv7. Malformed → 400 invalid_definition_id. |
| ListObjectLabels / PutObjectLabel / DeleteObjectLabel | kind (path) | string | yes | Lowercase object-kind discriminator (resource, node, project, domain, workload, network, …). Unknown → 400 invalid_kind. |
| ListObjectLabels / PutObjectLabel / DeleteObjectLabel | id (path) | string (uuid) | yes | Object identifier (UUIDv7). |
| ListLabelDefinitions | scope (query) | string | yes | See "Scope discriminator". |
| ListLabelDefinitions | cursor (query) | string | no | Opaque HMAC-signed continuation. Tampered → 400 invalid_cursor. |
| ListLabelDefinitions | limit (query) | integer | no | [1, 200], default 50. Out-of-range → 400 invalid_limit. |
| DeleteObjectLabel | qualified_key (query) | string | yes | Fully-qualified Label key (e.g. platform/env, acme:checkout/owner). |
Schemas
The OpenAPI spec is the authoritative source for field shapes. The schemas this surface uses are:
- Definitions:
LabelDefinitionRequest,LabelDefinitionUpdateRequest,LabelDefinition,LabelDefinitionListResponse. - Assignments:
LabelAssignmentRequest,LabelAssignment,LabelAssignmentList. - Selector preview:
LabelSelectorPreviewRequest,LabelSelectorPreviewResponse.
For the field-level shapes refer to api/openapi/plexsphere-v1.yaml under components/schemas/. The Definition shape carries the on_delete policy (block / cascade / orphan), the immutability flag, and the value schema; the Assignment shape carries the resolved qualified_key derived from the parent Definition's scope plus its local_key.
Error taxonomy
All error responses use the shared Problem envelope (application/problem+json). The 403 path uses the richer PermissionDenied shape, which carries the ReBAC denial reason (e.g. insufficient_relation), the traversed relation_path, and the request correlation_id so a client can render an actionable error. The closed code set this surface emits:
| Code | Status | Where | Meaning |
|---|---|---|---|
invalid_scope | 400 | List/Create | Scope discriminator does not match the regex. |
invalid_cursor | 400 | List | HMAC verification failed. |
invalid_limit | 400 | List | Out of [1, 200]. |
invalid_kind | 400 | per-object | Unknown object-kind discriminator. |
definition_conflict | 409 | Create | A Definition with the same (scope_id, local_key) already exists. |
immutable_violation | 409 | Update / PutObjectLabel | An immutable Definition rejected a value-schema or value change. |
assignments_exist | 409 | DeleteLabelDefinition | The Definition's on_delete=block refused a delete because Assignments still reference it. |
cardinality_exceeded | 409 | PutObjectLabel | Per-object 64-Assignment ceiling reached. |
definition_not_found | 404 | Get / Update / Delete / PutObjectLabel / DeleteObjectLabel | Referenced Definition does not exist OR is not visible to the caller (GetLabelDefinition collapses the 403 path into 404). |
aggregate_invariant | 422 | Create / Update / Delete / PutObjectLabel | Reserved key, invalid value schema, scope mismatch, system-seed delete. |
internal | 500 | every operation | Server-side failure path. |
A 403 response carries Problem.code = permission_denied plus the extended PermissionDenied fields documented in ../api/authz.md.
Cross-references
../../contexts/labels/index.md— bounded-context reference for Definitions, Assignments, the scope hierarchy, the on-delete policy semantics, the 64-cardinality ceiling, the system-seed reserved keys, and theSelectorPortseam consumed by policy and provisioning.../../how-to/labels/assign-object-labels.md— operator recipe that exercises Definitions CRUD, Assignment upsert, and selector preview against the kind dev.../../how-to/labels/manage-label-definitions.md— operator how-to for managing Definitions across scopes.../../../api/openapi/plexsphere-v1.yaml— OpenAPI 3.1 spec; theLabel*operations and theLabelDefinitionRequest/LabelDefinition/LabelDefinitionListResponse/LabelDefinitionUpdateRequest/LabelAssignmentRequest/LabelAssignment/LabelAssignmentList/LabelSelectorPreviewRequest/LabelSelectorPreviewResponseschemas.../../../internal/labels/— the bounded-context implementation:Definitionaggregate,Assignmentvalue object, the selector parser, and theSelectorPortseam.