Skip to main content
Source of truth: exhaustive for what it covers. Generated from Velt data-model metadata. If a name isn’t on this page, it doesn’t exist: don’t use it (a missing identifier resolves to undefined). Never invent or guess a name; use only identifiers that appear verbatim here or that you verified against ground truth and added here. Scope: exhaustive for the comments + notifications + core data layer (every documented field, the hook/event that surfaces it, and the API that mutates it). Other-feature entities (recorder, presence, cursor, CRDT, suggestions) are summarized, and out-of-scope internals (deep anchor/xpath structures, transcription, full document-user shape) are documented as absences rather than fully enumerated.
The data layer behind every Velt feature: what Velt stores, the field names and types on each entity, the hook/event that surfaces each field at runtime, and the API that mutates it. Use this to decide whether a requirement is feasible with primitives/headless: a UI need is only achievable if the field it depends on actually exists, is exposed by a hook/event, and (if it must change) is writable via an API. Where a field or capability does not exist, this file says so plainly; see ## Absences & limits. How to read each table: Field is the property name as it appears on the object you receive. Type is its shape. Meaning is what it holds. Exposed by is the read hook (or event payload) that carries it live. Writable via is the API that changes it: read-only means there is no client API to set it (server-derived or auto-generated). Cross-references: Hooks for hook signatures, Events for event names/payloads, APIs for imperative element methods, Component config for custom-data prop shapes (customStatus/customPriority/customCategory/customReactions). The two foundational entities are CommentAnnotation (a thread: one pin, with a list of comments) and Comment (a single reply inside that thread). Most other entities hang off these.

CommentAnnotation (thread)

One comment pin / thread. The root object returned by useCommentAnnotations(). Its comments array holds the individual replies.
FieldTypeMeaningExposed byWritable via
annotationIdstringUnique thread id.useCommentAnnotations, useCommentAnnotationByIdread-only (auto-generated)
annotationNumbernumberHuman-facing thread number (shown on the pin).sameread-only (auto-generated)
annotationIndexnumber1-based position in the current annotation list.sameread-only (computed)
commentsComment[]The replies in this thread (see Comment).sameuseAddComment / useUpdateComment / useDeleteComment
fromUserThe thread’s creator (see User).sameread-only (set at create)
statusCustomStatusWorkflow status object ({id,color,name,type}). Defaults to the built-in OPEN status.sameuseUpdateStatus (updateStatus({annotationId,status}))
statusUpdatedByUserIdstringId of the user who last changed status.sameread-only
priorityCustomPriorityPriority object ({id,color,name}). Absent until set.sameuseUpdatePriority
commentCategoriesCustomCategory[]Categories assigned to the thread.sameset via customCategory config + auto/assign flows
assignedToUserThe assignee, if assigned.sameuseAssignUser (assignUser({annotationId,assignedTo}))
approvedbooleanWhether the thread was approved (approval flow).sameuseApproveCommentAnnotation / useRejectCommentAnnotation
iam.accessModeCommentAccessModePublic vs private access mode.sameuseUpdateAccess (updateAccess({annotationId,accessMode}))
visibilityConfig{type,organizationId?,userIds?}Visibility scope (public / organizationPrivate / restricted).sameuseUpdateAccess + visibility on add requests; private mode APIs
resolvedByUserId / resolvedByUserstring / UserWho resolved the thread.sameset by useResolveCommentAnnotation
contextanyCustom app data attached to the thread (see storage patterns).samecontext provider / addContext on the add event / context prop
contextIdstringHash id derived from context.sameread-only (derived)
customListCustomAnnotationDropdownItem[]Selected items from a custom annotation dropdown.samecustomListDataOnAnnotation prop + dropdown UI
location / locationIdLocation / numberSub-location this thread belongs to (see Location).sameset at create via location/locationId props
targetElement / targetElementIdTargetElement / stringDOM anchor the pin attaches to.sameset at create
targetTextRangeTargetTextRangeText selection range for inline/text comments.sameset at create (text comments)
position / positionX / positionYCursorPosition / numberPin coordinates.sameread-only (set at placement; drag updates)
screenWidth / screenHeight / screenScrollHeight / screenScrollTopnumberAuthor’s screen/scroll dimensions at capture time.sameread-only (auto-generated)
pageInfoPageInfoPage metadata (path/url/title) at creation.sameset via useSetPageInfo
createdAt / lastUpdatedtimestampThread create / last-update times.sameread-only (auto-generated)
unreadbooleanWhether the current user has unread content here.same; counts via useUnreadCommentCountByAnnotationIdread (mark-as-read UI / actions)
views / viewedByUserIds / viewedByCommentAnnotationViews / string[] / User[]Read-receipt data (who has seen it). See Views.sameread-only (server-derived)
subscribedUsers / unsubscribedUsers / subscribedGroupsmapsNotification subscription state, keyed by user/group.sameuseSubscribeCommentAnnotation / useUnsubscribeCommentAnnotation
involvedUserIds / mentionedUserIdsstring[]Server-derived sets of participants / @mentioned users.sameread-only (server-derived)
ghostCommentGhostCommentPresent when the anchor element is gone (orphaned pin).sameread-only
isDraftbooleanThread is an unsent draft.sameread-only
multiThreadAnnotationIdstringOwning multi-thread pin id, when in multi-thread mode (see MultiThreadCommentAnnotation).sameset at create
colorstringPin/thread color used to render the marker.sameread-only (SDK-assigned)
metadataCommentMetadataThread-level metadata bag (extends BaseMetadata; [key]: any). Distinct from context: a separate per-thread bag, not the custom-context store.sameset at create
areaAnnotationIdstringLinked area-annotation id (area comments).sameread-only
isPageAnnotationbooleanThread is a page-level comment (not anchored to a DOM element).sameread-only
targetInlineCommentElementIdstringTarget element id for an inline-comment section.sameset at create
inlineCommentSectionConfig{ id: string, name?: string }The inline-comment section this thread belongs to.sameset at create
selectAllContentbooleanText comment spans all text content of the targetElementId.sameset at create (text comments)
sourceIdstringCaller-supplied source id stamped on the thread (e.g. via addManualComment).sameset at create
commentType / typestringComment kind / annotation type ('comment' default).sameset at create
sourceTypestring'agent' marks an AI-authored thread (blue pin + agent header).sameread-only (stamped server-side)
agentAgentDataAI agent identity + output (see AgentData). Present on agent threads.sameread-only (written upstream)
suggestionSuggestionDataSuggestion-mode data; present iff type === 'suggestion'.sameSuggestion element API (not a direct setter)
resolved / inProgress / documentParams / documentParamsIdNoneDeprecated. Use status and location/locationId instead.None

