> ## Documentation Index
> Fetch the complete documentation index at: https://velt.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Complete Field Inventory

> An exhaustive, ground-truthed inventory of every persisted self-hosting field — Velt's DB vs. your DB — with type, example value, description, and notes for comments, reactions, recordings, notifications, activity, and attachments.

> **Companion to the [Self-Hosting Overview](/self-host-data/overview).** That guide's field-inventory
> section lists the field *names* split between Velt's DB and your DB. This page adds **Type**,
> **Example value**, **Description**, and **Notes** for every field, covers **both sides** of the
> split, and **expands every nested structural object** into its own sub-table. Ground-truthed against
> the SDK models and the resolver *strip* logic, not just the guide.

***

## How to read this

When a customer registers a data provider (`Velt.setDataProviders({ … })`), the split happens
**on the frontend, before anything is written**:

* **Velt's DB** keeps the **structural remainder** — IDs, positions,
  locations/targets, statuses, timestamps, relationships, and flags.
* **Your DB** receives the **`Partial<X>` PII payload** the SDK strips on the device and hands to your
  provider's `save` (or `saveConfig.url`) — then merges back on read so the UI renders identically to
  a fully-Velt-hosted setup.

> ### 🔒 Privacy guarantee
>
> **Anything not stored in Velt's DB never leaves the frontend device for Velt's network — not in
> transit, not at rest.** The PII is stripped on the device **before** any request to Velt is made and
> is sent only to your DB (or recomputed entirely on the client). Velt never receives it, transmits
> it, or stores it.

### Note vocabulary

| Term                   | Meaning                                                                                                                                                                                                                                                                                                                   |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **kept**               | Sent to Velt and written to its DB verbatim.                                                                                                                                                                                                                                                                              |
| **reduced**            | User object collapsed to `{ userId }` before any write — only the `userId` is sent to Velt; the rest of the user object (name / email / avatar) is **never sent to Velt**.                                                                                                                                                |
| **never sent to Velt** | The field's data is stripped on the frontend **before** any write, so it never leaves the device for Velt — not in transit, not at rest. It goes only to your DB (when applicable) or is recomputed on the client. (For a few fields the *key* is still written to Velt as `null` / `{}` — only the *value* is withheld.) |
| **copied-not-moved**   | Sent to **both** your DB and Velt's DB (replicated, not relocated).                                                                                                                                                                                                                                                       |
| **@deprecated**        | Still stored for back-compat; do not rely on it.                                                                                                                                                                                                                                                                          |

> **Features covered:** `comment`, `reaction`, `recorder`, `notification`, `activity`, `attachment`
> (+ shared building blocks). Features **without** a self-hosting data-provider split — `cursor`,
> `presence`, `huddle`, `selection`, `tag`, `area`, `arrow`, `rewriter`, standalone `transcription` —
> are **not** included; they have no resolver and stay fully Velt-hosted.

***

## Table of Contents

