docs.jsonld
type: ApiDocumentation
title: Hello API Platform
entrypoint: /
supported class: Error
supported class: ConstraintViolation
supported class: ApiToken
supported class: Comment
supported class: CustomFieldDefinition
supported class: Discussion
supported class: MediaObject
supported class: Notification
supported class: Page
supported class: Project
supported class: PushSubscription
supported class: Segment
supported class: Space
supported class: Tag
supported class: Task
supported class: User
supported class: Group
supported class: Entrypoint
supported class: ConstraintViolationList

Error
type: Class
title: Error
supported property: SupportedProperty
  property: Error/id
  title: id
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Error/title
  title: title
  required: false
  readable: true
  writeable: false
  description: A short, human-readable summary of the problem.
supported property: SupportedProperty
  property: Error/detail
  title: detail
  required: false
  readable: true
  writeable: false
  description: A human-readable explanation specific to this occurrence of the problem.
supported property: SupportedProperty
  property: Error/instance
  title: instance
  required: false
  readable: true
  writeable: false
  description: A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
supported property: SupportedProperty
  property: Error/type
  title: type
  required: false
  readable: true
  writeable: false
  description: A URI reference that identifies the problem type
supported property: SupportedProperty
  property: Error/meta
  title: meta
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Error/source
  title: source
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Error/description
  title: description
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/trace
  title: trace
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/message
  title: message
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/code
  title: code
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/file
  title: file
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/line
  title: line
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Error/trace
  title: trace
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a Error resource.
  method: GET
  returns: Error
  title: getError
description: A representation of common errors.
sub class of: Error

Error/id
type: Property
domain: Error
the hydra core vocabulary: id
range: string

Error/title
type: Property
domain: Error
the hydra core vocabulary: title
range: string

Error/detail
type: Property
domain: Error
the hydra core vocabulary: detail
range: string

Error/instance
type: Property
domain: Error
the hydra core vocabulary: instance
range: string

Error/type
type: Property
domain: Error
the hydra core vocabulary: type
range: string

Error/meta
type: Property
domain: Error
the hydra core vocabulary: meta

Error/source
type: Property
domain: Error
the hydra core vocabulary: source

Error/description
type: Property
domain: Error
the hydra core vocabulary: description
range: string

Error/trace
type: Property
domain: Error
the hydra core vocabulary: trace

Error/message
type: Property
domain: Error
the hydra core vocabulary: message
range: string

Error/code
type: Property
domain: Error
the hydra core vocabulary: code
range: integer

Error/file
type: Property
domain: Error
the hydra core vocabulary: file
range: string

Error/line
type: Property
domain: Error
the hydra core vocabulary: line
range: integer

ConstraintViolation
type: Class
title: ConstraintViolation
supported property: SupportedProperty
  property: ConstraintViolation/status
  title: status
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: ConstraintViolation/errorTitle
  title: errorTitle
  required: false
  readable: false
  writeable: true
supported property: SupportedProperty
  property: ConstraintViolation/violations
  title: violations
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/detail
  title: detail
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/message
  title: message
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: ConstraintViolation/code
  title: code
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: ConstraintViolation/file
  title: file
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/line
  title: line
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/description
  title: description
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/type
  title: type
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/title
  title: title
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/instance
  title: instance
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/statusCode
  title: statusCode
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/headers
  title: headers
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/trace
  title: trace
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolation/previous
  title: previous
  required: false
  readable: true
  writeable: true
supported operation: Operation
  type: FindAction
  description: Retrieves a ConstraintViolation resource.
  method: GET
  returns: ConstraintViolation
  title: getConstraintViolation
supported operation: Operation
  type: FindAction
  description: Retrieves a ConstraintViolation resource.
  method: GET
  returns: ConstraintViolation
  title: getConstraintViolation
supported operation: Operation
  type: FindAction
  description: Retrieves a ConstraintViolation resource.
  method: GET
  returns: ConstraintViolation
  title: getConstraintViolation
supported operation: Operation
  type: FindAction
  description: Retrieves a ConstraintViolation resource.
  method: GET
  returns: ConstraintViolation
  title: getConstraintViolation
description: Unprocessable entity
sub class of: Error

ConstraintViolation/status
type: Property
domain: ConstraintViolation
the hydra core vocabulary: status
range: integer

ConstraintViolation/errorTitle
type: Property
domain: ConstraintViolation
the hydra core vocabulary: errorTitle
range: string

ConstraintViolation/violations
type: Property
domain: ConstraintViolation
the hydra core vocabulary: violations
range: ConstraintViolationList

ConstraintViolation/detail
type: Property
domain: ConstraintViolation
the hydra core vocabulary: detail
range: string

ConstraintViolation/message
type: Property
domain: ConstraintViolation
the hydra core vocabulary: message
range: string

ConstraintViolation/code
type: Property
domain: ConstraintViolation
the hydra core vocabulary: code
range: string
range: integer

ConstraintViolation/file
type: Property
domain: ConstraintViolation
the hydra core vocabulary: file
range: string

ConstraintViolation/line
type: Property
domain: ConstraintViolation
the hydra core vocabulary: line
range: integer

ConstraintViolation/description
type: Property
domain: ConstraintViolation
the hydra core vocabulary: description
range: string

ConstraintViolation/type
type: Property
domain: ConstraintViolation
the hydra core vocabulary: type
range: string

ConstraintViolation/title
type: Property
domain: ConstraintViolation
the hydra core vocabulary: title
range: string

ConstraintViolation/instance
type: Property
domain: ConstraintViolation
the hydra core vocabulary: instance
range: string

ConstraintViolation/statusCode
type: Property
domain: ConstraintViolation
the hydra core vocabulary: statusCode
range: integer

ConstraintViolation/headers
type: Property
domain: ConstraintViolation
the hydra core vocabulary: headers

ConstraintViolation/trace
type: Property
domain: ConstraintViolation
the hydra core vocabulary: trace

ConstraintViolation/previous
type: Property
domain: ConstraintViolation
the hydra core vocabulary: previous
range: integer

ApiToken
type: Class
title: ApiToken
supported property: SupportedProperty
  property: ApiToken/name
  title: name
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: ApiToken/accessPolicy
  title: accessPolicy
  required: false
  readable: true
  writeable: true
  description: Optional capability narrowing for this token, in the shared {@see AccessPolicy} shape: `{ categories: { tasks: 'view', … }, items: { project: { '<uuid>': 'edit' } } }`. `null` = unrestricted (acts exactly as the owner). Validated by {@see validateAccessPolicy()}.
supported property: SupportedProperty
  property: ApiToken/lastUsedAt
  title: lastUsedAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ApiToken/expiresAt
  title: expiresAt
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: ApiToken/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a ApiToken resource.
  method: GET
  returns: ApiToken
  title: getApiToken
supported operation: Operation
  type: DeleteAction
  description: Deletes the ApiToken resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteApiToken
description: Personal access token bound to a single user, used to authenticate MCP (and any future programmatic) callers via `Authorization: Bearer`. Plaintext is shown to the human exactly once at creation (in the POST response under `plainToken`) — only the sha256 hash is persisted, so a database leak cannot be replayed against the API. Hashing matches the pattern already established by {@see PasswordResetToken} and {@see UserInvite}, just with a different prefix and longer entropy. A token acts AS its owner, optionally narrowed by `accessPolicy` — the same {@see AccessPolicy} shape used for admin-impersonation consent (per-category + per-item none/view/edit). `null` means "unrestricted": the token can do exactly what its owner can in the app. The policy is enforced uniformly across the REST API + MCP by the shared engine (App\Security\Access\ActorPolicyResolver + AccessPolicyListener / AccessPolicyItemScope on REST; the MCP controller on tool dispatch).

ApiToken/name
type: Property
domain: ApiToken
the hydra core vocabulary: name
range: string