MultiThreadCommentAnnotation

The grouping pin that holds multiple comment threads at one anchor, used when multi-thread mode is on (enableMultiThreadMode()). Each child CommentAnnotation points back to it via CommentAnnotation.multiThreadAnnotationId. It is not a thread itself: it is a container whose commentAnnotationsMap holds the child threads.
FieldTypeMeaning
annotationIdstringUnique multi-thread pin id (the value child threads carry in multiThreadAnnotationId).
commentAnnotationsMap{ [commentAnnotationId]: CommentAnnotation }The child threads grouped under this pin, keyed by their id.
commentAnnotations{ [commentAnnotationId]: boolean }Membership map of child thread ids (presence flags).
fromUserCreator of the multi-thread pin.
location / locationIdLocation / numberSub-document scope.
positionCursorPositionPin coordinates.
targetElement / targetElementIdTargetElement / stringDOM anchor the pin attaches to.
targetTextRangeTargetTextRangeText selection range (inline/text multi-thread).
targetInlineCommentElementId / inlineCommentSectionConfigstring / { id, name? }Inline-comment section anchoring.
areaAnnotationIdstringLinked area-annotation id (area multi-thread).
typestringAnnotation kind.
isDraftbooleanPin is an unsent draft.
sourceIdstringCaller-supplied source id.
metadataMultiThreadMetadataMetadata bag (extends BaseMetadata).
ghostComment / contextGhostComment / anyOrphaned-pin marker / custom context bag.
All fields are read-only from the SDK (set at placement / managed); children are mutated through the normal comment APIs on each CommentAnnotation.

Comment (reply)