1. [Comments](#1-comments-comment)
2. [Reactions](#2-reactions-reaction)
3. [Recordings](#3-recordings-recorder)
4. [Notifications](#4-notifications-notification)
5. [Activity](#5-activity-activity)
6. [Attachments](#6-attachments-attachment)
7. [Shared building blocks](#7-shared-building-blocks)
8. [Summary matrix & field counts](#8-summary-matrix--field-counts)

***

## 1. Comments (`comment`)

Model: `CommentAnnotation`. PII payload: `PartialCommentAnnotation`.

### 1.A — Stored in Velt's DB (`CommentAnnotation`, structural remainder)

| Field                          | Type                                                                              | Example                                                                                 | Description                                                              | Notes                                                                                                                                      |
| ------------------------------ | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `annotationId`                 | `string`                                                                          | `"a8f3c2e1-9b4d-4e7a-8c1f-2d3e4f5a6b7c"`                                                | Unique id for the comment pin annotation.                                | auto-generated; join key (also in your DB)                                                                                                 |
| `annotationNumber`             | `number`                                                                          | `42`                                                                                    | Sequential pin number.                                                   | auto-generated                                                                                                                             |
| `visibilityConfig`             | `CommentAnnotationVisibilityConfig` `{ type; organizationId?; userIds?[] }`       | `{ type: 'private', userIds: ['u_1','u_2'] }`                                           | Who can see the annotation (public/private/org/user-list).               | kept                                                                                                                                       |
| `comments`                     | `Comment[]`                                                                       | `[{ commentId: 482910, type: 'text', from: { userId: 'u_1' } }]`                        | All comments in the thread.                                              | kept; per-comment PII never sent to Velt — see [Comment sub-table](#comment-per-thread-comment)                                            |
| `commentCategories`            | `CustomCategory[]`                                                                | `[{ id: 'bug', name: 'Bug', color: '#f00' }]`                                           | Categories the annotation belongs to.                                    | defaults `[]`                                                                                                                              |
| `from`                         | `User` → `{ userId }`                                                             | `{ userId: 'u_1' }`                                                                     | Creator of the annotation.                                               | reduced when `user` provider active; **copied-not-moved**                                                                                  |
| `color`                        | `string`                                                                          | `"#1F64FF"`                                                                             | Pin color.                                                               | kept                                                                                                                                       |
| `resolved`                     | `boolean`                                                                         | `false`                                                                                 | Whether marked resolved.                                                 | **@deprecated**                                                                                                                            |
| `inProgress`                   | `boolean`                                                                         | `false`                                                                                 | Whether marked in-progress.                                              | **@deprecated**                                                                                                                            |
| `lastUpdated`                  | `Timestamp` (`any`)                                                               | `{ seconds: 1717804800, nanoseconds: 0 }`                                               | Last-updated time.                                                       | auto-generated                                                                                                                             |
| `createdAt`                    | `Timestamp` (`any`)                                                               | `{ seconds: 1717800000, nanoseconds: 0 }`                                               | Created time.                                                            | auto-generated                                                                                                                             |
| `positionX`                    | `number`                                                                          | `320.5`                                                                                 | Pin X position.                                                          | auto-generated                                                                                                                             |
| `positionY`                    | `number`                                                                          | `148.2`                                                                                 | Pin Y position.                                                          | auto-generated                                                                                                                             |
| `screenWidth`                  | `number`                                                                          | `1440`                                                                                  | Author screen width.                                                     | auto-generated                                                                                                                             |
| `screenHeight`                 | `number`                                                                          | `900`                                                                                   | Author screen height.                                                    | auto-generated                                                                                                                             |
| `screenScrollHeight`           | `number`                                                                          | `3200`                                                                                  | Author scroll height.                                                    | auto-generated                                                                                                                             |
| `screenScrollTop`              | `number`                                                                          | `640`                                                                                   | Author scroll-top offset.                                                | auto-generated                                                                                                                             |
| `taggedElementPath`            | `string`                                                                          | `"/html/body/div[1]/main/p[3]"`                                                         | XPath of the clicked element.                                            | auto-generated                                                                                                                             |
| `taggedElementRect`            | `any` (DOMRect-like)                                                              | `{ x: 120, y: 300, width: 200, height: 24 }`                                            | Bounding rect of the clicked element.                                    | auto-generated                                                                                                                             |
| `targetElement`                | `TargetElement \| null`                                                           | `{ xpath: '/html/body/div', topPercentage: 42 }`                                        | DOM anchor of the comment.                                               | kept — see [TargetElement](#targetelement) (its own `targetText` IS sent to Velt; only `targetTextRange.text` is withheld)                 |
| `targetElementId`              | `string \| null`                                                                  | `"editor-section-2"`                                                                    | Target element id you supplied.                                          | kept                                                                                                                                       |
| `position`                     | `CursorPosition \| null`                                                          | `{ top: 148, left: 320 }`                                                               | Pin position descriptor.                                                 | kept — see [CursorPosition](#cursorposition)                                                                                               |
| `locationId`                   | `number \| null`                                                                  | `987654321`                                                                             | Hash of `location`.                                                      | kept                                                                                                                                       |
| `location`                     | `Location \| null`                                                                | `{ id: 'page-1', version: { id: 'v1', name: 'V1' } }`                                   | Sub-document location.                                                   | kept — see [Location](#location--version)                                                                                                  |
| `type`                         | `string`                                                                          | `"comment"`                                                                             | Discriminator (`comment` \| `suggestion`).                               | default `'comment'`; load-bearing suggestion discriminator                                                                                 |
| `commentType`                  | `string`                                                                          | `"suggestion"`                                                                          | Secondary discriminator.                                                 | load-bearing suggestion discriminator                                                                                                      |
| `sourceType`                   | `string`                                                                          | `"agent"`                                                                               | Origin; `'agent'` = AI-authored.                                         | kept                                                                                                                                       |
| `metadata`                     | `CommentMetadata` (extends `BaseMetadata`; `[key]: any`)                          | `{ documentId: 'doc_1', organizationId: 'org_1', apiKey: 'velt_xxx' }`                  | Org/doc/api routing context.                                             | **full** kept in Velt's DB; `getClientMetadata` copy sent to your DB — see [BaseMetadata](#basemetadata)                                   |
| `targetTextRange`              | `TargetTextRange \| null`                                                         | `{ commonAncestorContainerFXpath: '/html/body/div/p', occurrence: 1 }`                  | Text-comment anchor.                                                     | kept **minus `.text`** (only `text` is withheld) — see [TargetTextRange](#targettextrange)                                                 |
| `selectAllContent`             | `boolean`                                                                         | `true`                                                                                  | Comment spans all text of the target element.                            | kept                                                                                                                                       |
| `approved`                     | `boolean`                                                                         | `false`                                                                                 | Approval flag.                                                           | kept                                                                                                                                       |
| `status`                       | `CustomStatus`                                                                    | `{ id: 'OPEN', name: 'Open', type: 'DEFAULT', color: '#888' }`                          | Workflow status.                                                         | default `OPEN`                                                                                                                             |
| `statusUpdatedByUserId`        | `string \| null`                                                                  | `"u_1"`                                                                                 | Who last changed status.                                                 | already a userId                                                                                                                           |
| `annotationIndex`              | `number`                                                                          | `1`                                                                                     | 1-based index in the available list.                                     | kept                                                                                                                                       |
| `pageInfo`                     | `PageInfo`                                                                        | `{ title: 'Home', url: 'https://app.com' }`                                             | Page context at creation.                                                | default `new PageInfo()` — see [PageInfo](#pageinfo)                                                                                       |
| `assignedTo`                   | `User` → `{ userId }`                                                             | `{ userId: 'u_2' }`                                                                     | Assignee.                                                                | reduced when `user` provider active; **copied-not-moved**                                                                                  |
| `priority`                     | `CustomPriority`                                                                  | `{ id: 'P1', name: 'High', color: '#f00' }`                                             | Priority.                                                                | kept                                                                                                                                       |
| `ghostComment`                 | `GhostComment \| null` `{ targetElement?; message?; type?; isSameGroup? }`        | `{ message: 'element not found', type: 'desktop' }`                                     | Placeholder when target element is missing.                              | kept                                                                                                                                       |
| `areaAnnotationId`             | `string`                                                                          | `"area_77"`                                                                             | Connected area annotation id.                                            | kept                                                                                                                                       |
| `context`                      | `any`                                                                             | `{ access: { roles: ['editor'] }, accessFields: ['roles'] }`                            | Your custom context data.                                                | kept; `context.access` and `context.accessFields` are **mandatory** for querying and are always retained (not subject to `fieldsToRemove`) |
| `contextId`                    | `string`                                                                          | `"ctx_abc123"`                                                                          | Hash of `context`.                                                       | kept                                                                                                                                       |
| `iam`                          | `CommentIAMConfig` `{ accessMode? }`                                              | `{ accessMode: 'PUBLIC' }`                                                              | IAM access config.                                                       | default `new CommentIAMConfig()`                                                                                                           |
| `isPageAnnotation`             | `boolean`                                                                         | `false`                                                                                 | Page-level annotation flag.                                              | kept                                                                                                                                       |
| `targetInlineCommentElementId` | `string`                                                                          | `"inline-sec-3"`                                                                        | Inline-comment target element id.                                        | kept                                                                                                                                       |
| `inlineCommentSectionConfig`   | `InlineCommentSectionConfig` `{ id; name? }`                                      | `{ id: 'sec_3', name: 'Section 3' }`                                                    | Inline-comment section config.                                           | kept                                                                                                                                       |
| `customList`                   | `CustomAnnotationDropdownItem[]`                                                  | `[{ id: 'tag1', label: 'Backend' }]`                                                    | Custom dropdown items.                                                   | defaults `[]`                                                                                                                              |
| `subscribedUsers`              | `CommentAnnotationSubscribedUsers` `{ [hash]: { user: User; type } }`             | `{ 'h_u1': { user: { userId: 'u_1' }, type: 'manual' } }`                               | Subscribed users.                                                        | nested `user` reduced when `user` provider active                                                                                          |
| `unsubscribedUsers`            | `CommentAnnotationUnsubscribedUsers`                                              | `{ 'h_u3': { user: { userId: 'u_3' }, type: 'auto' } }`                                 | Unsubscribed users.                                                      | nested `user` reduced when `user` provider active                                                                                          |
| `subscribedGroups`             | `CommentAnnotationSubscribedGroups` `{ [groupId]: { type } }`                     | `{ 'grp_eng': { type: 'manual' } }`                                                     | Subscribed groups.                                                       | defaults `{}`                                                                                                                              |
| `resolvedByUserId`             | `string \| null`                                                                  | `"u_1"`                                                                                 | Who resolved the annotation.                                             | already a userId; **copied-not-moved**                                                                                                     |
| `multiThreadAnnotationId`      | `string`                                                                          | `"thread_5"`                                                                            | Links multi-thread annotations.                                          | kept                                                                                                                                       |
| `isDraft`                      | `boolean`                                                                         | `false`                                                                                 | Draft flag.                                                              | kept                                                                                                                                       |
| `sourceId`                     | `string`                                                                          | `"src_99"`                                                                              | Source id.                                                               | kept                                                                                                                                       |
| `views`                        | `CommentAnnotationViews`                                                          | `{ views: { 'u_1': { timestamp: 1717800000 } } }`                                       | View tracking.                                                           | kept — see [CommentAnnotationViews](#commentannotationviews)                                                                               |
| `viewedByUserIds`              | `string[]`                                                                        | `["u_1","u_2"]`                                                                         | Viewer userIds.                                                          | already userIds                                                                                                                            |
| `involvedUserIds`              | `string[]`                                                                        | `["u_1","u_2"]`                                                                         | All userIds participating in the thread (authors, assignees, @mentions). | already userIds; auto-maintained                                                                                                           |
| `suggestion`                   | `SuggestionData`                                                                  | `{ status: 'pending', resolvedBy: { userId: 'u_1' } }`                                  | SDK-managed suggestion data (iff `type === 'suggestion'`).               | kept; nested `resolvedBy` reduced when `user` provider active                                                                              |
| `agent`                        | `AgentData` `{ agentName?; name?; avatar?; result?: { title? }; agentFields?[] }` | `{ agentName: 'Reviewer Bot', result: { title: 'Fix typo' }, agentFields: ['result'] }` | AI-agent identity + output.                                              | read-only from SDK; kept; `agent.agentFields` is **mandatory** for querying and is always retained (not subject to `fieldsToRemove`)       |

### 1.B — Stored in your DB (`PartialCommentAnnotation`)

| Field                           | Type                                        | Example                                                                                   | Description                                | Notes                                                                                         |
| ------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
| `annotationId`                  | `string`                                    | `"a8f3c2e1-…"`                                                                            | Join key.                                  | required; also in Velt's DB                                                                   |
| `metadata`                      | `BaseMetadata`                              | `{ documentId: 'doc_1', organizationId: 'org_1', apiKey: 'velt_xxx' }`                    | Client-facing metadata.                    | `getClientMetadata(data.metadata ?? {})`                                                      |
| `comments`                      | `{ [commentId]: PartialComment }`           | `{ '482910': { commentId: 482910, commentText: 'Looks good', from: { userId: 'u_1' } } }` | Per-comment PII map.                       | required (`{}`); re-keyed array → map                                                         |
| `comments[].commentId`          | `string \| number`                          | `482910`                                                                                  | Per-comment id.                            | always sent                                                                                   |
| `comments[].commentHtml`        | `string`                                    | `"<p>Looks good 👍</p>"`                                                                  | Rich-text body (PII).                      | only if truthy; **never sent to Velt** (stripped on the frontend → your DB)                   |
| `comments[].commentText`        | `string`                                    | `"Looks good 👍"`                                                                         | Plain-text body (PII).                     | only if truthy; **never sent to Velt** (stripped on the frontend → your DB)                   |
| `comments[].attachments`        | `{ [attachmentId]: PartialAttachment }`     | `{ 1: { attachmentId: 1, name: 'spec.pdf', url: 'https://cdn/…/spec.pdf' } }`             | Per-comment attachment name/url.           | only when the **`attachment`** resolver is also active                                        |
| `comments[].from`               | `PartialUser` `{ userId }`                  | `{ userId: 'u_1' }`                                                                       | Comment author.                            | only if truthy                                                                                |
| `comments[].to`                 | `PartialUser[]`                             | `[{ userId: 'u_2' }]`                                                                     | @mentioned users.                          | only if truthy                                                                                |
| `comments[].taggedUserContacts` | `{ userId; contact?: { userId }; text? }[]` | `[{ userId: 'u_2', text: '@Jane' }]`                                                      | Tagged contacts (PII text labels).         | only if truthy                                                                                |
| `from`                          | `PartialUser` `{ userId }`                  | `{ userId: 'u_1' }`                                                                       | Annotation creator.                        | only if truthy; **copied-not-moved**                                                          |
| `assignedTo`                    | `PartialUser` `{ userId }`                  | `{ userId: 'u_2' }`                                                                       | Assignee.                                  | only if truthy; **copied-not-moved**                                                          |
| `targetTextRange`               | `{ text: string }`                          | `{ text: 'the selected sentence' }`                                                       | Selected text content (PII).               | the `.text` is **never sent to Velt** (stripped → your DB); rest of `targetTextRange` is sent |
| `resolvedByUserId`              | `string \| null`                            | `"u_1"`                                                                                   | Who resolved it.                           | only if truthy; **copied-not-moved**                                                          |
| `[fieldsToRemove]`              | `any`                                       | `{ internalTicketId: 'JIRA-42' }`                                                         | Your custom fields kept off Velt entirely. | per `config.fieldsToRemove`; truthy-gated; **never sent to Velt** (→ your DB)                 |
| `[additionalFields]`            | `any`                                       | `{ teamName: 'Growth' }`                                                                  | Your custom fields replicated.             | per `config.additionalFields`; deep-copied; sent to **both** (falsy preserved)                |

### 1.C — Comment strip rules

* The only PII stripped on the frontend (and therefore **never sent to Velt**) is: per-comment
  `commentText`/`commentHtml`, attachment `name`/`url` (when the `attachment` resolver is active),
  `targetTextRange.text`, and any `fieldsToRemove`. Each comment whose PII is withheld gets
  `isCommentResolverUsed = true`; attachments get `isAttachmentResolverUsed = true`.
* `from` / `assignedTo` / `resolvedByUserId` are **copied-not-moved** (sent to both).
* A `save` to your provider only fires when the comment PII actually changed **and** the action maps
  to a `ResolverActions` value (`COMMENT_ANNOTATION_ADD` / `COMMENT_ADD` / `COMMENT_UPDATE` /
  `COMMENT_DELETE`, or a draft). Pure status / priority / assignment changes do **not** call `save`.
* Truthy-gating: empty-string `commentText` etc. are **not** sent to your provider (and are not
  withheld from Velt either). `additionalFields` is the exception — it uses `!== undefined`, so
  `0` / `""` / `false` are copied.
* **Delete** sends only `{ apiKey, documentId, organizationId, folderId? }`.

***

## 2. Reactions (`reaction`)

Model: `ReactionAnnotation`. PII payload: `PartialReactionAnnotation`.

### 2.A — Stored in Velt's DB (`ReactionAnnotation`)

| Field                    | Type                                                      | Example                                                                | Description                                       | Notes                                                                                                            |
| ------------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `annotationId`           | `string`                                                  | `"rXa92Kf0bQ3nLpT7v"`                                                  | Unique reaction-pin id.                           | auto-generated; join key (also in your DB)                                                                       |
| `context`                | `Context`                                                 | `{ category: 'design' }`                                               | Context object at creation.                       | kept                                                                                                             |
| `commentAnnotationId`    | `string`                                                  | `"cmtAnn_5fK28dQ"`                                                     | Connected comment annotation.                     | kept                                                                                                             |
| `reactions`              | `Reaction[]`                                              | `[{ variant: '1f44d', from: { userId: 'u_1' }, lastUpdated: <Date> }]` | Individual reactions.                             | defaults `[]`; each `from` reduced when `user` provider active — see [Reaction sub-table](#reaction-per-element) |
| `lastUpdated`            | `any` (server timestamp)                                  | `1717804800000`                                                        | Annotation last-updated.                          | auto-generated                                                                                                   |
| `targetElement`          | `TargetElement \| null`                                   | `{ xpath: '#cta-btn' }`                                                | DOM anchor.                                       | kept — see [TargetElement](#targetelement)                                                                       |
| `targetElementId`        | `string \| null`                                          | `"cta-btn"`                                                            | Target element id you supplied.                   | kept                                                                                                             |
| `position`               | `CursorPosition \| null`                                  | `null`                                                                 | Pin position.                                     | value **never sent to Velt** — written as `null` on every write to Velt's DB (independent of self-hosting)       |
| `locationId`             | `number \| null`                                          | `1843762915`                                                           | Hash of `location`.                               | auto-generated                                                                                                   |
| `location`               | `Location \| null`                                        | `{ id: 'page-2', version: { id: 'v1', name: 'Draft' } }`               | Sub-document location.                            | kept — see [Location](#location--version)                                                                        |
| `type`                   | `string`                                                  | `"reaction"`                                                           | Discriminator.                                    | default `'reaction'`                                                                                             |
| `annotationIndex`        | `number`                                                  | `3`                                                                    | 1-based index in available list.                  | kept                                                                                                             |
| `pageInfo`               | `PageInfo`                                                | `{ title: 'Dashboard', url: 'https://app.example.com/dashboard' }`     | Page context.                                     | default `new PageInfo()` — see [PageInfo](#pageinfo)                                                             |
| `from`                   | `User` → `{ userId }`                                     | `{ userId: 'u_1' }`                                                    | Creator of the reaction annotation.               | reduced when `user` provider active; **copied-not-moved**                                                        |
| `isReactionResolverUsed` | `boolean`                                                 | `true`                                                                 | Resolver-used flag.                               | flag; set `true` on strip                                                                                        |
| `involvedUserIds`        | `string[]`                                                | `["u_1","u_2"]`                                                        | All userIds that have reacted on this annotation. | already userIds; auto-maintained                                                                                 |
| `metadata`               | `ReactionMetadata` (extends `BaseMetadata`; `[key]: any`) | `{ documentId: 'doc_42', organizationId: 'org_7' }`                    | Routing metadata.                                 | **full** kept; `getClientMetadata` copy sent to your DB                                                          |

### 2.B — Stored in your DB (`PartialReactionAnnotation`)

| Field          | Type                       | Example                                             | Description                                 | Notes                                          |
| -------------- | -------------------------- | --------------------------------------------------- | ------------------------------------------- | ---------------------------------------------- |
| `annotationId` | `string`                   | `"rXa92Kf0bQ3nLpT7v"`                               | Join key.                                   | required; also in Velt's DB                    |
| `metadata`     | `BaseMetadata`             | `{ documentId: 'doc_42', organizationId: 'org_7' }` | Client-facing metadata.                     | `getClientMetadata(annotation.metadata ?? {})` |
| `icon`         | `string`                   | `"1f44d"`                                           | Emoji/icon code — the only relocated field. | **never sent to Velt**                         |
| `from`         | `PartialUser` `{ userId }` | `{ userId: 'u_1' }`                                 | Reaction creator.                           | only if present; **copied-not-moved**          |

### 2.C — Reaction strip rules

* Only `icon` is **never sent to Velt** (stripped on the frontend → your DB); everything else is kept and `isReactionResolverUsed` set `true`.
* `from` is **copied-not-moved**. Per-element `reactions[].from` is reduced to `{ userId }` (when
  `user` provider active) **only inside Velt's DB** — it is not part of the `Partial` payload.
* `position`'s value is **never sent to Velt** — written as `null` on every write to Velt's DB regardless of self-hosting.
* An unchanged save (deep-compare vs. cache) skips stripping entirely (icon not re-processed, flag not set).

***

## 3. Recordings (`recorder`)

Model: `RecorderAnnotation`. PII payload: `PartialRecorderAnnotation`.

### 3.A — Stored in Velt's DB (`RecorderAnnotation`)

| Field                    | Type                                                      | Example                                                                  | Description                          | Notes                                                                                                                                                                         |
| ------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `annotationId`           | `string`                                                  | `"rec_a1b2c3d4"`                                                         | Unique recorder-pin id.              | auto-generated; join key                                                                                                                                                      |
| `context`                | `Context`                                                 | `{ scopeId: 'scope-1' }`                                                 | Context object.                      | kept                                                                                                                                                                          |
| `commentAnnotationId`    | `string`                                                  | `"cmt_99x8"`                                                             | Connected comment annotation.        | kept                                                                                                                                                                          |
| `from`                   | `User` → `{ userId }`                                     | `{ userId: 'user-123' }`                                                 | Creator.                             | **reduced** to `{ userId }`; the full `User` (name/email/avatar) is **never sent to Velt** (→ your DB)                                                                        |
| `color`                  | `string`                                                  | `"#FF5733"`                                                              | Pin color.                           | kept                                                                                                                                                                          |
| `lastUpdated`            | `any`                                                     | `1717804800000`                                                          | Last-updated time.                   | auto-generated                                                                                                                                                                |
| `positionX`              | `number`                                                  | `342`                                                                    | Pin X position.                      | auto-generated                                                                                                                                                                |
| `positionY`              | `number`                                                  | `128`                                                                    | Pin Y position.                      | auto-generated                                                                                                                                                                |
| `screenWidth`            | `number`                                                  | `1920`                                                                   | Author screen width.                 | auto-generated                                                                                                                                                                |
| `screenHeight`           | `number`                                                  | `1080`                                                                   | Author screen height.                | auto-generated                                                                                                                                                                |
| `screenScrollHeight`     | `number`                                                  | `4200`                                                                   | Author scroll height.                | auto-generated                                                                                                                                                                |
| `screenScrollTop`        | `number`                                                  | `320`                                                                    | Author scroll-top.                   | auto-generated                                                                                                                                                                |
| `recorderedElementPath`  | `string`                                                  | `"/html/body/div[2]/button[1]"`                                          | XPath of clicked element.            | auto-generated                                                                                                                                                                |
| `recorderedElementRect`  | `any`                                                     | `{ top: 100, left: 50, width: 200, height: 40 }`                         | Bounding rect of clicked element.    | auto-generated                                                                                                                                                                |
| `targetElement`          | `TargetElement \| null`                                   | `{ xpath: '/html/body/div[2]' }`                                         | DOM anchor.                          | kept — see [TargetElement](#targetelement)                                                                                                                                    |
| `position`               | `CursorPosition \| null`                                  | `{ top: 128, left: 342 }`                                                | Pin position.                        | kept — see [CursorPosition](#cursorposition)                                                                                                                                  |
| `locationId`             | `number \| null`                                          | `1837465921`                                                             | Hash of `location`.                  | kept                                                                                                                                                                          |
| `location`               | `Location \| null`                                        | `{ id: 'doc-1', locationName: 'Page 1' }`                                | Sub-document location.               | kept — see [Location](#location--version)                                                                                                                                     |
| `type`                   | `string`                                                  | `"recorder"`                                                             | Discriminator.                       | default `'recorder'`                                                                                                                                                          |
| `recordingType`          | `RecorderType` (`'audio' \| 'video' \| 'screen'`)         | `"video"`                                                                | Recording kind.                      | default `'audio'`                                                                                                                                                             |
| `mode`                   | `RecorderLayoutMode`                                      | `"floating"`                                                             | Recorder layout mode.                | default `'floating'`                                                                                                                                                          |
| `approved`               | `boolean`                                                 | `true`                                                                   | Approval flag.                       | kept                                                                                                                                                                          |
| `attachment`             | `Attachment \| null`                                      | `null`                                                                   | Single recorded-media attachment.    | **@deprecated**; value **never sent to Velt** — written as `null`; full object → your DB                                                                                      |
| `attachments`            | `Attachment[]`                                            | `[{ attachmentId: 'att_1', name: 'recording.webm' }]`                    | Recording attachments.               | **reduced to stubs** `{ attachmentId, name }` (url etc. never sent to Velt)                                                                                                   |
| `annotationIndex`        | `number`                                                  | `3`                                                                      | 1-based index.                       | kept                                                                                                                                                                          |
| `pageInfo`               | `PageInfo`                                                | `{ title: 'Home', url: 'https://app.example.com' }`                      | Page context.                        | default `new PageInfo()` — see [PageInfo](#pageinfo)                                                                                                                          |
| `recordedTime`           | `{ duration?: number; display?: string } \| null`         | `{ duration: 12500, display: '00:00:12' }`                               | Recorded duration.                   | kept; sent to Velt                                                                                                                                                            |
| `transcription`          | `Transcription`                                           | *(absent when resolver active)*                                          | Transcript of the media.             | **never sent to Velt** when resolver active → see [Your DB](#3b--stored-in-your-db-partialrecorderannotation). Present in Velt's DB **only** when no recorder resolver is set |
| `waveformData`           | `number[]`                                                | `[0.1, 0.4, 0.2, 0.9]`                                                   | Audio waveform.                      | kept; sent to Velt                                                                                                                                                            |
| `displayName`            | `string`                                                  | `"Sprint demo recording"`                                                | Display name.                        | kept; sent to Velt (top level)                                                                                                                                                |
| `metadata`               | `RecorderMetadata` (extends `BaseMetadata`; `[key]: any`) | `{ apiKey: 'AbC', documentId: 'doc-1', organizationId: 'org-1' }`        | Routing metadata.                    | **full** kept; `getClientMetadata` copy sent to your DB                                                                                                                       |
| `latestVersion`          | `number`                                                  | `2`                                                                      | Current edit version number.         | kept                                                                                                                                                                          |
| `recordingEditVersions`  | `{ [version: number]: RecorderAnnotationEditVersion }`    | `{ 1: { recordedTime: {...}, waveformData: [...], displayName: 'v1' } }` | Per-edit version history.            | per-version PII withheld (`from`→`{userId}`, `attachment`→`null`, `attachments`→stubs, `transcription` never sent); non-PII per-version fields kept                           |
| `isRecorderResolverUsed` | `boolean`                                                 | `true`                                                                   | Resolver-used flag.                  | flag; set `true` on strip                                                                                                                                                     |
| `isUrlAvailable`         | `boolean`                                                 | `false`                                                                  | Real URL (vs. local blob) ready yet. | kept **and** copied to your DB                                                                                                                                                |

### 3.B — Stored in your DB (`PartialRecorderAnnotation`)

| Field                   | Type                                                   | Example                                                                                                                                         | Description                                                             | Notes                                                                                  |
| ----------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `annotationId`          | `string`                                               | `"rec_a1b2c3d4"`                                                                                                                                | Join key.                                                               | always present                                                                         |
| `metadata`              | `BaseMetadata`                                         | `{ apiKey: 'AbC', documentId: 'doc-1', organizationId: 'org-1', folderId: 'f-1' }`                                                              | Client-facing metadata.                                                 | `getClientMetadata(data.metadata)`                                                     |
| `from`                  | `User` (full)                                          | `{ userId: 'user-123', name: 'Jane Doe', email: 'jane@acme.com', photoUrl: 'https://…' }`                                                       | Full creator user object (PII).                                         | deep-cloned full object                                                                |
| `transcription`         | `Transcription`                                        | `{ from: { userId: 'user-123' }, transcriptedText: 'Hello team…', vttUrl: 'https://…/cap.vtt' }`                                                | Full transcript (PII).                                                  | **never sent to Velt** — see [Transcription](#transcription-your-db-recorder-payload)  |
| `attachment`            | `Attachment \| null`                                   | `{ attachmentId: 'att_1', name: 'recording.webm', url: 'https://storage/…' }`                                                                   | Deprecated single attachment (with URL).                                | **@deprecated**; sent when defined; value never sent to Velt (written as `null` there) |
| `attachments`           | `Attachment[]`                                         | `[{ attachmentId: 'att_1', name: 'recording.webm', url: 'https://storage/…' }]`                                                                 | Full attachment list incl. media URLs (PII).                            | sent only when length > 0; reduced to stubs in Velt's DB                               |
| `recordingEditVersions` | `Record<number, PartialRecorderAnnotationEditVersion>` | `{ 1: { from: { userId: 'user-123' }, attachments: [{ attachmentId: 'att_1', url: 'https://…' }], transcription: { transcriptedText: '…' } } }` | Per-version PII (`from`, `attachment`, `attachments`, `transcription`). | only versions with ≥1 PII field                                                        |
| `isUrlAvailable`        | `boolean`                                              | `false`                                                                                                                                         | Real URL ready yet.                                                     | copied (also kept in Velt's DB)                                                        |
| `[additionalFields]`    | `any`                                                  | `{ customTag: 'launch-demo' }`                                                                                                                  | Your replicated custom fields.                                          | per `config.additionalFields`; kept in Velt's DB too                                   |

### 3.C — Recorder strip rules

* Never sent to Velt: `transcription` (entire object → your DB), the value of `attachment`
  (written as `null`), and the urls inside `attachments` (Velt keeps only `{ attachmentId, name }`
  stubs). `from` is reduced to `{ userId }`;
  `isRecorderResolverUsed` set `true`.
* `recordingEditVersions` per-version PII is withheld the same way; non-PII per-version fields
  (`recordedTime`, `waveformData`, `displayName`, `boundedTrimRanges`, `boundedScaleRanges`) are sent to Velt.
* Top-level `displayName` / `waveformData` / `recordedTime` are sent to Velt (not part of the your-DB payload).
* **Recording files stay on Velt** unless you also set `recorder.storage` (the bring-your-own storage
  scope). The recorder *metadata* resolver and the recording *file* storage are independent toggles.

***

## 4. Notifications (`notification`)

Model: `Notification` / `NotificationRawData`. PII payload: `PartialNotification`. **Custom
notifications only** (`notificationSource === 'custom'`); standard comment/recording notifications
resolve through their own feature providers. This is **read-only enrichment** — there is **no
write-side strip** and **no `save`**.

### 4.A — Stored in Velt's DB (structural notification envelope)

| Field                        | Type                                                                                                                                                                   | Example                                                                                       | Description                                         | Notes                                                                    |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------ |
| `id`                         | `string`                                                                                                                                                               | `"notif_a1b2c3d4"`                                                                            | Notification id / join key.                         | required; the resolver `get()` lookup key                                |
| `notificationSource`         | `string`                                                                                                                                                               | `"custom"`                                                                                    | Source discriminator.                               | resolver applies only when `'custom'`                                    |
| `actionType`                 | `string`                                                                                                                                                               | `"created"`                                                                                   | Action kind.                                        | structural                                                               |
| `actionUser`                 | `User` → `{ userId }`                                                                                                                                                  | `{ userId: 'user_42' }`                                                                       | Who triggered it.                                   | reduced when `user` provider active; full user object never sent to Velt |
| `timestamp`                  | `number`                                                                                                                                                               | `1717689600000`                                                                               | Created time (epoch ms).                            | ordering key                                                             |
| `targetAnnotationId`         | `string`                                                                                                                                                               | `"annotation_99"`                                                                             | Comment annotation it points at.                    | structural; underlying comment PII resolves via the comment resolver     |
| `metadata`                   | `NotificationMetadata` `{ apiKey?; organizationId?; clientOrganizationId?; documentId?; clientDocumentId?: string\|number; locationId?: number; location?: Location }` | `{ apiKey: 'key_xyz', organizationId: 'org_1', documentId: 'doc_5', locationId: 1234567890 }` | Routing identifiers.                                | identifiers only (no display PII)                                        |
| `notifyUsers`                | `{ [emailHash]: boolean }`                                                                                                                                             | `{ "a1b2c3": true }`                                                                          | Recipients by **hashed** email.                     | keys are hashes, not raw emails                                          |
| `notifyUsersByUserId`        | `{ [userIdHash]: boolean }`                                                                                                                                            | `{ "f9e8d7": true }`                                                                          | Recipients by **hashed** userId.                    | keys are hashes                                                          |
| `isCommentResolverUsed`      | `boolean`                                                                                                                                                              | `true`                                                                                        | Comment resolver supplied the comment text.         | flag                                                                     |
| `isNotificationResolverUsed` | `boolean`                                                                                                                                                              | `true`                                                                                        | Notification resolver enriched this record on read. | flag; auto-set by merge                                                  |

### 4.B — Stored in your DB (`PartialNotification`)

| Field                                | Type                                                                              | Example                                                                              | Description                                 | Notes                                             |
| ------------------------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------- | ------------------------------------------------- |
| `notificationId`                     | `string`                                                                          | `"notif_a1b2c3d4"`                                                                   | Join key.                                   | required; the only non-PII field                  |
| `displayHeadlineMessageTemplate`     | `string`                                                                          | `"{{actionUser}} mentioned {{recipientUser}}"`                                       | Headline template (PII copy).               | never sent to Velt (custom)                       |
| `displayHeadlineMessageTemplateData` | `{ actionUser?: User; recipientUser?: User; actionMessage?: string; [key]: any }` | `{ actionUser: { userId: 'user_42', name: 'Ada' }, actionMessage: 'mentioned you' }` | Headline template data (full user objects). | PII; your DB only                                 |
| `displayBodyMessage`                 | `string`                                                                          | `"Take a look when you get a chance."`                                               | Rendered body text.                         | PII; your DB only                                 |
| `displayBodyMessageTemplate`         | `string`                                                                          | `"{{users}} replied to your comment"`                                                | Body template.                              | PII; your DB only                                 |
| `displayBodyMessageTemplateData`     | `{ [key]: any }`                                                                  | `{ users: [{ userId: 'user_7', name: 'Linus' }] }`                                   | Body template data.                         | PII; your DB only                                 |
| `notificationSourceData`             | `any`                                                                             | `{ comment: { text: 'Looks great!' } }`                                              | Your custom source payload.                 | PII; your DB only                                 |
| `[key: string]`                      | `any`                                                                             | `{ customBadge: 'urgent' }`                                                          | Any extra custom fields you return.         | merged verbatim on read (except `notificationId`) |

### 4.C — Notification strip rules

* **No write-side strip / no `save`.** For custom notifications the `PartialNotification` PII is never
  sent to Velt at all; it lives in your backend and is fetched on read, then merged into both the
  `notification` and its raw form, setting `isNotificationResolverUsed = true`.
* The only write-side reduction is `actionUser → { userId }` (when `user` provider active).
* `isUnread`, `forYou`, and the rendered `displayHeadlineMessage` are **computed on the client** (from
  `notificationViews` / `notifyUsers*` / the templates) and stored in neither DB — omitted from the
  tables above.
* Resolution order is **notification → user → comment** (notification PII fills userIds that the user
  resolver then enriches).
* **Delete** calls your provider with `{ notificationId, organizationId }`.

***

## 5. Activity (`activity`)

Model: `ActivityRecord`. PII payload: `PartialActivityRecord`. **Append-only** — there is **no
`delete`**. Activity records can carry PII from several features at once.

### 5.A — Stored in Velt's DB (`ActivityRecord`)

| Field                        | Type                                                                                                                                     | Example                                                                   | Description                                                      | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                         | `string`                                                                                                                                 | `"act_8f3kd92mz"`                                                         | Unique activity id / join key.                                   | auto-generated                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `featureType`                | `ActivityFeatureType` (`'comment'\|'reaction'\|'recorder'\|'crdt'\|'custom'`)                                                            | `"comment"`                                                               | Feature that generated the activity.                             | structural                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `actionType`                 | `string`                                                                                                                                 | `"comment_annotation.status_change"`                                      | Action (`entity_type.action`).                                   | structural                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `eventType`                  | `string`                                                                                                                                 | `"STATUS_CHANGED"`                                                        | Underlying status/event.                                         | optional                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `actionUser`                 | `User` → `{ userId }`                                                                                                                    | `{ userId: 'user_123' }`                                                  | Who performed the action.                                        | reduced when `user` provider active; full user object never sent to Velt                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `timestamp`                  | `number`                                                                                                                                 | `1717804800000`                                                           | Server timestamp (epoch ms).                                     | auto-generated                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `metadata`                   | `ActivityMetadata` (extends `BaseMetadata`; `[key]: any`)                                                                                | `{ organizationId: 'org_1', documentId: 'doc_42', folderId: 'folder_9' }` | Denormalized routing IDs.                                        | **full** kept; `getClientMetadata` copy sent to your DB                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `targetEntityId`             | `string`                                                                                                                                 | `"annotation_55"`                                                         | Parent entity id (annotation/recording/…).                       | resolver lookup key                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `targetSubEntityId`          | `string \| null`                                                                                                                         | `"comment_88"`                                                            | Sub-entity id (commentId); null for entity-level.                | optional                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `changes`                    | `ActivityChanges` `{ [key]: { from?; to? } }`                                                                                            | `{ status: { from: { id: 'open' }, to: { id: 'closed' } } }`              | Linear-style from/to pairs.                                      | user objects → `{ userId }`; `commentText` entry **never sent to Velt** (→ your DB) when **activity** resolver active                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `entityData`                 | `TEntity` (e.g. `CommentAnnotation`/`ReactionAnnotation`/`RecorderAnnotation`) — built-in feature types; or arbitrary shape for `custom` | `{ annotationId: 'annotation_55', status: { id: 'open' } }`               | Parent-entity snapshot.                                          | **Built-in feature types** (`comment`/`reaction`/`recorder`): object is kept, only specific PII *fields inside it* are stripped — comment `commentText`/`commentHtml`/`attachments` (name/url)/`from`/`to`/`taggedUserContacts`; reaction `icon`/`from`/`metadata` (only when reaction resolver also active); recorder `from`/`transcription`/`attachment`/`attachments` (only when recorder resolver also active). Structural IDs are preserved so reads can rematch. User objects → `{ userId }`. **Custom** (`featureType === 'custom'`): no automatic field-level stripping — `entityData` is only removed if you list it in `fieldsToRemove` (then the whole field is moved to your DB). |
| `entityTargetData`           | `TTarget` (e.g. `Comment`) — built-in; or arbitrary for `custom`                                                                         | `{ commentId: 'comment_88', from: { userId: 'user_123' } }`               | Sub-entity snapshot.                                             | **Built-in**: same partial-strip model as `entityData` (e.g. comment fields `commentText`/`commentHtml`/`attachments`/`from`/`to`/`taggedUserContacts` removed; structural ids kept). **Custom**: untouched unless listed in `fieldsToRemove` (then removed wholesale).                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `displayMessageTemplate`     | `string`                                                                                                                                 | `"{{actionUser.name}} shipped {{releaseName}}"`                           | Template — **custom activities only**.                           | kept (unless you list it in `fieldsToRemove`, in which case it's moved wholesale to your DB)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `displayMessageTemplateData` | `Record<string, unknown>`                                                                                                                | `{ actionUser: { userId: 'user_123' }, releaseName: 'v2.1' }`             | Template values — **custom only**.                               | user objects → `{ userId }`; non-user values kept                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `actionIcon`                 | `string`                                                                                                                                 | `"https://cdn.example.com/icons/status.svg"`                              | Display icon (typically custom).                                 | optional                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `immutable`                  | `boolean`                                                                                                                                | `true`                                                                    | If true, cannot be updated/deleted via REST.                     | optional flag                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `isActivityResolverUsed`     | `boolean`                                                                                                                                | `true`                                                                    | Resolver withheld PII from this record (PII never sent to Velt). | flag; set `true` only when PII was extracted                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |

### 5.B — Stored in your DB (`PartialActivityRecord`)

| Field                        | Type                                                                                    | Example                                                           | Description                                     | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ---------------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                         | `string`                                                                                | `"act_8f3kd92mz"`                                                 | Correlation key.                                | required; same as `ActivityRecord.id`                                                                                                                                                                                                                                                                                                                                                                                                        |
| `metadata`                   | `BaseMetadata`                                                                          | `{ organizationId: 'org_1', documentId: 'doc_42' }`               | Client-facing metadata copy.                    | `getClientMetadata` subset                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `changes`                    | `ActivityChanges`                                                                       | `{ commentText: { from: 'old text', to: 'new text' } }`           | PII change pairs.                               | for **comment** activities, only `{ commentText }`; sent only when the **activity** resolver is active (if only the comment resolver is registered, `changes['commentText']` is preserved on the Velt side to avoid unrestorable loss)                                                                                                                                                                                                       |
| `entityData`                 | `unknown` (`PartialReaction…` / `PartialRecorder…` for built-ins; arbitrary for custom) | `{ annotationId: 'rec_77', transcription: {…}, attachment: {…} }` | Feature-specific entity PII.                    | **Reaction**: only when activity **and** reaction resolvers are both active (carries `icon`/`from`/`metadata`). **Recorder**: only when activity **and** recorder resolvers are both active (carries `from`/`transcription`/`attachment`/`attachments`). **Comment**: handled by the comment resolver's own store — not duplicated here. **Custom**: only if you list `entityData` in `fieldsToRemove` (then the entire field is relocated). |
| `entityTargetData`           | `unknown`                                                                               | `{ commentId: 'comment_88', commentText: 'Looks good' }`          | Sub-entity PII snapshot.                        | Built-in feature types: per-feature PII fields only. Custom: only if you list `entityTargetData` in `fieldsToRemove` (wholesale relocation). Merged back on read by wholesale replacement.                                                                                                                                                                                                                                                   |
| `displayMessageTemplateData` | `Record<string, unknown>`                                                               | `{ releaseName: 'v2.1', secretNote: 'internal only' }`            | Custom-activity template values.                | only when listed in `fieldsToRemove` (`custom` only); users → `{ userId }` Velt-side                                                                                                                                                                                                                                                                                                                                                         |
| `[key: string]`              | `any`                                                                                   | `{ customField: { ssn: '***' } }`                                 | Any top-level field listed in `fieldsToRemove`. | **`featureType === 'custom'` only**; the entire top-level key is moved wholesale to your DB and deleted from the Velt record                                                                                                                                                                                                                                                                                                                 |

### 5.C — Activity strip rules

The activity resolver runs a **strip-on-write / resolve-on-read** cycle: PII is pulled out of the
`ActivityRecord` before it ships to Velt and is merged back from your DB on read. *How much* is
removed depends on `featureType`.

**Built-in `featureType` (`comment` / `reaction` / `recorder`) — partial strip**

* `entityData` and `entityTargetData` objects are **kept** on the Velt record; only specific PII
  *fields inside them* are removed. Structural identifiers (annotation/comment/target IDs) are
  deliberately preserved so the read path can match resolved data back.
* Per feature (matches the per-feature partial tables above):
  * **comment** — removes `commentText`, `commentHtml`, `attachments` (name/url), `from`, `to`,
    `taggedUserContacts` from both `entityData` and `entityTargetData`.
  * **reaction** — removes `icon`, `from`, `metadata` *only when both the activity resolver and the
    reaction resolver are active*; otherwise only user objects are reduced to `{ userId }` and the
    rest of the structure survives.
  * **recorder** — removes `from`, `transcription`, `attachment`, `attachments` *only when both the
    activity resolver and the recorder resolver are active*; otherwise only user objects are reduced
    to `{ userId }`.
* `changes['commentText']` is removed from the Velt record (→ your DB) **only** when the activity
  resolver is active; if only the comment resolver is registered it is preserved on the Velt side to
  avoid unrestorable loss.
* Comment `entityData` / `entityTargetData` PII is handled by the comment resolver's own store, not
  duplicated into the activity resolver payload.

**`featureType === 'custom'` — wholesale removal by config**

* There is no feature-aware partial stripping for custom activities. The only fields removed are the
  **top-level keys you list in `fieldsToRemove`**: each listed key is moved wholesale to your DB and
  `delete`d from the Velt record. On read it's restored by wholesale replacement.
* This means if you list `entityData` or `entityTargetData` in `fieldsToRemove`, the *entire field*
  disappears from the Velt record (not field-by-field). If you don't list them, they're left
  untouched — there is no automatic stripping for custom.
* `fieldsToRemove` is ignored for built-in feature types.

**Universal**

* `displayMessage` is always recomputed on the client (from the template + values) and stored in
  neither DB.
* `actionUser` and any user objects in `changes` and `displayMessageTemplateData` are reduced to
  `{ userId }` whenever a `user` resolver is active.
* Append-only: no delete.

***

## 6. Attachments (`attachment`)

Stored model: `Attachment` — embedded on comments (`comments[].attachments`) and recordings
(`attachments`). Upload payload: `ResolverAttachment` / `AttachmentResolverMetadata`. **There is no
`Partial<X>` strip and no `get`** — attachments are *binary files*. When you register an `attachment`
(or `recorder.storage`) provider, Velt hands you the raw `File`; you return `{ url }`.

### 6.A — Stored in Velt's DB (`Attachment` record — kept, minus the binary bytes)

| Field                      | Type                                          | Example                                                    | Description                                   | Notes                                                 |
| -------------------------- | --------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------- | ----------------------------------------------------- |
| `attachmentId`             | `number`                                      | `742193`                                                   | Unique attachment id (random 6-digit).        | auto-generated; primary key                           |
| `isAttachmentResolverUsed` | `boolean`                                     | `true`                                                     | Self-hosted storage used for this attachment. | flag                                                  |
| `name`                     | `string`                                      | `"design-spec.pdf"`                                        | Original file name.                           | kept; also sent (reduced) to your storage             |
| `size`                     | `number`                                      | `204800`                                                   | File size (bytes).                            | kept                                                  |
| `type`                     | `RecorderFileFormat` (`'mp3'\|'mp4'\|'webm'`) | `"mp4"`                                                    | File format.                                  | kept (string union, not enum)                         |
| `url`                      | `string`                                      | `"https://customer-cdn.example.com/files/design-spec.pdf"` | Download URL.                                 | with self-hosting = the URL **your** storage returned |
| `thumbnail`                | `string`                                      | `"data:image/png;base64,iVBORw0KGgo…"`                     | Base64 thumbnail.                             | kept                                                  |
| `thumbnailWithPlayIconUrl` | `string`                                      | `"https://cdn/…/thumb-play.png"`                           | Thumbnail with play overlay (video).          | kept                                                  |
| `metadata`                 | `any`                                         | `{ width: 1920, height: 1080, duration: 12.4 }`            | Arbitrary attachment metadata.                | kept (distinct from `AttachmentResolverMetadata`)     |
| `mimeType`                 | `any`                                         | `"application/pdf"`                                        | MIME type.                                    | kept; also sent (reduced) to your storage             |
| `previewImages`            | `string[]`                                    | `["data:image/png;base64,…","data:image/png;base64,…"]`    | Preview images.                               | kept                                                  |

> The **binary bytes are never sent to Velt** when you self-host storage; they go straight to your
> bucket and only the returned `url` (plus the structural fields above) is stored on the `Attachment`
> record.

### 6.B — Handed to your storage provider (upload payload) / returned

| Field                          | Type                         | Example                                                                                                                                            | Description                                  | Notes                                                                       |
| ------------------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------- |
| `file`                         | `File`                       | `File { name: 'mockup.png', size: 23456, type: 'image/png' }`                                                                                      | Raw binary content.                          | multipart `file` part (URL mode) or `provider.save` arg; never sent to Velt |
| `attachment.attachmentId`      | `number`                     | `742199`                                                                                                                                           | Attachment id (inside `request.attachment`). | required                                                                    |
| `attachment.name`              | `string`                     | `"mockup.png"`                                                                                                                                     | File name.                                   | optional                                                                    |
| `attachment.mimeType`          | `string`                     | `"image/png"`                                                                                                                                      | MIME type.                                   | optional                                                                    |
| `metadata`                     | `AttachmentResolverMetadata` | `{ organizationId: 'org_xyz789', documentId: 'doc_a1b2c3', folderId: null, attachmentId: 742199, commentAnnotationId: 'ann_55', apiKey: 'AbCd…' }` | Routing context.                             | see sub-fields below                                                        |
| `metadata.organizationId`      | `string \| null`             | `"org_xyz789"`                                                                                                                                     | Org scope.                                   | nullable                                                                    |
| `metadata.documentId`          | `string \| null`             | `"doc_a1b2c3"`                                                                                                                                     | Document scope.                              | nullable                                                                    |
| `metadata.folderId`            | `string \| null`             | `"folder_q1reports"`                                                                                                                               | Folder scope.                                | optional + nullable; on delete only when truthy                             |
| `metadata.attachmentId`        | `number \| null`             | `742199`                                                                                                                                           | Mirror of top-level id.                      | nullable                                                                    |
| `metadata.commentAnnotationId` | `string \| null`             | `"ann_55"`                                                                                                                                         | Owning comment annotation.                   | nullable; dropped on delete                                                 |
| `metadata.apiKey`              | `string \| null`             | `"AbCdEf123456PublicApiKey"`                                                                                                                       | Velt public API key.                         | nullable                                                                    |
| `event`                        | `ResolverActions`            | `"ATTACHMENT_SAVE"`                                                                                                                                | Why the call fired.                          | optional; delete uses `ATTACHMENT_DELETE`                                   |
| **(return)** `url`             | `string`                     | `"https://cdn.customer.com/velt/742199/mockup.png"`                                                                                                | URL of the stored file you return.           | `SaveAttachmentResolverData.url`; persisted back onto `Attachment.url`      |

### 6.C — Attachment strip rules

* No annotation-style `Partial<X>`. The JSON `request` is exactly
  `{ attachment: { attachmentId, name, mimeType }, metadata, event }`; the `File` is destructured out
  and sent as binary — straight to your storage, never to Velt.
* **Delete** sends `{ attachmentId, metadata: { apiKey, documentId, organizationId, folderId? }, event }`.
* Comment attachments (`DataProviders.attachment`) and recording files
  (`DataProviders.recorder.storage`) are **independent** storage scopes — omit either to keep that
  scope on Velt-managed storage.

***

## 7. Shared building blocks

These nested objects are embedded inside the feature records above and are kept verbatim in Velt's DB
(unless a sub-field is noted as never sent to Velt). The `metadata` envelope is the one whose
*your-DB* copy is reduced via `getClientMetadata()`.

### BaseMetadata

The `metadata` field present on every feature payload. Full version kept in Velt's DB; client-facing
subset sent to your DB.

| Field                  | Type               | Example                         | Description                            | Notes                                                                  |
| ---------------------- | ------------------ | ------------------------------- | -------------------------------------- | ---------------------------------------------------------------------- |
| `apiKey`               | `string`           | `"AbCdEf123456PublicApiKey"`    | Velt public API key.                   | optional                                                               |
| `documentId`           | `string`           | `"doc_a1b2c3"`                  | Velt-internal hashed document id.      | auto-derived from `clientDocumentId`                                   |
| `clientDocumentId`     | `string`           | `"my-page-123"`                 | The raw documentId you passed.         | dropped from the client-facing copy (after mapping → `documentId`)     |
| `organizationId`       | `string`           | `"org_xyz789"`                  | Velt-internal hashed org id.           | auto-derived from `clientOrganizationId`                               |
| `clientOrganizationId` | `string`           | `"acme-corp"`                   | The raw organizationId you passed.     | dropped from the client-facing copy (after mapping → `organizationId`) |
| `folderId`             | `string`           | `"folder_q1reports"`            | Velt-internal hashed folder id.        | auto-derived from `veltFolderId`                                       |
| `veltFolderId`         | `string`           | `"vfolder_001"`                 | Velt-assigned folder id.               | Velt-internal; not included in the client-facing copy sent to your DB  |
| `documentMetadata`     | `DocumentMetadata` | `{ documentName: 'Q1 Report' }` | Your descriptive document metadata.    | optional                                                               |
| `sdkVersion`           | `string \| null`   | `"5.0.2-beta.34"`               | SDK version that produced the payload. | auto-generated                                                         |

> **Client-facing transform (`getClientMetadata`):** `clientDocumentId → documentId`,
> `clientOrganizationId → organizationId`; `veltFolderId` / `parentVeltFolderId` / `pageInfo`
> dropped. `folderId` included only when truthy.

### Location & Version

| Field           | Type                                     | Example                         | Description                     | Notes                       |
| --------------- | ---------------------------------------- | ------------------------------- | ------------------------------- | --------------------------- |
| `id`            | `string`                                 | `"page-2"`                      | Unique location id.             | optional                    |
| `locationName`  | `string`                                 | `"Page 2 - Section A"`          | Human-readable location name.   | optional                    |
| `version`       | `Version` `{ id: string; name: string }` | `{ id: 'v1', name: 'Draft 1' }` | Version object.                 | optional                    |
| `[key: string]` | `any`                                    | `{ page: 3, tab: 'design' }`    | Arbitrary custom location keys. | open index signature — kept |

### PageInfo

| Field         | Type          | Example                                                             | Description                        | Notes                  |
| ------------- | ------------- | ------------------------------------------------------------------- | ---------------------------------- | ---------------------- |
| `url`         | `string`      | `"https://app.example.com/editor?doc=123"`                          | Full page URL.                     | optional               |
| `path`        | `string`      | `"/editor"`                                                         | Path (excl. base URL).             | optional               |
| `queryParams` | `string`      | `"doc=123&tab=design"`                                              | Query string.                      | optional               |
| `baseUrl`     | `string`      | `"https://app.example.com"`                                         | Domain.                            | optional               |
| `title`       | `string`      | `"Editor - Acme App"`                                               | Page title.                        | optional               |
| `arrowUrl`    | `string`      | `"https://app.example.com/editor#arrow-1"`                          | Arrow-annotation reference URL.    | optional               |
| `areaUrl`     | `string`      | `"https://app.example.com/editor#area-1"`                           | Area-annotation reference URL.     | optional               |
| `commentUrl`  | `string`      | `"https://app.example.com/editor#comment-1"`                        | Comment-annotation reference URL.  | optional               |
| `tagUrl`      | `string`      | `"https://app.example.com/editor#tag-1"`                            | Tag-annotation reference URL.      | optional               |
| `recorderUrl` | `string`      | `"https://app.example.com/editor#rec-1"`                            | Recorder-annotation reference URL. | optional               |
| `screenWidth` | `number`      | `1440`                                                              | Author screen width.               | auto-generated         |
| `deviceInfo`  | `IDeviceInfo` | `{ browserName: 'Chrome', osName: 'macOS', deviceType: 'desktop' }` | Device/browser info.               | auto-generated default |

### TargetElement

| Field            | Type                   | Example                                             | Description                                | Notes       |
| ---------------- | ---------------------- | --------------------------------------------------- | ------------------------------------------ | ----------- |
| `xpath`          | `string`               | `"/html/body/div[1]/button"`                        | XPath of the target element.               | optional    |
| `fXpath`         | `string`               | `"/html[1]/body[1]/div[1]/button[1]"`               | Full XPath.                                | optional    |
| `cfXpath`        | `string`               | `"/html[1]/body[1]/div[1]@class=toolbar/button[1]"` | Full XPath with class names.               | optional    |
| `topPercentage`  | `number`               | `42.5`                                              | Relative top position on the element (%).  | default `0` |
| `leftPercentage` | `number`               | `63.1`                                              | Relative left position on the element (%). | default `0` |
| `anchor`         | `AnchorRecord \| null` | `{ … robust anchor descriptor … }`                  | Robust anchor descriptor.                  | optional    |

### TargetTextRange

| Field                            | Type           | Example                                        | Description                             | Notes                                                                         |
| -------------------------------- | -------------- | ---------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------- |
| `commonAncestorContainer`        | `string`       | `"/html/body/div[2]/p[1]"`                     | XPath of the common ancestor container. | optional                                                                      |
| `commonAncestorContainerFXpath`  | `string`       | `"/html[1]/body[1]/div[2]/p[1]"`               | Full XPath of the container.            | optional                                                                      |
| `commonAncestorContainerCFXpath` | `string`       | `"/html[1]/body[1]/div[2]@class=content/p[1]"` | Full XPath with class names.            | optional                                                                      |
| `commonAncestorContainerAnchor`  | `AnchorRecord` | `{ … robust anchor descriptor … }`             | Robust anchor descriptor.               | optional                                                                      |
| `text`                           | `string`       | `"the quick brown fox"`                        | The selected text snippet.              | **never sent to Velt** for comments (→ your DB) — the only sub-field withheld |
| `occurrence`                     | `number`       | `1`                                            | Which occurrence of the text.           | default `1`                                                                   |

### CursorPosition

| Field              | Type     | Example | Description                      | Notes                         |
| ------------------ | -------- | ------- | -------------------------------- | ----------------------------- |
| `top`              | `number` | `148`   | Top position of the pin/cursor.  | default `0`                   |
| `left`             | `number` | `320`   | Left position of the pin/cursor. | default `0`                   |
| `parentScaleX`     | `number` | `1`     | X-scale of the parent element.   | optional (transform handling) |
| `parentScaleY`     | `number` | `1`     | Y-scale of the parent element.   | optional (transform handling) |
| `transformContext` | `any`    | `{ … }` | Transform context info.          | optional                      |

### CommentAnnotationViews

| Field      | Type                                                      | Example                                                         | Description                          | Notes    |
| ---------- | --------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------ | -------- |
| `views`    | `{ [userId]: { timestamp } }`                             | `{ 'u_1': { timestamp: 1717800000 } }`                          | Per-user annotation view timestamps. | kept     |
| `comments` | `{ [commentId]: { views: { [userId]: { timestamp } } } }` | `{ '482910': { views: { 'u_1': { timestamp: 1717800100 } } } }` | Per-comment view timestamps.         | kept     |
| `metadata` | `BaseMetadata`                                            | `{ documentId: 'doc_1' }`                                       | Routing metadata.                    | optional |

### Reaction (per element)

The element shape of `ReactionAnnotation.reactions[]`, kept in Velt's DB.

| Field         | Type                  | Example                    | Description                                 | Notes                                                                     |
| ------------- | --------------------- | -------------------------- | ------------------------------------------- | ------------------------------------------------------------------------- |
| `variant`     | `string`              | `"1f44d"`                  | Emoji variant/identifier for this reaction. | optional; kept; sent to Velt                                              |
| `from`        | `User` → `{ userId }` | `{ userId: 'u_1' }`        | Who added this individual reaction.         | required on element; reduced when `user` provider active (Velt-side only) |
| `lastUpdated` | `Date`                | `2026-06-08T10:32:00.000Z` | When this reaction was added.               | auto-generated                                                            |

### Comment (per-thread comment)

The element shape of `CommentAnnotation.comments[]`, kept in Velt's DB with per-comment PII never sent to Velt.

| Field                     | Type                                   | Example                                   | Description                          | Notes                                                                                                                                                        |
| ------------------------- | -------------------------------------- | ----------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `commentId`               | `number`                               | `482910`                                  | Per-comment id.                      | auto-generated                                                                                                                                               |
| `context`                 | `any`                                  | `{ … }`                                   | Per-comment context.                 | kept                                                                                                                                                         |
| `type`                    | `'text' \| 'voice'`                    | `"text"`                                  | Comment content type.                | default `'text'`                                                                                                                                             |
| `commentText`             | `string`                               | `"Looks good 👍"`                         | Plain-text body.                     | **never sent to Velt** (→ your DB)                                                                                                                           |
| `isCommentTextAvailable`  | `boolean`                              | `true`                                    | Whether comment text exists.         | kept                                                                                                                                                         |
| `isCommentResolverUsed`   | `boolean`                              | `true`                                    | Comment resolver used (per comment). | flag                                                                                                                                                         |
| `commentHtml`             | `string`                               | `"<p>Looks good 👍</p>"`                  | Rich-text body.                      | **never sent to Velt** (→ your DB)                                                                                                                           |
| `replaceContentHtml`      | `string`                               | `"<p>fixed</p>"`                          | HTML to replace on accept.           | kept                                                                                                                                                         |
| `replaceContentText`      | `string`                               | `"fixed"`                                 | Text to replace on accept.           | kept                                                                                                                                                         |
| `commentVoiceUrl`         | `string`                               | `"https://…/voice.webm"`                  | Voice-comment URL.                   | kept                                                                                                                                                         |
| `from`                    | `User` → `{ userId }`                  | `{ userId: 'u_1' }`                       | Comment author.                      | reduced when `user` provider active                                                                                                                          |
| `to`                      | `User[]` → `{ userId }[]`              | `[{ userId: 'u_2' }]`                     | @mentioned users.                    | reduced when `user` provider active                                                                                                                          |
| `lastUpdated`             | `Date`                                 | `2026-06-08T10:32:00Z`                    | Last-updated time.                   | auto-generated                                                                                                                                               |
| `createdAt`               | `any`                                  | `{ seconds: 1717800000 }`                 | Created time.                        | auto-generated                                                                                                                                               |
| `status`                  | `'added' \| 'updated'`                 | `"added"`                                 | Comment status.                      | default `'added'`                                                                                                                                            |
| `attachments`             | `Attachment[]`                         | `[{ attachmentId: 1, name: 'spec.pdf' }]` | Attachments.                         | `name`/`url` never sent to Velt when `attachment` resolver active — see [Attachment](#6a--stored-in-velts-db-attachment-record--kept-minus-the-binary-bytes) |
| `recorders`               | `RecordedData[]`                       | `[…]`                                     | Embedded recorded data.              | kept                                                                                                                                                         |
| `reactionAnnotationIds`   | `string[]`                             | `["rXa92…"]`                              | Linked reaction-annotation ids.      | kept                                                                                                                                                         |
| `taggedUserContacts`      | `AutocompleteUserContactReplaceData[]` | `[{ userId: 'u_2', text: '@Jane' }]`      | Tagged contacts.                     | `contact` reduced to `{ userId }`; PII text → your DB                                                                                                        |
| `customList`              | `AutocompleteReplaceData[]`            | `[…]`                                     | Custom autocomplete replacements.    | kept                                                                                                                                                         |
| `toOrganizationUserGroup` | `AutocompleteGroupReplaceData[]`       | `[…]`                                     | Tagged org groups.                   | kept                                                                                                                                                         |
| `isDraft`                 | `boolean`                              | `false`                                   | Draft flag.                          | kept                                                                                                                                                         |
| `editedAt`                | `any`                                  | `{ seconds: 1717800500 }`                 | Edited time.                         | auto-generated                                                                                                                                               |
| `isEdited`                | `boolean`                              | `true`                                    | Edited flag.                         | kept                                                                                                                                                         |

### Transcription (your-DB recorder payload)

Full object sent to your DB (never sent to Velt) when the recorder resolver is active.

| Field                  | Type     | Example                       | Description                    | Notes          |
| ---------------------- | -------- | ----------------------------- | ------------------------------ | -------------- |
| `from`                 | `User`   | `{ userId: 'user-123' }`      | Who created the transcription. | required       |
| `lastUpdated`          | `number` | `1717804800000`               | Created/updated time.          | optional       |
| `srtBucketPath`        | `string` | `"orgs/o1/rec/cap.srt"`       | Storage path of the SRT file.  | optional       |
| `srtUrl`               | `string` | `"https://…/cap.srt"`         | SRT file URL.                  | optional       |
| `transcriptedText`     | `string` | `"Hello team, in this demo…"` | Transcribed text.              | optional (PII) |
| `transcriptionLatency` | `number` | `840`                         | Time to transcribe (ms).       | optional       |
| `vttBucketPath`        | `string` | `"orgs/o1/rec/cap.vtt"`       | Storage path of the VTT file.  | optional       |
| `vttUrl`               | `string` | `"https://…/cap.vtt"`         | VTT file URL.                  | optional       |

***

## 8. Summary matrix & field counts

### Where does each field-class live?

| Data                                                                         | Velt's DB                                    | Your DB / storage |
| ---------------------------------------------------------------------------- | -------------------------------------------- | ----------------- |
| Comment text / HTML                                                          | —                                            | ✅                 |
| Comment attachment file + name/url                                           | —                                            | ✅                 |
| Reaction `icon`                                                              | —                                            | ✅                 |
| Recording transcript / file                                                  | file stubs only                              | ✅                 |
| Custom-notification display/template PII                                     | —                                            | ✅                 |
| Activity PII (commentText changes, entity data, template data)               | —                                            | ✅                 |
| User profile (name, email, avatar)                                           | `{ userId }` only (when `user` provider set) | ✅                 |
| Your custom fields via `fieldsToRemove`                                      | —                                            | ✅                 |
| Your custom fields via `additionalFields`                                    | ✅                                            | ✅                 |
| Structural data (IDs, locations, status, targets, timestamps, flags)         | ✅                                            | —                 |
| Client-computed fields (`unread`, `isUnread`, `forYou`, `displayMessage`, …) | —                                            | —                 |

*Anything in the right-hand column with `—` under "Velt's DB" is **never sent to Velt** — it is
stripped on the frontend and goes only to your DB (or is recomputed on the client).*

### Field counts (persisted fields only)

| Feature       | Velt's DB                                                               | Your DB (`Partial<X>`)                                                                                                           | Strip model                                           |
| ------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| Comments      | 53 top-level (+ \~24-field `Comment` element)                           | `PartialCommentAnnotation` + nested `PartialComment`                                                                             | strip on the frontend, merge on read                  |
| Reactions     | 16 top-level (+ 3-field `Reaction` element)                             | `{ annotationId, metadata, icon, from? }`                                                                                        | strip `icon` only                                     |
| Recordings    | 35 (incl. reduced `from`/`attachments`; `transcription` → your DB only) | `PartialRecorderAnnotation` (+ per-version partials, `additionalFields`)                                                         | strip + reduce on the frontend; optional file storage |
| Notifications | 11 (structural + flags)                                                 | `PartialNotification` (6 PII + open index)                                                                                       | **read-only enrichment** (custom only)                |
| Activity      | 17                                                                      | `PartialActivityRecord` (`{ id, metadata?, changes?, entityData?, entityTargetData?, displayMessageTemplateData?, [key]: any }`) | strip on the frontend; append-only                    |
| Attachments   | `Attachment` (12 fields, minus bytes)                                   | `file` + `{ attachmentId, name, mimeType }` + `AttachmentResolverMetadata` + `event` → returns `{ url }`                         | binary file storage only                              |

### Resolver flags (set in Velt's DB; never sent from your side)

`isCommentResolverUsed` · `isReactionResolverUsed` · `isRecorderResolverUsed` ·
`isNotificationResolverUsed` · `isActivityResolverUsed` · `isAttachmentResolverUsed`