ApiToken/accessPolicy
type: Property
domain: ApiToken
the hydra core vocabulary: accessPolicy

ApiToken/lastUsedAt
type: Property
domain: ApiToken
the hydra core vocabulary: lastUsedAt
range: dateTime

ApiToken/expiresAt
type: Property
domain: ApiToken
the hydra core vocabulary: expiresAt
range: dateTime

ApiToken/createdAt
type: Property
domain: ApiToken
the hydra core vocabulary: createdAt
range: dateTime

Comment
type: Class
title: Comment
supported property: SupportedProperty
  property: Comment/commentableType
  title: commentableType
  required: false
  readable: true
  writeable: false
  description: `'task'`, `'page'`, or `'discussion'` — discriminator for the polymorphic parent FK. Stamped server-side from whichever of `task` / `page` / `discussion` the client supplied; never accepted directly on the wire so clients can't desync it from the FK trio.
supported property: SupportedProperty
  property: Comment/task
  title: task
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Comment/page
  title: page
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Comment/discussion
  title: discussion
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Comment/author
  title: author
  required: false
  readable: true
  writeable: false
  description: Author is set server-side by {@see CommentAuthorProcessor} on POST; the setter exists so PATCH can no-op the field, but the serializer group keeps it read-only over the wire.
supported property: SupportedProperty
  property: Comment/body
  title: body
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Comment/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Comment/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
  description: Bumped automatically on PATCH via the lifecycle hook; lets the PWA render an "(edited)" affordance without diffing payloads.
supported operation: Operation
  type: FindAction
  description: Retrieves a Comment resource.
  method: GET
  returns: Comment
  title: getComment
supported operation: Operation
  description: Updates the Comment resource.
  expects: Comment
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Comment
  title: patchComment
supported operation: Operation
  type: DeleteAction
  description: Deletes the Comment resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteComment
description: Unified comment thread (#228). Replaces the per-parent `TaskComment` / `PageComment` split: every comment now lives in one `comment` table with a polymorphic parent — `commentable_type` discriminates between `'task'`, `'page'`, and `'discussion'`, and exactly one of `task` / `page` / `discussion` is set to match. All three are real FKs (rather than a single bare `commentable_id`) so we keep cascade-on-delete from the parent. Comments are flat (no reply tree) and ordered chronologically. The previous task-side reply tree was flattened in the unification migration; the chronological view is the canonical thread surface. Access is parent-aware: - read: anyone who can read the parent (task accessibility OR page / discussion space membership). - post: same set as read. A locked discussion additionally refuses *new* comments (existing ones stay editable/deletable). - edit: author only. - delete: author, OR an admin escalation suited to the parent — the task's owner can delete on tasks; the space-admin can delete on pages and discussions. `@mention` tokens in `body` are scanned by {@see App\Service\CommentMentionService} after persist and create one `mention` Notification per resolved recipient. The recipient set is derived from the parent (task: owner + project space; page / discussion: the parent's space).

Comment/commentableType
type: Property
domain: Comment
the hydra core vocabulary: commentableType
range: string

Comment/task
type: Link
domain: Comment
the hydra core vocabulary: task
max cardinality: 1
range: Task

Comment/page
type: Link
domain: Comment
the hydra core vocabulary: page
max cardinality: 1
range: Page

Comment/discussion
type: Link
domain: Comment
the hydra core vocabulary: discussion
max cardinality: 1
range: Discussion

Comment/author
type: Property
domain: Comment
the hydra core vocabulary: author
max cardinality: 1
range: User

Comment/body
type: Property
domain: Comment
the hydra core vocabulary: body
range: string

Comment/createdAt
type: Property
domain: Comment
the hydra core vocabulary: createdAt
range: dateTime

Comment/updatedAt
type: Property
domain: Comment
the hydra core vocabulary: updatedAt
range: dateTime

CustomFieldDefinition
type: Class
title: CustomFieldDefinition
supported property: SupportedProperty
  property: CustomFieldDefinition/project
  title: project
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: CustomFieldDefinition/space
  title: space
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: CustomFieldDefinition/name
  title: name
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: CustomFieldDefinition/kind
  title: kind
  required: false
  readable: true
  writeable: true
  description: Top-level family — one of {@see CustomFieldKind}'s values.
supported property: SupportedProperty
  property: CustomFieldDefinition/subtype
  title: subtype
  required: true
  readable: true
  writeable: true
  description: Specialisation within the kind. The (kind, subtype) pair is the registry lookup key for the concrete strategy.
supported property: SupportedProperty
  property: CustomFieldDefinition/config
  title: config
  required: false
  readable: true
  writeable: true
  description: Per-kind configuration payload. Shape is owned by the strategy for the (kind, subtype) pair — see CustomFieldTypeInterface docblock.
supported property: SupportedProperty
  property: CustomFieldDefinition/footer
  title: footer
  required: false
  readable: true
  writeable: true
  description: Optional footer aggregation descriptor `{kind, label?}`. Null means no footer row for this field; the kind, when present, must be one supported by the strategy (enforced in {@see ValidCustomFieldDefinition}).
supported property: SupportedProperty
  property: CustomFieldDefinition/nullable
  title: nullable
  required: false
  readable: true
  writeable: true
  description: Whether a missing or null value is acceptable for this field.
supported property: SupportedProperty
  property: CustomFieldDefinition/position
  title: position
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: CustomFieldDefinition/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a CustomFieldDefinition resource.
  method: GET
  returns: CustomFieldDefinition
  title: getCustomFieldDefinition
supported operation: Operation
  description: Updates the CustomFieldDefinition resource.
  expects: CustomFieldDefinition
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: CustomFieldDefinition
  title: patchCustomFieldDefinition
supported operation: Operation
  type: DeleteAction
  description: Deletes the CustomFieldDefinition resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteCustomFieldDefinition
description: Per-project custom field definition. Widened in #227 from the flat 5-type enum to a (kind, subtype, config) triple backed by a strategy registry — see {@see App\CustomField\Type\CustomFieldTypeInterface}. Storage shape: - `kind` — top-level family (boolean|text|numeric|date|select|reference) - `subtype` — specialisation within the kind (e.g. `money` under `numeric`) - `config` — per-kind config payload (options, min/max, currency, multi, …) - `footer` — optional `{kind, label?}` aggregation descriptor - `nullable` — whether a missing value is legal for this field Per-kind config + footer validation runs through {@see ValidCustomFieldDefinition} (class-level constraint), which dispatches into the strategy. Per-value validation lives on Task's {@see App\Validator\ValidCustomFieldValues}. Read access mirrors the parent project's space (any space member). Write access is space-admin only — the field schema is structural and easy to disrupt for the rest of the project.

CustomFieldDefinition/project
type: Link
domain: CustomFieldDefinition
the hydra core vocabulary: project
max cardinality: 1
range: Project

CustomFieldDefinition/space
type: Link
domain: CustomFieldDefinition
the hydra core vocabulary: space
max cardinality: 1
range: Space

CustomFieldDefinition/name
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: name
range: string

CustomFieldDefinition/kind
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: kind
range: string

CustomFieldDefinition/subtype
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: subtype
range: string

CustomFieldDefinition/config
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: config

CustomFieldDefinition/footer
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: footer
range: string

CustomFieldDefinition/nullable
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: nullable
range: boolean

CustomFieldDefinition/position
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: position
range: integer

CustomFieldDefinition/createdAt
type: Property
domain: CustomFieldDefinition
the hydra core vocabulary: createdAt
range: dateTime

Discussion
type: Class
title: Discussion
supported property: SupportedProperty
  property: Discussion/space
  title: space
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Discussion/author
  title: author
  required: false
  readable: true
  writeable: false
  description: Stamped by DiscussionAuthorProcessor on POST; read-only on the wire so a client can't post on someone else's behalf.
supported property: SupportedProperty
  property: Discussion/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Discussion/body
  title: body
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Discussion/category
  title: category
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Discussion/isPinned
  title: isPinned
  required: false
  readable: true
  writeable: true
  description: Symfony's PropertyInfoExtractor derives the property name from the getter — `isPinned()` would strip the `is` prefix and yield `pinned`, which doesn't match the writable name `isPinned` (from `setIsPinned`).
supported property: SupportedProperty
  property: Discussion/isLocked
  title: isLocked
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Discussion/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Discussion/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Discussion/commentCount
  title: commentCount
  required: false
  readable: true
  writeable: false
  description: Reply aggregates — computed at read time by {@see App\State\DiscussionAggregateProvider} and never persisted.
supported property: SupportedProperty
  property: Discussion/lastActivityAt
  title: lastActivityAt
  required: false
  readable: true
  writeable: false
  description: Falls back to the thread's creation time so a reply-less thread still reports a meaningful "last activity" instead of null.
supported property: SupportedProperty
  property: Discussion/participantCount
  title: participantCount
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Discussion/participants
  title: participants
  required: false
  readable: true
  writeable: false
  description: Distinct people who have touched the thread (author first, then repliers), capped for display. The full count lives in {@see $participantCount}.
supported operation: Operation
  type: FindAction
  description: Retrieves a Discussion resource.
  method: GET
  returns: Discussion
  title: getDiscussion
supported operation: Operation
  description: Updates the Discussion resource.
  expects: Discussion
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Discussion
  title: patchDiscussion
supported operation: Operation
  type: DeleteAction
  description: Deletes the Discussion resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteDiscussion
description: Space-level discussion thread. Read/list access mirrors the parent space — every space member can browse and post; edit is author-only; delete + pin/lock are author OR space admin. Originally project-scoped (#91) and reparented to the space (#185) so the natural unit of conversation matches the access boundary: spaces are what users join and switch between, not projects.

Discussion/space
type: Link
domain: Discussion
the hydra core vocabulary: space
max cardinality: 1
range: Space

Discussion/author
type: Property
domain: Discussion
the hydra core vocabulary: author
max cardinality: 1
range: User

Discussion/title
type: Property
domain: Discussion
the hydra core vocabulary: title
range: string

Discussion/body
type: Property
domain: Discussion
the hydra core vocabulary: body
range: string

Discussion/category
type: Property
domain: Discussion
the hydra core vocabulary: category
range: string

Discussion/isPinned
type: Property
domain: Discussion
the hydra core vocabulary: isPinned
range: boolean

Discussion/isLocked
type: Property
domain: Discussion
the hydra core vocabulary: isLocked
range: boolean

Discussion/createdAt
type: Property
domain: Discussion
the hydra core vocabulary: createdAt
range: dateTime

Discussion/updatedAt
type: Property
domain: Discussion
the hydra core vocabulary: updatedAt
range: dateTime

Discussion/commentCount
type: Property
domain: Discussion
the hydra core vocabulary: commentCount
range: integer

Discussion/lastActivityAt
type: Property
domain: Discussion
the hydra core vocabulary: lastActivityAt
range: dateTime

Discussion/participantCount
type: Property
domain: Discussion
the hydra core vocabulary: participantCount
range: integer

Discussion/participants
type: Property
domain: Discussion
the hydra core vocabulary: participants
range: User

MediaObject
type: Class
title: MediaObject
supported property: SupportedProperty
  property: MediaObject/kind
  title: kind
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: MediaObject/originalName
  title: originalName
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: MediaObject/mimeType
  title: mimeType
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: MediaObject/byteSize
  title: byteSize
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: MediaObject/createdOn
  title: createdOn
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: MediaObject/variantUrls
  title: variantUrls
  required: false
  readable: true
  writeable: false
  description: Variants serialized as public URLs, keyed by variant name.
supported property: SupportedProperty
  property: MediaObject/downloadUrl
  title: downloadUrl
  required: false
  readable: true
  writeable: false
  description: Authenticated download URL for attachment-kind media. Avatars stay on the public `/media/...` URL — they're displayed everywhere inside the app and don't carry sensitive content. Returning null for non- attachment kinds keeps the frontend selector trivial: prefer `downloadUrl` when present, fall back to `variantUrls.original`.
supported operation: Operation
  type: FindAction
  description: Retrieves a MediaObject resource.
  method: GET
  returns: MediaObject
  title: getMediaObject
description: Shared media container. Owned by the uploading user, referenced by domain entities (User.avatar today; tasks/projects/comments later). `variants` stores a map of variant name to Flysystem path — images get "thumb"/"profile" entries; plain attachments use "original".

MediaObject/kind
type: Property
domain: MediaObject
the hydra core vocabulary: kind
range: string

MediaObject/originalName
type: Property
domain: MediaObject
the hydra core vocabulary: originalName
range: string

MediaObject/mimeType
type: Property
domain: MediaObject
the hydra core vocabulary: mimeType
range: string

MediaObject/byteSize
type: Property
domain: MediaObject
the hydra core vocabulary: byteSize
range: integer

MediaObject/createdOn
type: Property
domain: MediaObject
the hydra core vocabulary: createdOn
range: dateTime

MediaObject/variantUrls
type: Property
domain: MediaObject
the hydra core vocabulary: variantUrls
range: string

MediaObject/downloadUrl
type: Property
domain: MediaObject
the hydra core vocabulary: downloadUrl
range: string

Notification
type: Class
title: Notification
supported property: SupportedProperty
  property: Notification/actor
  title: actor
  required: false
  readable: true
  writeable: false
  description: Who triggered the notification (commenter, assigner, …). Null for system-generated rows like reminders. Embedded under `notification:read` so the row renders an actor avatar without a follow-up fetch. SET NULL on actor deletion so the row survives.
supported property: SupportedProperty
  property: Notification/type
  title: type
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Notification/title
  title: title
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Notification/body
  title: body
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Notification/task
  title: task
  required: false
  readable: true
  writeable: false
  description: Optional link back to the task this notification refers to.
supported property: SupportedProperty
  property: Notification/comment
  title: comment
  required: false
  readable: true
  writeable: false
  description: Comment that triggered this notification — only set for `mention` rows. Together with `recipient` it forms a unique key so editing a comment can't re-fire mentions to the same user.
supported property: SupportedProperty
  property: Notification/readAt
  title: readAt
  required: false
  readable: true
  writeable: true
  description: Set by NotificationUpdateProcessor when the user marks the row read.
supported property: SupportedProperty
  property: Notification/archivedAt
  title: archivedAt
  required: false
  readable: true
  writeable: true
  description: Set when the user archives the row (single PATCH or the bulk archive endpoint). `null` means it's still in the active inbox; any value means archived ("View archived" surfaces these).
supported property: SupportedProperty
  property: Notification/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Notification/digestedAt
  title: digestedAt
  required: false
  readable: true
  writeable: false
  description: Stamped by App\Command\DispatchNotificationDigestCommand once a row has been included in a digest email so the next digest run skips it.
supported property: SupportedProperty
  property: Notification/targetUrl
  title: targetUrl
  required: false
  readable: true
  writeable: false
  description: Front-end deep-link for the row, resolved from whichever parent is set (comment unwraps to its own parent). Null when nothing is linkable. Computed at read time — never persisted.
supported property: SupportedProperty
  property: Notification/targetLabel
  title: targetLabel
  required: false
  readable: true
  writeable: false
  description: Title of the linked entity (task / discussion / page title).
supported property: SupportedProperty
  property: Notification/contextLabel
  title: contextLabel
  required: false
  readable: true
  writeable: false
  description: Breadcrumb context — the parent project or space name.
supported operation: Operation
  type: FindAction
  description: Retrieves a Notification resource.
  method: GET
  returns: Notification
  title: getNotification
supported operation: Operation
  description: Updates the Notification resource.
  expects: Notification
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Notification
  title: patchNotification
description: In-app notification for the recipient. Kinds: `task_reminder` (App\Command\DispatchTaskRemindersCommand), `mention` / `reply` / `comment` (comment activity, via App\Service\NotificationDispatcher), and `assigned` / `status` (task activity). Most carry an `actor` (the user who triggered it) and resolve a deep-link `target` from whichever of task / comment / discussion / page is set. Read access is per-user — App\Doctrine\NotificationRecipientExtension scopes listings to the current recipient. The only client-writable fields are `readAt` and `archivedAt` (mark-read / archive); everything else is server-controlled. Bulk mark-read / archive live on App\Controller\NotificationBulkController; the live bell subscribes to the per-user Mercure topic minted by NotificationsMercureTokenController.

Notification/actor
type: Property
domain: Notification
the hydra core vocabulary: actor
max cardinality: 1
range: User

Notification/type
type: Property
domain: Notification
the hydra core vocabulary: type
range: string

Notification/title
type: Property
domain: Notification
the hydra core vocabulary: title
range: string

Notification/body
type: Property
domain: Notification
the hydra core vocabulary: body
range: string

Notification/task
type: Link
domain: Notification
the hydra core vocabulary: task
max cardinality: 1
range: Task

Notification/comment
type: Link
domain: Notification
the hydra core vocabulary: comment
max cardinality: 1
range: Comment

Notification/readAt
type: Property
domain: Notification
the hydra core vocabulary: readAt
range: dateTime

Notification/archivedAt
type: Property
domain: Notification
the hydra core vocabulary: archivedAt
range: dateTime

Notification/createdAt
type: Property
domain: Notification
the hydra core vocabulary: createdAt
range: dateTime

Notification/digestedAt
type: Property
domain: Notification
the hydra core vocabulary: digestedAt
range: dateTime

Notification/targetUrl
type: Property
domain: Notification
the hydra core vocabulary: targetUrl
range: string

Notification/targetLabel
type: Property
domain: Notification
the hydra core vocabulary: targetLabel
range: string

Notification/contextLabel
type: Property
domain: Notification
the hydra core vocabulary: contextLabel
range: string

Page
type: Class
title: Page
supported property: SupportedProperty
  property: Page/space
  title: space
  required: true
  readable: true
  writeable: true
  description: Owning space — gates read/list via `PageAccessExtension` and drives the security expressions for write/delete. Required.
supported property: SupportedProperty
  property: Page/parent
  title: parent
  required: false
  readable: true
  writeable: true
  description: Optional parent page. Self-FK forms the tree; cascade-on-delete means deleting a parent removes all descendants. The PR doesn't enforce a max depth — the renderer collapses deep nests.
supported property: SupportedProperty
  property: Page/createdBy
  title: createdBy
  required: false
  readable: true
  writeable: false
  description: Stamped server-side by {@see PageAuthorProcessor} on create.
supported property: SupportedProperty
  property: Page/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Page/body
  title: body
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Page/position
  title: position
  required: false
  readable: true
  writeable: true
  description: Sort key within the parent (or top-level if parent is null). Two pages can share a position; ties break on `createdAt DESC`. The client supplies it on create/edit; no auto-renumber.
supported property: SupportedProperty
  property: Page/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Page/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a Page resource.
  method: GET
  returns: Page
  title: getPage
supported operation: Operation
  description: Updates the Page resource.
  expects: Page
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Page
  title: patchPage
supported operation: Operation
  type: DeleteAction
  description: Deletes the Page resource.
  method: DELETE
  returns: owl:Nothing
  title: deletePage
description: Long-form, hierarchical Notion-style document owned by a `Space` (#183). Title + markdown body + optional parent page form a tree; audit history flows through Gedmo `Loggable` like Project/Task. Access mirrors the parent space (#185 access matrix): - read/list/create: any space member - edit: page author OR space admin - delete: page author OR space admin (cascades to descendants via the self-FK `ON DELETE CASCADE`) Comments live in the unified {@see Comment} entity (#228) with `commentable_type='page'`. Each page exposes its comments via the `comments` inverse mapping below for cascade-delete and template convenience; clients fetch the thread by querying `GET /comments?page=/pages/<uuid>` ordered by `createdAt ASC`.

Page/space
type: Property
domain: Page
the hydra core vocabulary: space
max cardinality: 1
range: Space

Page/parent
type: Property
domain: Page
the hydra core vocabulary: parent
max cardinality: 1
range: Page

Page/createdBy
type: Property
domain: Page
the hydra core vocabulary: createdBy
max cardinality: 1
range: User

Page/title
type: Property
domain: Page
the hydra core vocabulary: title
range: string

Page/body
type: Property
domain: Page
the hydra core vocabulary: body
range: string

Page/position
type: Property
domain: Page
the hydra core vocabulary: position
range: integer

Page/createdAt
type: Property
domain: Page
the hydra core vocabulary: createdAt
range: dateTime

Page/updatedAt
type: Property
domain: Page
the hydra core vocabulary: updatedAt
range: dateTime

Project
type: Class
title: Project
supported property: SupportedProperty
  property: Project/owner
  title: owner
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Project/space
  title: space
  required: false
  readable: true
  writeable: true
  description: The space this project lives in (#181). Set by ProjectOwnerProcessor to the creator's personal space when the client doesn't specify one — keeps the existing PWA, which is unaware of spaces, working unchanged. Made non-null at the DB level once the migration's backfill populates every existing row.
supported property: SupportedProperty
  property: Project/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Project/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Project/createdOn
  title: createdOn
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Project/taskCount
  title: taskCount
  required: false
  readable: true
  writeable: false
  description: Total task count, surfaced on the wire for list/dashboard rows.
supported property: SupportedProperty
  property: Project/completedTaskCount
  title: completedTaskCount
  required: false
  readable: true
  writeable: false
  description: Count of completed tasks (those with a `completedOn` timestamp).
supported property: SupportedProperty
  property: Project/members
  title: members
  required: false
  readable: true
  writeable: false
  description: Backward-compatible serialization shim (#185 → PR 4): the existing PWA reads `project.members` to render member chips and to check "is the user a member?". The underlying property is gone (`project_member` was dropped) — this getter projects the parent space's effective members onto a plain User[] so the JSON-LD response continues to expose a `members` array.
supported operation: Operation
  type: FindAction
  description: Retrieves a Project resource.
  method: GET
  returns: Project
  title: getProject
supported operation: Operation
  description: Updates the Project resource.
  expects: Project
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Project
  title: patchProject
supported operation: Operation
  type: DeleteAction
  description: Deletes the Project resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteProject
description: Shared container for tasks. Lives inside a {@see Space} (#181), and access is determined by membership in that space — every space member can read, edit, and (subject to admin/author rules) delete the project and its tasks. The `owner` field records who created the project for display/audit — it does not grant extra privileges beyond what space membership already provides. Per #185: - read/list/create: any space member - edit: any space member (project metadata is shared state) - delete: project creator (owner) or space admin - Post requires the caller to be a member of the target space (enforced via securityPostDenormalize so the validator runs after the IRI denormalises into a Space entity).

Project/owner
type: Property
domain: Project
the hydra core vocabulary: owner
max cardinality: 1
range: User

Project/space
type: Link
domain: Project
the hydra core vocabulary: space
max cardinality: 1
range: Space

Project/title
type: Property
domain: Project
the hydra core vocabulary: title
range: string

Project/description
type: Property
domain: Project
the hydra core vocabulary: description
range: string

Project/createdOn
type: Property
domain: Project
the hydra core vocabulary: createdOn
range: dateTime

Project/taskCount
type: Property
domain: Project
the hydra core vocabulary: taskCount
range: integer

Project/completedTaskCount
type: Property
domain: Project
the hydra core vocabulary: completedTaskCount
range: integer

Project/members
type: Property
domain: Project
the hydra core vocabulary: members
range: User

PushSubscription
type: Class
title: PushSubscription
supported property: SupportedProperty
  property: PushSubscription/endpoint
  title: endpoint
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: PushSubscription/p256dh
  title: p256dh
  required: true
  readable: false
  writeable: true
  description: Public ECDH key from the browser's subscription.keys.p256dh.
supported property: SupportedProperty
  property: PushSubscription/auth
  title: auth
  required: true
  readable: false
  writeable: true
  description: Authentication secret from subscription.keys.auth. Same write-only exposure as `p256dh`.
supported property: SupportedProperty
  property: PushSubscription/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a PushSubscription resource.
  method: GET
  returns: owl:Nothing
  title: getPushSubscription
supported operation: Operation
  type: DeleteAction
  description: Deletes the PushSubscription resource.
  method: DELETE
  returns: owl:Nothing
  title: deletePushSubscription
description: Web Push subscription registered by the browser via PushManager. Multiple rows per user are expected (one per device + browser combination). The endpoint URL is unique across the whole table — the browser surfaces the same URL for re-subscribes, so a unique index lets the API treat re-registration as an idempotent upsert candidate (today we 409 the duplicate; future iteration could collapse to the existing row). Listing and deletion are scoped to the current user via a Doctrine query extension (see PushSubscriptionUserExtension). Subscription payloads (p256dh + auth keys) are write-only on the API surface so the secrets never echo back in a GET response.

PushSubscription/endpoint
type: Property
domain: PushSubscription
the hydra core vocabulary: endpoint
range: string

PushSubscription/p256dh
type: Property
domain: PushSubscription
the hydra core vocabulary: p256dh
range: string

PushSubscription/auth
type: Property
domain: PushSubscription
the hydra core vocabulary: auth
range: string

PushSubscription/createdAt
type: Property
domain: PushSubscription
the hydra core vocabulary: createdAt
range: dateTime

Segment
type: Class
title: Segment
supported property: SupportedProperty
  property: Segment/key
  title: key
  required: true
  readable: true
  writeable: false
  description: Stable, code-facing handle ("beta-new-editor"). Lowercase, no underscores — it maps 1:1 to the synthetic role `ROLE_SEGMENT_BETA_NEW_EDITOR` ({@see getRole()}), and the reverse mapping in {@see roleToKey()} relies on the no-underscore rule. Set once on create; read-only on the wire afterwards (like a username).
supported property: SupportedProperty
  property: Segment/name
  title: name
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Segment/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Segment/purpose
  title: purpose
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Segment/enabled
  title: enabled
  required: false
  readable: true
  writeable: true
  description: Master switch. A disabled segment matches nobody — the voter and evaluator short-circuit on it — so you can dark-launch or kill a rollout without deleting its config or membership rows.
supported property: SupportedProperty
  property: Segment/rolloutPercentage
  title: rolloutPercentage
  required: false
  readable: true
  writeable: true
  description: Percentage (0–100) of users included by a deterministic hash of `key:userId`. Null/0 means "no rollout bucket". Stable per (user, segment): a user who is in at 20% stays in when you raise it to 30%.
supported property: SupportedProperty
  property: Segment/includedRoles
  title: includedRoles
  required: false
  readable: true
  writeable: true
  description: Auto-include users whose runtime roles (User::getRoles, which appends the implicit ROLE_USER) intersect this list — e.g. `["ROLE_ADMIN"]` to expose a beta to every admin. Synthetic segment roles are rejected here (a segment can't be gated on another segment) by the entity validator below.
supported property: SupportedProperty
  property: Segment/createdBy
  title: createdBy
  required: false
  readable: true
  writeable: false
  description: Who created the segment. Nullable + SET NULL so removing a user never orphans the segment config.
supported property: SupportedProperty
  property: Segment/memberships
  title: memberships
  required: false
  readable: true
  writeable: false
  description: Manual include / exclude overrides. Read-only via the embedded collection; mutated through {@see \App\Controller\SegmentMemberController}.
supported property: SupportedProperty
  property: Segment/createdOn
  title: createdOn
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Segment/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Segment/role
  title: role
  required: false
  readable: true
  writeable: false
supported operation: Operation
  type: FindAction
  description: Retrieves a Segment resource.
  method: GET
  returns: Segment
  title: getSegment
supported operation: Operation
  description: Updates the Segment resource.
  expects: Segment
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Segment
  title: patchSegment
supported operation: Operation
  type: DeleteAction
  description: Deletes the Segment resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteSegment
description: A named cohort of users used for A/B testing, beta-feature gating, and gradual rollouts. Instance-level (admin-managed) rather than space-scoped: a segment is a property of the whole deployment, like roles or the waitlist toggle. Membership is *dynamic* — a user belongs to a segment when any rule matches, with explicit per-user overrides winning (see {@see \App\Service\SegmentEvaluator} for the precedence). The rules are: - manual include / exclude rows ({@see SegmentMembership}) - the user's roles intersecting {@see $includedRoles} - a deterministic percentage {@see $rolloutPercentage} bucket Each segment surfaces as a synthetic role `ROLE_SEGMENT_<KEY>` (see {@see getRole()}). Because membership is dynamic the role isn't stored on the user; {@see \App\Security\SegmentVoter} answers `is_granted()` live and the active role strings are appended to the `/api/me` roles array. So feature-gating anywhere is just `is_granted('ROLE_SEGMENT_BETA_EDITOR')` server-side or `user.roles.includes('ROLE_SEGMENT_BETA_EDITOR')` in the PWA.

Segment/key
type: Property
domain: Segment
the hydra core vocabulary: key
range: string

Segment/name
type: Property
domain: Segment
the hydra core vocabulary: name
range: string

Segment/description
type: Property
domain: Segment
the hydra core vocabulary: description
range: string

Segment/purpose
type: Property
domain: Segment
the hydra core vocabulary: purpose
range: string

Segment/enabled
type: Property
domain: Segment
the hydra core vocabulary: enabled
range: boolean

Segment/rolloutPercentage
type: Property
domain: Segment
the hydra core vocabulary: rolloutPercentage
range: integer

Segment/includedRoles
type: Property
domain: Segment
the hydra core vocabulary: includedRoles
range: string

Segment/createdBy
type: Property
domain: Segment
the hydra core vocabulary: createdBy
max cardinality: 1
range: User

Segment/memberships
type: Property
domain: Segment
the hydra core vocabulary: memberships

Segment/createdOn
type: Property
domain: Segment
the hydra core vocabulary: createdOn
range: dateTime

Segment/updatedAt
type: Property
domain: Segment
the hydra core vocabulary: updatedAt
range: dateTime

Segment/role
type: Property
domain: Segment
the hydra core vocabulary: role
range: string

Space
type: Class
title: Space
supported property: SupportedProperty
  property: Space/name
  title: name
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Space/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Space/isPersonal
  title: isPersonal
  required: false
  readable: true
  writeable: false
  description: True only for the auto-created personal space attached to each user at signup. Write-only at the system level — never settable via the API (no `space:write` group on the property), so client payloads can't fake a personal space, and the value is fixed for the lifetime of the row.
supported property: SupportedProperty
  property: Space/visibility
  title: visibility
  required: false
  readable: true
  writeable: true
  description: Controls whether the space accepts additional members. `private` spaces are creator-only — invite endpoints and add-member reject with 409, and {@see SpaceUpdateProcessor} kicks every other membership on a shared → private transition. `shared` is the default for new spaces; personal spaces are always `private`, enforced by {@see validatePersonalIsPrivate()}.
supported property: SupportedProperty
  property: Space/color
  title: color
  required: false
  readable: true
  writeable: true
  description: Override color for the space avatar tile. When null the UI falls back to the creator's `personalizedColor` — same swatch on the person and their personal space by default, with the option to pick a different one for shared spaces (or override the personal one). Constrained to the same WCAG-AA palette as user avatars so white initials always render legibly.
supported property: SupportedProperty
  property: Space/createdAt
  title: createdAt
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Space/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
  description: Maintained by Gedmo Timestampable on every UPDATE. Powers the "edited 2h ago" / "updated yesterday" line on the spaces grid — it tracks edits to the space row itself (name, description, visibility), not activity on its contained projects / pages.
supported property: SupportedProperty
  property: Space/createdBy
  title: createdBy
  required: false
  readable: true
  writeable: false
  description: Audit-only — the user who originally created the space. Kept nullable so user deletion doesn't take shared spaces with it (control comes from membership, not creation). For personal spaces, a partial unique index in the migration enforces that a user has at most one personal space.
supported property: SupportedProperty
  property: Space/userMemberships
  title: userMemberships
  required: false
  readable: true
  writeable: false
  description: Direct user memberships. Populated by SpaceCreateProcessor for new shared spaces and by UserPasswordHasherProcessor for the personal space at signup. PR 3 (#186) will add the invite-by-email flow that lets admins extend this collection.
supported property: SupportedProperty
  property: Space/groupMemberships
  title: groupMemberships
  required: false
  readable: true
  writeable: false
  description: Group memberships — adds every member of a `UserGroup` to the space transitively. PR 1 only models the relationship; the management UI for adding groups lands in PR 4 (#187).
supported property: SupportedProperty
  property: Space/attachments
  title: attachments
  required: false
  readable: true
  writeable: true
  description: MediaObjects attached at the space level (cover docs, shared specs, anything not pinned to a specific task). Replaces the old `project_attachment` join — attachments now live at the space so they're available to every project + member in it.
supported property: SupportedProperty
  property: Space/invites
  title: invites
  required: false
  readable: false
  writeable: true
  description: Transient list of email addresses to invite at creation time — consumed by {@see SpaceCreateProcessor}, never persisted. Validated inline so a single bad address fails the whole create with 422 (atomic batch invite — partial success would leave the UX in a state nobody asked for). All-personal duplicates and the creator's own address are dropped in the processor without erroring.
supported property: SupportedProperty
  property: Space/projectsCount
  title: projectsCount
  required: false
  readable: true
  writeable: false
  description: Project count rooted in this space. EXTRA_LAZY makes this a single COUNT query — no row hydration.
supported property: SupportedProperty
  property: Space/pagesCount
  title: pagesCount
  required: false
  readable: true
  writeable: false
  description: Page count rooted in this space. Includes every level of the page tree (root pages + descendants) since access is by space, not by parent — the grid copy reads "N pages" so all pages matter.
supported operation: Operation
  type: FindAction
  description: Retrieves a Space resource.
  method: GET
  returns: Space
  title: getSpace
supported operation: Operation
  description: Updates the Space resource.
  expects: Space
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Space
  title: patchSpace
supported operation: Operation
  type: DeleteAction
  description: Deletes the Space resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteSpace
description: Top-level container for content (#181). Every user has a personal "Private" space, created on signup and not deletable. Additional shared spaces can be created and shared with other users and `UserGroup`s. Membership (`SpaceMembership` + `SpaceGroupMembership`) is what gates access to the space's content; the `createdBy` field is audit-only. PR 1 ships only the Space entity, its memberships, and a backfill that links existing projects/discussions/custom-field-definitions to their creator's personal space. The access-extension rewrite that actually swaps Project/Discussion/CFD permissions over to space membership lands in PR 2 (#185).

Space/name
type: Property
domain: Space
the hydra core vocabulary: name
range: string

Space/description
type: Property
domain: Space
the hydra core vocabulary: description
range: string

Space/isPersonal
type: Property
domain: Space
the hydra core vocabulary: isPersonal
range: boolean

Space/visibility
type: Property
domain: Space
the hydra core vocabulary: visibility
range: string

Space/color
type: Property
domain: Space
the hydra core vocabulary: color
range: string

Space/createdAt
type: Property
domain: Space
the hydra core vocabulary: createdAt
range: dateTime

Space/updatedAt
type: Property
domain: Space
the hydra core vocabulary: updatedAt
range: dateTime

Space/createdBy
type: Property
domain: Space
the hydra core vocabulary: createdBy
max cardinality: 1
range: User

Space/userMemberships
type: Property
domain: Space
the hydra core vocabulary: userMemberships

Space/groupMemberships
type: Property
domain: Space
the hydra core vocabulary: groupMemberships

Space/attachments
type: Property
domain: Space
the hydra core vocabulary: attachments
range: MediaObject

Space/invites
type: Property
domain: Space
the hydra core vocabulary: invites
range: string

Space/projectsCount
type: Property
domain: Space
the hydra core vocabulary: projectsCount
range: integer

Space/pagesCount
type: Property
domain: Space
the hydra core vocabulary: pagesCount
range: integer

Tag
type: Class
title: Tag
supported property: SupportedProperty
  property: Tag/owner
  title: owner
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Tag/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Tag/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Tag/color
  title: color
  required: true
  readable: true
  writeable: true
  description: Hex color for badge rendering. Stored as lowercase `#RRGGBB` so the PWA can drop it straight into an inline style.
supported operation: Operation
  type: FindAction
  description: Retrieves a Tag resource.
  method: GET
  returns: Tag
  title: getTag
supported operation: Operation
  description: Updates the Tag resource.
  expects: Tag
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Tag
  title: patchTag
supported operation: Operation
  type: DeleteAction
  description: Deletes the Tag resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteTag

Tag/owner
type: Link
domain: Tag
the hydra core vocabulary: owner
max cardinality: 1
range: User

Tag/title
type: Property
domain: Tag
the hydra core vocabulary: title
range: string

Tag/description
type: Property
domain: Tag
the hydra core vocabulary: description
range: string

Tag/color
type: Property
domain: Tag
the hydra core vocabulary: color
range: string

Task
type: Class
title: Task
supported property: SupportedProperty
  property: Task/owner
  title: owner
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Task/project
  title: project
  required: false
  readable: true
  writeable: true
  description: Optional project the task belongs to. When set, every project member can read and edit the task alongside its owner. Personal tasks leave this null.
supported property: SupportedProperty
  property: Task/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Task/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Task/createdOn
  title: createdOn
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Task/completedOn
  title: completedOn
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Task/dueDate
  title: dueDate
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Task/recurrenceRule
  title: recurrenceRule
  required: false
  readable: true
  writeable: true
  description: Optional recurrence rule. Persisted as a JSON object. The base shape is `{"frequency": "daily"|"weekly"|"monthly"|"yearly", "interval": int}`, optionally extended with `byDay` (weekday tokens), `monthlyMode`, `bySetPos`, and an `ends` end-condition — see {@see \App\Service\RecurrenceCalculator} for the full shape. Validated by {@see ValidRecurrence} (and the cross-field rule that recurrence requires a due date). When set, completing the task triggers {@see TaskUpdateProcessor} to clone the next occurrence.
supported property: SupportedProperty
  property: Task/reminders
  title: reminders
  required: false
  readable: true
  writeable: true
  description: Reminders to fire around the due date. Each entry is an object — either relative (`{type:"relative", value:int, unit:"minutes"|"hours"|"days", repeat:bool}`, fired `value` units before the due date) or absolute (`{type:"absolute", at:ISO-8601, repeat:bool}`, fired at a fixed time).
supported property: SupportedProperty
  property: Task/position
  title: position
  required: false
  readable: true
  writeable: false
  description: Per-owner sort key. Lower positions render first. Set server-side: assigned by the persist processor on create, rewritten in bulk by the reorder endpoint. Negative values are allowed so new tasks can be inserted at the top without having to shift existing rows.
supported property: SupportedProperty
  property: Task/tags
  title: tags
  required: false
  readable: true
  writeable: true
  description: Owning side of the Task↔Tag many-to-many. Membership is edited via PATCH /tasks/{id} with a `tags` array of Tag IRIs. Tags are scoped to the task's owner; cross-user IRIs are rejected by TagOwnerExtension during deserialization.
supported property: SupportedProperty
  property: Task/assignees
  title: assignees
  required: false
  readable: true
  writeable: true
  description: Users assigned to this task. Always a subset of {owner ∪ project members} — enforced by ValidAssignees on persist. Assignment is purely a "who's responsible" label; it grants no extra read/edit privileges (those still follow owner + project membership).
supported property: SupportedProperty
  property: Task/attachments
  title: attachments
  required: false
  readable: true
  writeable: true
  description: MediaObjects attached to this task. Membership is edited via PATCH with an `attachments` array of MediaObject IRIs; the PWA uploads via `POST /media-objects` (kind=attachment) first to obtain the IRI.
supported property: SupportedProperty
  property: Task/customFieldValues
  title: customFieldValues
  required: false
  readable: true
  writeable: true
  description: Per-task values for the project's {@see CustomFieldDefinition}s (#84). Mutated via the Task write group: clients PATCH a fresh array of `{definition, value}` pairs and orphanRemoval reaps any row whose definition isn't in the new payload. Type/required/scope rules are enforced by {@see ValidCustomFieldValues}.
supported property: SupportedProperty
  property: Task/searchMatches
  title: searchMatches
  required: false
  readable: true
  writeable: false
  description: Search match provenance for the hidden sources (comment / custom field), computed at read time by {@see App\State\TaskSearchProvenanceProvider} only when `?search=` is present. Empty otherwise. Never persisted.
supported operation: Operation
  type: FindAction
  description: Retrieves a Task resource.
  method: GET
  returns: Task
  title: getTask
supported operation: Operation
  description: Updates the Task resource.
  expects: Task
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Task
  title: patchTask
supported operation: Operation
  type: DeleteAction
  description: Deletes the Task resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteTask

Task/owner
type: Property
domain: Task
the hydra core vocabulary: owner
max cardinality: 1
range: User

Task/project
type: Property
domain: Task
the hydra core vocabulary: project
max cardinality: 1
range: Project

Task/title
type: Property
domain: Task
the hydra core vocabulary: title
range: string

Task/description
type: Property
domain: Task
the hydra core vocabulary: description
range: string

Task/createdOn
type: Property
domain: Task
the hydra core vocabulary: createdOn
range: dateTime

Task/completedOn
type: Property
domain: Task
the hydra core vocabulary: completedOn
range: dateTime

Task/dueDate
type: Property
domain: Task
the hydra core vocabulary: dueDate
range: dateTime

Task/recurrenceRule
type: Property
domain: Task
the hydra core vocabulary: recurrenceRule

Task/reminders
type: Property
domain: Task
the hydra core vocabulary: reminders

Task/position
type: Property
domain: Task
the hydra core vocabulary: position
range: integer

Task/tags
type: Property
domain: Task
the hydra core vocabulary: tags
range: Tag

Task/assignees
type: Property
domain: Task
the hydra core vocabulary: assignees
range: User

Task/attachments
type: Property
domain: Task
the hydra core vocabulary: attachments
range: MediaObject

Task/customFieldValues
type: Property
domain: Task
the hydra core vocabulary: customFieldValues

Task/searchMatches
type: Property
domain: Task
the hydra core vocabulary: searchMatches

User
type: Class
title: User
supported property: SupportedProperty
  property: User/email
  title: email
  required: true
  readable: true
  writeable: false
supported property: SupportedProperty
  property: User/roles
  title: roles
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: User/givenName
  title: givenName
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: User/familyName
  title: familyName
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: User/nickname
  title: nickname
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: User/personalizedColor
  title: personalizedColor
  required: false
  readable: true
  writeable: true
  description: Hex color (e.g., "#1e6091") used behind the initials fallback when the user has no avatar. Initialized to a random palette entry at registration; the user can swap it for any other palette entry on /account, but never to a free-form value (to keep contrast safe).
supported property: SupportedProperty
  property: User/avatar
  title: avatar
  required: false
  readable: false
  writeable: true
supported property: SupportedProperty
  property: User/avatarUrls
  title: avatarUrls
  required: false
  readable: true
  writeable: false
  description: Avatar variant URLs keyed by size ("thumb", "profile"). Null when the user has no avatar — the frontend falls back to initials on personalizedColor.
supported operation: Operation
  type: FindAction
  description: Retrieves a User resource.
  method: GET
  returns: User
  title: getUser
supported operation: Operation
  description: Updates the User resource.
  expects: User
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: User
  title: patchUser

User/email
type: Property
domain: User
the hydra core vocabulary: email
range: string

User/roles
type: Property
domain: User
the hydra core vocabulary: roles
range: string

User/givenName
type: Property
domain: User
the hydra core vocabulary: givenName
range: string

User/familyName
type: Property
domain: User
the hydra core vocabulary: familyName
range: string

User/nickname
type: Property
domain: User
the hydra core vocabulary: nickname
range: string

User/personalizedColor
type: Property
domain: User
the hydra core vocabulary: personalizedColor
range: string

User/avatar
type: Link
domain: User
the hydra core vocabulary: avatar
max cardinality: 1
range: MediaObject

User/avatarUrls
type: Property
domain: User
the hydra core vocabulary: avatarUrls
range: string

Group
type: Class
title: Group
supported property: SupportedProperty
  property: Group/owner
  title: owner
  required: false
  readable: true
  writeable: false
  description: The user who currently controls the group. Set by UserGroupOwnerProcessor on create; reassigned via the dedicated, step-up-protected transfer endpoint (not via PATCH).
supported property: SupportedProperty
  property: Group/title
  title: title
  required: true
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Group/slug
  title: slug
  required: false
  readable: true
  writeable: false
  description: Stable, URL-friendly handle ("creative", "eng", …) rendered in the UI with a `g-` prefix ("g-creative"). Generated once from the title on create by {@see UserGroupOwnerProcessor} and intentionally NOT regenerated on rename — like a username, it's a stable identifier people may reference. Read-only on the wire (no `group:write`).
supported property: SupportedProperty
  property: Group/description
  title: description
  required: false
  readable: true
  writeable: true
supported property: SupportedProperty
  property: Group/color
  title: color
  required: false
  readable: true
  writeable: true
  description: Override color for the group avatar tile. When null the UI falls back to the owner's `personalizedColor`. Constrained to the same WCAG-AA palette as user avatars so white initials stay legible.
supported property: SupportedProperty
  property: Group/createdOn
  title: createdOn
  required: false
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Group/updatedAt
  title: updatedAt
  required: false
  readable: true
  writeable: false
  description: Maintained by Gedmo Timestampable on every UPDATE — powers the "edited 2h ago" / "updated yesterday" line on the groups list and the "updated" row in the detail's At-a-glance card. Stored as `datetime` (not immutable) so Gedmo can mutate it in place.
supported property: SupportedProperty
  property: Group/memberships
  title: memberships
  required: false
  readable: true
  writeable: false
  description: The group's membership roster. The owner is added here automatically by UserGroupOwnerProcessor so access checks can rely solely on memberships without a special case for the owner.
supported property: SupportedProperty
  property: Group/attachedSpaces
  title: attachedSpaces
  required: false
  readable: true
  writeable: false
  description: Flattened summary of the spaces this group is attached to, for the detail page's "Attached to" card. Returned as plain arrays so the minimal space fields the UI needs don't pull the full Space serialization graph into the `group:read` context.
supported operation: Operation
  type: FindAction
  description: Retrieves a Group resource.
  method: GET
  returns: Group
  title: getGroup
supported operation: Operation
  description: Updates the Group resource.
  expects: Group
  expects header:
    header name: Content-Type
    possible value: application/merge-patch+json
  method: PATCH
  returns: Group
  title: patchGroup
supported operation: Operation
  type: DeleteAction
  description: Deletes the Group resource.
  method: DELETE
  returns: owl:Nothing
  title: deleteGroup
description: Reusable named set of users. The owner has full control: edit metadata, manage membership, transfer ownership, or delete the group. Other members are read-only — they appear in the group's listings but cannot mutate it. Named "UserGroup" rather than "Group" because (a) `group` is a reserved SQL keyword and (b) `Group` clashes with `Symfony\Component\Serializer\ Attribute\Groups`. The PWA still labels it "Group" in the UI. Membership is modelled by {@see UserGroupMembership} (one row per user, carrying a `joinedAt`) rather than a plain join table so the detail UI can show when each member joined. Transfer ownership, member removal, and leaving the group all run through dedicated controllers — there is no `group:write` on `owner` or `memberships`, so a stolen cookie can't silently reassign the group via a plain PATCH.

Group/owner
type: Property
domain: Group
the hydra core vocabulary: owner
max cardinality: 1
range: User

Group/title
type: Property
domain: Group
the hydra core vocabulary: title
range: string

Group/slug
type: Property
domain: Group
the hydra core vocabulary: slug
range: string

Group/description
type: Property
domain: Group
the hydra core vocabulary: description
range: string

Group/color
type: Property
domain: Group
the hydra core vocabulary: color
range: string

Group/createdOn
type: Property
domain: Group
the hydra core vocabulary: createdOn
range: dateTime

Group/updatedAt
type: Property
domain: Group
the hydra core vocabulary: updatedAt
range: dateTime

Group/memberships
type: Property
domain: Group
the hydra core vocabulary: memberships

Group/attachedSpaces
type: Property
domain: Group
the hydra core vocabulary: attachedSpaces

Entrypoint
type: Class
title: Entrypoint
supported property: SupportedProperty
  property: Entrypoint/apiToken
  title: getApiTokenCollection
  description: The collection of ApiToken resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/comment
  title: getCommentCollection
  description: The collection of Comment resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/customFieldDefinition
  title: getCustomFieldDefinitionCollection
  description: The collection of CustomFieldDefinition resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/discussion
  title: getDiscussionCollection
  description: The collection of Discussion resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/mediaObject
  title: getMediaObjectCollection
  description: The collection of MediaObject resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/notification
  title: getNotificationCollection
  description: The collection of Notification resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/page
  title: getPageCollection
  description: The collection of Page resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/project
  title: getProjectCollection
  description: The collection of Project resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/pushSubscription
  title: getPushSubscriptionCollection
  description: The collection of PushSubscription resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/segment
  title: getSegmentCollection
  description: The collection of Segment resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/space
  title: getSpaceCollection
  description: The collection of Space resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/tag
  title: getTagCollection
  description: The collection of Tag resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/task
  title: getTaskCollection
  description: The collection of Task resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/user
  title: getUserCollection
  description: The collection of User resources
  readable: true
  writeable: false
supported property: SupportedProperty
  property: Entrypoint/group
  title: getGroupCollection
  description: The collection of Group resources
  readable: true
  writeable: false
supported operation: Operation
  method: GET
  title: index
  description: The API Entrypoint.
  returns: Entrypoint

Entrypoint/apiToken
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: ApiToken
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of ApiToken resources.
  method: GET
  returns: Collection
  title: getApiTokenCollection
supported operation: Operation
  type: CreateAction
  description: Creates a ApiToken resource.
  expects: ApiToken
  method: POST
  returns: ApiToken
  title: postApiToken

Entrypoint/comment
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Comment
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Comment resources.
  method: GET
  returns: Collection
  title: getCommentCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Comment resource.
  expects: Comment
  method: POST
  returns: Comment
  title: postComment

Entrypoint/customFieldDefinition
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: CustomFieldDefinition
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of CustomFieldDefinition resources.
  method: GET
  returns: Collection
  title: getCustomFieldDefinitionCollection
supported operation: Operation
  type: CreateAction
  description: Creates a CustomFieldDefinition resource.
  expects: CustomFieldDefinition
  method: POST
  returns: CustomFieldDefinition
  title: postCustomFieldDefinition

Entrypoint/discussion
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Discussion
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Discussion resources.
  method: GET
  returns: Collection
  title: getDiscussionCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Discussion resource.
  expects: Discussion
  method: POST
  returns: Discussion
  title: postDiscussion

Entrypoint/mediaObject
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: MediaObject
supported operation: Operation
  type: CreateAction
  description: Creates a MediaObject resource.
  expects: MediaObject
  method: POST
  returns: MediaObject
  title: postMediaObject

Entrypoint/notification
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Notification
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Notification resources.
  method: GET
  returns: Collection
  title: getNotificationCollection

Entrypoint/page
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Page
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Page resources.
  method: GET
  returns: Collection
  title: getPageCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Page resource.
  expects: Page
  method: POST
  returns: Page
  title: postPage

Entrypoint/project
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Project
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Project resources.
  method: GET
  returns: Collection
  title: getProjectCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Project resource.
  expects: Project
  method: POST
  returns: Project
  title: postProject

Entrypoint/pushSubscription
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: PushSubscription
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of PushSubscription resources.
  method: GET
  returns: Collection
  title: getPushSubscriptionCollection
supported operation: Operation
  type: CreateAction
  description: Creates a PushSubscription resource.
  expects: PushSubscription
  method: POST
  returns: PushSubscription
  title: postPushSubscription

Entrypoint/segment
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Segment
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Segment resources.
  method: GET
  returns: Collection
  title: getSegmentCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Segment resource.
  expects: Segment
  method: POST
  returns: Segment
  title: postSegment

Entrypoint/space
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Space
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Space resources.
  method: GET
  returns: Collection
  title: getSpaceCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Space resource.
  expects: Space
  method: POST
  returns: Space
  title: postSpace

Entrypoint/tag
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Tag
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Tag resources.
  method: GET
  returns: Collection
  title: getTagCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Tag resource.
  expects: Tag
  method: POST
  returns: Tag
  title: postTag

Entrypoint/task
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Task
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Task resources.
  method: GET
  returns: Collection
  title: getTaskCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Task resource.
  expects: Task
  method: POST
  returns: Task
  title: postTask

Entrypoint/user
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: User
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of User resources.
  method: GET
  returns: Collection
  title: getUserCollection
supported operation: Operation
  type: CreateAction
  description: Creates a User resource.
  expects: User
  method: POST
  returns: User
  title: postUser

Entrypoint/group
type: Link
domain: Entrypoint
max cardinality: 1
range: Collection
range:
  equivalent class:
    on property: member
    all values from: Group
supported operation: Operation
  type: FindAction
  description: Retrieves the collection of Group resources.
  method: GET
  returns: Collection
  title: getGroupCollection
supported operation: Operation
  type: CreateAction
  description: Creates a Group resource.
  expects: Group
  method: POST
  returns: Group
  title: postGroup

ConstraintViolationList
type: Class
title: ConstraintViolationList
description: A constraint violation List.
supported property: SupportedProperty
  property: ConstraintViolationList/propertyPath
  title: propertyPath
  description: The property path of the violation
  readable: true
  writeable: false
supported property: SupportedProperty
  property: ConstraintViolationList/message
  title: message
  description: The message associated with the violation
  readable: true
  writeable: false

ConstraintViolationList/propertyPath
type: Property
label: propertyPath
domain: ConstraintViolationList
range: string

ConstraintViolationList/message
type: Property
label: message
domain: ConstraintViolationList
range: string