A single message inside a thread’s comments array. Not fetched on its own as a reactive stream: read it from the parent CommentAnnotation.comments, or imperatively via useGetComment() (await getComment({annotationId})Comment[]).
FieldTypeMeaningExposed byWritable via
commentIdnumberUnique id within the thread.CommentAnnotation.comments[], useGetCommentread-only (auto-generated)
commentTextstringPlain-text body.sameuseUpdateComment
commentHtmlstringRich-text (HTML) body.sameuseUpdateComment
fromUserThe reply’s author.sameread-only (set at create)
toUser[]Users @mentioned in this reply.sameset at create (mentions)
taggedUserContactsAutocompleteUserContactReplaceData[]Resolved mention contacts.sameset at create
status'added' | 'updated'Lifecycle marker of the reply (newly added vs subsequently updated).sameread-only (set by add/update)
customListAutocompleteReplaceData[]Selected items from a custom autocomplete list on the reply.samecreateCustomListDataOnComment + composer UI
toOrganizationUserGroupAutocompleteGroupReplaceData[]Group @mentions (org/team mentions) on the reply.sameset at create (group mentions)
type'text' | 'voice'Content kind.sameset at create
commentVoiceUrlstringVoice-note URL (voice comments).sameset at create
attachmentsAttachment[]Files on this reply (see Attachment).sameuseAddAttachment / useDeleteAttachment
recordersRecordedData[]Recordings on this reply (see RecordedData).sameuseGetRecording / useDeleteRecording
reactionAnnotations / reactionAnnotationIdsReactionAnnotation[] / string[]Reactions on this reply (see ReactionAnnotation).sameuseAddReaction / useToggleReaction / useDeleteReaction
createdAttimestampWhen the reply was posted.sameread-only (auto-generated)
lastUpdatedDateWhen the reply was last touched.sameread-only (auto-generated)
isEditedbooleanWhether the reply was edited after posting.sameread-only (set by edit)
editedAttimestampWhen it was last edited.sameread-only (set by edit)
isDraftbooleanReply is an unsent draft.sameread-only
metadataanyCustomer-supplied per-reply data, persisted as-is (see storage patterns). Only present when a caller provides it.sameset on the Comment passed to useAddComment/useUpdateComment
contextanyPer-reply context bag.sameset on the Comment at add time
replaceContentHtml / replaceContentTextstringReplacement content for accepted suggestions.sameset at create (suggestion flows)
sourceTypestring'agent' for agent-authored replies.sameread-only (server-side)
agentAgentDataAgent identity for an agent reply.sameread-only (written upstream)
No per-edit author identity. Comment records isEdited/editedAt but not who edited it: see Absences.

User / UserContact

User is the identified user (yourself, or any from/assignedTo/to actor on a thread). UserContact is a lighter shape used for mention/assignee lists. User fields: userId (your id), name, email, photoUrl, color, textColor (avatar colors), initial, plan, groupId, organizationId, type, isAdmin, isReadOnly, isAnonymous, isGuest, userSnippylyId (internal id used as the key in views), contacts (UserContact[]). UserContact fields: userId, name, email (required), photoUrl, groupId, visibility ('group' \| 'private'), color, textColor, initial.
ReadExposed byWritable via
Current identified useruseCurrentUser()User | nulluseIdentify() (identify(user))
Current user’s permissionsuseCurrentUserPermissions()permission provider
Mention/contact listuseContactList(), useContactUtils()customAutocompleteSearch + ContactElement
Selected contact (mention/assign)useContactSelected()SelectedUserContactNone
SelectedUserContact wraps a UserContact with isOrganizationContact, isDocumentContact, documentAccessType. The assignUser API takes a UserContact as assignedTo. All user identity originates from identify(); the SDK adds color/textColor/userSnippylyId/initial.

Reaction / ReactionAnnotation

A ReactionAnnotation is the container pinned to a comment (or element); it holds a reactions array of individual Reactions. ReactionAnnotation fields: annotationId, commentAnnotationId (the comment it’s attached to), reactions (Reaction[]), from (User), targetElement/targetElementId, position (CursorPosition), location/locationId, type (string), annotationIndex (number, 1-based), pageInfo (PageInfo), icon/iconUrl/iconEmoji, lastUpdated, metadata, involvedUserIds (server-derived), and context (typed Context: { access: {[key]: string|number}, accessFields?: string[] }: not a free-form any bag, unlike CommentAnnotation.context). Reaction fields: variant (emoji variant), from (User), lastUpdated.
ReadExposed byWritable via
Reactions on a commentCommentAnnotation.comments[].reactionAnnotationsuseAddReaction / useDeleteReaction / useToggleReaction
Reaction eventsuseCommentEventCallback('addReaction' | 'deleteReaction' | 'toggleReaction')None
The emoji set is defined by the customReactions prop (ReactionMap: { [reactionId]: { url?, svg?, emoji? } }). Reaction add/delete/toggle requests reference a reactionId and optional customReaction (ReactionItem).

