Skip to content

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

MethodPathOperation IDReBAC gateAudit relationBody cap
GET/v1/label-definitionsListLabelDefinitionsread on the scope object (per-row filter)label_definition.listn/a
POST/v1/label-definitionsCreateLabelDefinitionmanage on the scope objectlabel_definition.create8 KiB
GET/v1/label-definitions/{id}GetLabelDefinition(none — repo-layer visibility, invisible Definitions surface as 404)label_definition.readn/a
PATCH/v1/label-definitions/{id}UpdateLabelDefinitionmanage on the Definition's scopelabel_definition.update8 KiB
DELETE/v1/label-definitions/{id}DeleteLabelDefinitionmanage on the Definition's scopelabel_definition.deleten/a
POST/v1/labels/selectors/previewPreviewLabelSelectorplatform-level read on the selector surfacelabel_selector.preview16 KiB
GET/v1/objects/{kind}/{id}/labelsListObjectLabelsmaintainer on the target objectlabel_assignment.listn/a
PUT/v1/objects/{kind}/{id}/labelsPutObjectLabeldual: assign on the Definition AND maintainer on the target objectlabel_assignment.upsert8 KiB
DELETE/v1/objects/{kind}/{id}/labelsDeleteObjectLabelmaintainer on the target objectlabel_assignment.deleten/a
  • ListLabelDefinitions.limit is clamped at the handler to [1, 200] with default 50.
  • ListLabelDefinitions.cursor is opaque, HMAC-signed by the server; a tampered cursor surfaces as 400 invalid_cursor.
  • PreviewLabelSelector returns 200 even when the selector fails to parse — the response body carries an errors array with byte offsets so clients can render inline squiggles without distinguishing status codes. A 400 is reserved for malformed bodies (the request is not a LabelSelectorPreviewRequest).
  • DeleteObjectLabel is idempotent: a delete of an already-absent Assignment surfaces as 204, not 404. The endpoint is addressed by qualified_key rather than definition_id so a client can issue the delete without first resolving the Definition.

Scope discriminator

ListLabelDefinitions.scope and CreateLabelDefinition.scope accept the same three forms:

FormMeaningExample
platformDefinitions 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

OperationParameterTypeRequiredNotes
GetLabelDefinition / UpdateLabelDefinition / DeleteLabelDefinitionid (path)string (uuid)yesUUIDv7. Malformed → 400 invalid_definition_id.
ListObjectLabels / PutObjectLabel / DeleteObjectLabelkind (path)stringyesLowercase object-kind discriminator (resource, node, project, domain, workload, network, …). Unknown → 400 invalid_kind.
ListObjectLabels / PutObjectLabel / DeleteObjectLabelid (path)string (uuid)yesObject identifier (UUIDv7).
ListLabelDefinitionsscope (query)stringyesSee "Scope discriminator".
ListLabelDefinitionscursor (query)stringnoOpaque HMAC-signed continuation. Tampered → 400 invalid_cursor.
ListLabelDefinitionslimit (query)integerno[1, 200], default 50. Out-of-range → 400 invalid_limit.
DeleteObjectLabelqualified_key (query)stringyesFully-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:

CodeStatusWhereMeaning
invalid_scope400List/CreateScope discriminator does not match the regex.
invalid_cursor400ListHMAC verification failed.
invalid_limit400ListOut of [1, 200].
invalid_kind400per-objectUnknown object-kind discriminator.
definition_conflict409CreateA Definition with the same (scope_id, local_key) already exists.
immutable_violation409Update / PutObjectLabelAn immutable Definition rejected a value-schema or value change.
assignments_exist409DeleteLabelDefinitionThe Definition's on_delete=block refused a delete because Assignments still reference it.
cardinality_exceeded409PutObjectLabelPer-object 64-Assignment ceiling reached.
definition_not_found404Get / Update / Delete / PutObjectLabel / DeleteObjectLabelReferenced Definition does not exist OR is not visible to the caller (GetLabelDefinition collapses the 403 path into 404).
aggregate_invariant422Create / Update / Delete / PutObjectLabelReserved key, invalid value schema, scope mismatch, system-seed delete.
internal500every operationServer-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 the SelectorPort seam 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; the Label* operations and the LabelDefinitionRequest / LabelDefinition / LabelDefinitionListResponse / LabelDefinitionUpdateRequest / LabelAssignmentRequest / LabelAssignment / LabelAssignmentList / LabelSelectorPreviewRequest / LabelSelectorPreviewResponse schemas.
  • ../../../internal/labels/ — the bounded-context implementation: Definition aggregate, Assignment value object, the selector parser, and the SelectorPort seam.