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.
## Absences & limits.
How to read each table:
- Field: the property name as it appears on the object you receive.
- Type: its shape.
- Meaning: what it holds.
- Exposed by: the read hook (or event payload) that carries it live.
- Writable via: the API that changes it.
read-onlymeans there is no client API to set it (server-derived or auto-generated).
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 byuseCommentAnnotations(). Its comments array holds the individual replies.
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.
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’scomments 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[]).
No per-edit author identity.CommentrecordsisEdited/editedAtbut 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.
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
AReactionAnnotation 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.
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.
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).
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 onCommentAnnotation.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).
Location
A sub-document scope. Threads, reactions, presence and cursors all carry alocation.
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 onCommentAnnotation.pageInfo and ReactionAnnotation.pageInfo (read); written via useSetPageInfo() ({ setPageInfo }) / cleared via useClearPageInfo().
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’sCommentAnnotation.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 (typecontrols single vs multi-select). The selected items land onCommentAnnotation.customListasCustomAnnotationDropdownItem[].
Not the same asComment.customList. That one is a per-reply autocomplete list (AutocompleteReplaceData[]), set viacreateCustomListDataOnComment. The annotation-level dropdown isCustomAnnotationDropdownItem[].
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 onCommentAnnotation.agent and Comment.agent when sourceType === 'agent'.
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 withuseNotificationsData(query?) → Notification[].
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:
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 withusePresenceData(query?) → GetPresenceDataResponse (or the deprecated usePresenceUsers() → PresenceUser[]).
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 withuseCursorUsers() → CursorUser[].
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 withuseGetRecording() (imperative) or from Comment.recorders[]; also delivered by useRecorderAddHandler().
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 withuseAllActivities(config?) → ActivityRecord[].
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 withuseLiveStateData / useSetLiveStateData (and useUiState for shared UI state).
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:
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.
CommenthasisEditedandeditedAtbut 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-editedBydistinction.fromis the author; there is no separate last-editor user object onCommentorCommentAnnotation(onlystatusUpdatedByUserIdandresolvedByUserIdexist, scoped to those specific actions). agent,suggestion,involvedUserIds,mentionedUserIds,views/viewedByare 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
Notificationobject directly. Change copy through message templates / the notification resolver, and behavior throughtabConfig/ 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, andimmutablerecords reject REST mutation entirely. contextis set, not patched per-field client-side. It is provided wholesale (provider/addContext/prop) and hashed intocontextId; there is no React hook to mutate one key of an existing thread’scontextin place (server REST is the path for editing stored context).