Attachment

A file on a comment.
FieldTypeMeaningExposed byWritable via
attachmentIdnumberUnique id.Comment.attachments[], useGetAttachmentread-only (auto-generated)
namestringFile name.sameset on upload
sizenumberFile size in bytes.sameread-only
typeRecorderFileFormatFile type.sameread-only
mimeTypeanyMIME type.sameread-only
urlstringDownload URL.sameread-only (server-set)
bucketPathstringStorage path.sameread-only
thumbnail / thumbnailWithPlayIconUrl / previewImagesstring / string[]Preview images.sameread-only
metadataanyArbitrary attachment metadata.sameset on upload (metadata in upload options)
isAttachmentResolverUsedbooleanWhether this attachment was served via the attachment resolver (self-hosting).sameread-only (server-derived)
Mutate via useAddAttachment (addAttachment({annotationId,files})), useDeleteAttachment, useGetAttachment. allowedFileTypes prop whitelists types.

CustomStatus / CustomPriority / CustomCategory

Definition objects you supply via props/APIs to populate the status/priority/category controls. They share a base {id, color, name} (CustomFilter).
TypeFieldsDefined viaLands on
CustomStatusid, color, name, type:'default'|'ongoing'|'terminal', lightColor?, svg?, iconUrl?customStatus prop or commentElement.setCustomStatus([...])CommentAnnotation.status
CustomPriorityid, color, name, lightColor?, svg?, iconUrl?customPriority propCommentAnnotation.priority
CustomCategoryid, color, namecustomCategory propCommentAnnotation.commentCategories[]
The type on CustomStatus drives lifecycle semantics (terminal ≈ resolved/done). See Component config for the exact prop shapes and examples. Right-tool note: statuses/priorities are a small fixed workflow vocabulary; for arbitrary per-thread labels prefer customCategory or the thread context bag rather than overloading status.

Views

Read-receipt / view-tracking data. Surfaced on CommentAnnotation.views and through the views feature. CommentAnnotationViews = { views: { [userSnippylyId]: { timestamp } }, comments: { [commentId]: { views: {...} } }, metadata? }. So per-thread and per-comment “seen by whom and when” is keyed by userSnippylyId (the internal user id on User). Views also has documentViews and locationViews (aggregated by user and by date).
ReadExposed byWritable via
Per-thread / per-comment viewsCommentAnnotation.views, .viewedBy, .viewedByUserIdsread-only (server-derived)
Aggregate analyticsuseUniqueViewsByUser, useUniqueViewsByDate, useViewsUtilsread-only

Location

A sub-document scope. Threads, reactions, presence and cursors all carry a location. Location fields: id, locationName, version ({id, name}), plus arbitrary custom keys ([key: string]: any): see storage patterns. Set via useSetLocation/useSetLocations/useSetRootLocation (or the location/locationId props on comment surfaces). Filterable in the sidebar via the location filter. The SDK derives a numeric locationId hash from the Location object.

PageInfo

Page metadata captured when an annotation is created, plus per-feature deep-link URLs. Surfaced on CommentAnnotation.pageInfo and ReactionAnnotation.pageInfo (read); written via useSetPageInfo() ({ setPageInfo }) / cleared via useClearPageInfo().
FieldTypeMeaning
urlstringFull URL of the page.
pathstringPath, excluding the base URL.
queryParamsstringQuery-string of the page.
baseUrlstringBase URL (domain).
titlestringPage title.
screenWidthnumberAuthor’s screen width (auto-generated).
deviceInfoIDeviceInfoDevice/screen info.
commentUrl / recorderUrl / tagUrl / areaUrl / arrowUrlstringDeep-link reference URLs to the respective annotation type.
All fields are optional. Read-only except via useSetPageInfo (you supply the override; the SDK auto-fills screenWidth/deviceInfo).

CustomAnnotationDropdownData / CustomAnnotationDropdownItem

The shape behind a thread’s CommentAnnotation.customList: a custom dropdown attached to an annotation via createCustomListDataOnAnnotation(data) (see APIs).
  • CustomAnnotationDropdownItem = { id: string, label: string }: a single selectable option.
  • CustomAnnotationDropdownData = { type: 'multi' | 'single', placeholder?: string, data: CustomAnnotationDropdownItem[] }: the dropdown definition (type controls single vs multi-select). The selected items land on CommentAnnotation.customList as CustomAnnotationDropdownItem[].
Not the same as Comment.customList. That one is a per-reply autocomplete list (AutocompleteReplaceData[]), set via createCustomListDataOnComment. The annotation-level dropdown is CustomAnnotationDropdownItem[].

TargetTextRange

The anchored text selection on a text/inline comment (CommentAnnotation.targetTextRange). Most fields are xpath internals (commonAncestorContainer, commonAncestorContainerFXpath, commonAncestorContainerCFXpath, commonAncestorContainerAnchor, occurrence) used to re-resolve the selection in the DOM. The one customer-useful field:
  • text (string): the actual selected text the comment was anchored to. Read this to show “commented on: …”. The remaining fields are anchor-resolution internals; treat them as opaque.

AgentData

Identity + output of an AI agent that authored a thread/reply. Present on CommentAnnotation.agent and Comment.agent when sourceType === 'agent'.
FieldTypeMeaning
agentNamestringCanonical agent display name (card header).
namestringLegacy display name; superseded by agentName.
avatarstringAgent avatar URL (falls back to a default icon).
resultAgentResultStructured output; result.title is the bold headline on the suggestion card.
agentFieldsstring[]Queryable agent attributes; matchable via CommentRequestQuery.agentFields.
Read-only from the SDK: written upstream by the agent; there is no client API to author or edit agent. The agent-suggestion UI renders only when agent is present, type === 'suggestion', and the viewer is an admin.

Notification

A single notification row. Read with useNotificationsData(query?)Notification[].
FieldTypeMeaningWritable via
idstringNotification id.read-only
notificationSourcestringSource feature (e.g. comment).read-only
actionTypestringWhat happened (e.g. added/updated).read-only
isUnreadbooleanUnread for this user.read (mark-read flows on the panel)
isNotificationResolverUsedbooleanWhether this notification’s copy was produced by the notification resolver.read-only
isCommentResolverUsedbooleanWhether the comment text shown came via the comment resolver (self-hosting).read-only
actionUserUserWho triggered it.read-only
timestampnumberWhen it occurred.read-only
forYoubooleanWhether it targets the current user (“For You” tab).read-only
displayHeadlineMessage / displayBodyMessagestringRendered text.read-only (template-driven)
displayHeadlineMessageTemplate / displayBodyMessageTemplate (+ …TemplateData)string / objectMessage templates + variables (override copy).resolver / template config
targetAnnotationIdstringThe annotation this notification points to.read-only
metadataNotificationMetadatadocumentId, locationId, location, org ids.read-only
notifyUsers / notifyUsersByUserIdmapsRecipients by email / user id.read-only
notificationSourceDataanyRaw source payload.read-only

NotificationRawData (the notification resolver input)

When you customize notification copy through the notification resolver (or templates), the SDK builds a raw notification object first: NotificationRawData: which is the shape the resolver round-trip is formed from. The guide tells customizers to use the resolver; this is what it receives. Key fields you can read/branch on:
FieldTypeMeaning
oldStatus / newStatusstringStatus before/after a status-change notification.
oldAccess / newAccessstringAccess mode before/after an access-change notification.
annotationDataanyThe triggering annotation object (targetAnnotationData holds the specific comment).
customDataanyArbitrary custom data carried through to the resolver/template.
notifyUsers{ [email]: boolean }Recipient map (by email hash).
actionUser / actionType / notificationSourceUser / enum / enumWho acted, what happened, and the source feature.
displayHeadlineMessageTemplate / displayBodyMessageTemplate (+ …TemplateData)string / objectTemplate + variables you override to customize copy.
This is the raw, server-shaped input: distinct from the Notification row above (the resolved object you read in the UI via useNotificationsData). Counts via useUnreadNotificationsCount(){ forYou, all }. Settings via useNotificationSettings() (settings, setSettings, setSettingsInitialConfig). Panel tabs configured by tabConfig (forYou/documents/all/people). Notification content itself is not mutated by a client write API: customize copy through templates/resolvers, behavior through props/settings.

PresenceUser

A user currently present on the document. Read with usePresenceData(query?)GetPresenceDataResponse (or the deprecated usePresenceUsers()PresenceUser[]).
FieldTypeMeaning
userIdstringUser id.
name / email / photoUrlstringIdentity (from identify).
onlineStatusstringactive / inactive / offline (auto-managed).
color / textColor / initialstringAvatar colors / initial.
timestampanyLast heartbeat time.
location / locationIdLocation / numberWhere the user is present.
isTabAway / isUserIdlebooleanActivity flags.
isReadOnly / isAnonymousbooleanAccess flags.
localOnlybooleanCustom user added via addUser, not persisted.
userSnippylyIdstringInternal id.
All presence fields are read-only (server-/SDK-managed); identity comes from identify(). Change events: usePresenceEventCallback('multipleUsersOnline' \| 'userStateChange'). Inactivity thresholds set via VeltPresence props (inactivityTime, offlineInactivityTime).

CursorUser

A user’s live cursor. Read with useCursorUsers()CursorUser[].
FieldTypeMeaning
userId / name / email / photoUrlNoneIdentity.
positionX / positionYnumberLive mouse position.
commentstringShort note attached to the cursor.
onlineStatusstringactive / inactive / offline.
color / textColor / initialstringCursor colors / initial.
screenWidth / screenHeight / screenScrollHeightnumberAuthor screen dimensions.
targetElement / positionTargetElement / CursorPositionAnchor for the cursor.
location / locationIdLocation / numberScope.
timestampanyLast update.
All read-only (auto-generated live). Change event: onCursorUserChange on VeltCursor. Avatars on cursors via the avatarMode prop.

RecordedData

A finished recording attached to a comment. Read with useGetRecording() (imperative) or from Comment.recorders[]; also delivered by useRecorderAddHandler().
FieldTypeMeaning
idstringRecorder annotation id.
typestringaudio / video / screen.
videoUrl / audioUrlstringMedia URLs.
thumbnailUrl / thumbnailWithPlayIconUrlstringPoster images.
videoPlayerUrlstringHosted player URL.
tagstringEmbeddable player tag.
chunkUrls{ [n]: string }Per-chunk upload URLs.
Mutate via useDeleteRecording / useGetRecording. Recorder change events via useRecorderEventCallback(...) and the recorder events in Events (recordingDone, transcriptionDone, …). Recording media URLs are server-set (read-only).

ActivityRecord

A single activity-feed event. Read with useAllActivities(config?)ActivityRecord[].
FieldTypeMeaning
idstringActivity id.
featureType'comment'|'reaction'|'recorder'|'crdt'|'custom'Originating feature.
actionTypestringentity_type.action (e.g. comment_annotation.status_change, comment.add).
actionUserUserWho acted.
timestampnumberWhen (server-set).
targetEntityIdstringParent entity id (e.g. annotationId).
targetSubEntityIdstring | nullSub-entity id (e.g. commentId); null for entity-level actions.
changes{ [key]: { from?, to? } }From/to change pairs (e.g. status, assignedTo).
entityData / entityTargetDatasnapshotFull parent / sub-entity snapshot at action time.
displayMessagestringComputed display text (never stored).
displayMessageTemplate (+ …TemplateData)string / objectCustom activities only: your own message template.
actionIconstringIcon URL/identifier.
immutablebooleanIf true, cannot be updated/deleted via REST.
Scope/filter via ActivitySubscribeConfig (documentIds, featureTypes, actionTypes, userIds, maxDays, …). Reading is reactive; activities are written by the features themselves (custom activities are created server-side / via REST, not a React mutation hook in this guide).

LiveStateData

Arbitrary application state synced across collaborators. Read/write with useLiveStateData / useSetLiveStateData (and useUiState for shared UI state).
FieldTypeMeaningWritable via
idstringMD5 of liveStateDataId.read-only (derived)
liveStateDataIdstringYour sync key.set at write
datastring|number|boolean|JSONThe synced value (this is your free-form payload).useSetLiveStateData (merge? to deep-merge)
updatedByUserWho last wrote it.read-only (auto)
lastUpdatedanyLast write time.read-only (auto)
tabIdstring | nullOriginating tab.auto
This is the general-purpose escape hatch for syncing custom data that has no home on any other entity. Single-editor-mode state (SingleEditorLiveStateData: editor, requestEditorAccess) rides the same feature; access events are in Events under Live State Sync.

Custom-data storage patterns

Four places hold arbitrary application data. Pick by lifetime and scope:
MechanismShapeSet viaSurfaces onUse for
Thread contextcontext?: anyuseSetContextProvider() (a (documentId, location?) => context provider), the addContext(context) callback on the addCommentAnnotation event, or the context prop on comment surfaces (VeltComments, VeltCommentTool, VeltCommentPin, VeltCommentBubble, sidebars, inline sections)CommentAnnotation.context (+ derived contextId)App state a thread was created against (record id, route, selection metadata). Filterable via the sidebar custom filter.
Reply metadatametadata?: anythe metadata field on the Comment you pass to useAddComment / useUpdateCommentComment.metadataPer-reply app data persisted as-is (only present when you set it).
Location custom fields{ id, locationName, version?, [key]: any }useSetLocation / useSetLocationsCommentAnnotation.location, PresenceUser.location, CursorUser.locationSub-document scoping + extra attributes that travel with the scope.
CustomCategory{ id, color, name }customCategory propCommentAnnotation.commentCategories[]A fixed, filterable label vocabulary (the right tool for tag-like categorization).
For larger/looser app state with no entity home, use LiveStateData (above). The context bag is the most common choice for “attach my record id to this thread.”

Absences & limits

State these as confident “not supported” verdicts:
  • Per-edit editor identity is not stored. Comment has isEdited and editedAt but no field for who edited a reply. “Who edited this reply” cannot be shown client-side from the model. Only the original author (Comment.from) is available.
  • No per-reply createdBy-vs-editedBy distinction. from is the author; there is no separate last-editor user object on Comment or CommentAnnotation (only statusUpdatedByUserId and resolvedByUserId exist, scoped to those specific actions).
  • agent, suggestion, involvedUserIds, mentionedUserIds, views/viewedBy are read-only. They are written upstream/server-side; no client API mutates them. UI that displays them is feasible; UI that edits them is not (for suggestions, use the Suggestion element API, not a field write).
  • Notification content has no client write API. You cannot mutate a Notification object directly. Change copy through message templates / the notification resolver, and behavior through tabConfig / settings, not by writing the entity. This forces a template/resolver (server-side) path for custom notification text.
  • Presence/cursor fields are fully managed. onlineStatus, positions, colors, timestamps are auto-generated; you influence them only through identity (identify) and config props (inactivityTime, avatarMode, …), never by writing the field.
  • Activity records are not mutated from React. Reading is reactive (useAllActivities); creating/updating custom activities and deleting records is a server-side/REST path, and immutable records reject REST mutation entirely.
  • context is set, not patched per-field client-side. It is provided wholesale (provider/addContext/prop) and hashed into contextId; there is no React hook to mutate one key of an existing thread’s context in place (server REST is the path for editing stored context).

Runtime-access map

Quick lookup: entity → the hook that reads it live → the event that fires on change.
EntityRead hookChange event (.on(...) / use…EventCallback)
CommentAnnotation (all)useCommentAnnotations()addCommentAnnotation, deleteCommentAnnotation, updateStatus, updatePriority, assignUser, updateAccess, resolveComment
CommentAnnotation (one)useCommentAnnotationById({annotationId})same
CommentAnnotation (filtered)useGetCommentAnnotations(query?)same
Comment (reply)CommentAnnotation.comments[] / useGetComment()addComment, updateComment, deleteComment
User (current)useCurrentUser()userUpdate (core)
UserContact (lists)useContactList() / useContactSelected()autocompleteSearch
ReactionAnnotation / Reactionvia Comment.reactionAnnotationsaddReaction, deleteReaction, toggleReaction
Attachmentvia Comment.attachments / useGetAttachment()addAttachment, deleteAttachment, attachmentDownloadClicked
RecordedDatauseGetRecording() / useRecorderAddHandler()recorder events (recordingDone, transcriptionDone, deleteRecording)
NotificationuseNotificationsData() / useUnreadNotificationsCount()settingsUpdated
PresenceUserusePresenceData()multipleUsersOnline, userStateChange
CursorUseruseCursorUsers()onCursorUserChange (VeltCursor prop)
ActivityRecorduseAllActivities(config?)(reactive stream; no separate change event)
LiveStateDatauseLiveStateData() / useUiState()live-state-sync events (editorAssigned, accessRequested, …)
ViewsCommentAnnotation.views / useUniqueViewsByUser(reactive; server-derived)