> ## 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.

# Add Comment Annotations

Use this API to add comment annotations to a document within an organization.

* You can add comments on an elemement, text or page.
* You can provide HTML or text content.
* Additional filters can be applied using location IDs.

# Endpoint

`POST https://api.velt.dev/v2/commentannotations/add`

# Headers

<ParamField header="x-velt-api-key" type="string" required>
  Your API key.
</ParamField>

<ParamField header="x-velt-auth-token" type="string" required>
  Your [Auth Token](/security/auth-tokens).
</ParamField>

# Body

#### Params

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="organizationId" type="string" required>
      Organization ID
    </ParamField>

    <ParamField body="documentId" type="string" required>
      Document ID
    </ParamField>

    <ParamField body="createOrganization" type="boolean">
      If set to true, a new organization will be created (if it doesn't exist) before the annotation is added.
    </ParamField>

    <ParamField body="createDocument" type="boolean">
      If set to true, a new document will be created (if it doesn't exist) before the annotation is added.
    </ParamField>

    <ParamField body="verifyUserPermissions" type="boolean">
      When enabled, verifies the user has access to the document before creating comment annotations.
      This ensures annotations respect document access permissions configured via Access Control or Permission Provider.

      Default: `false`
    </ParamField>

    <ParamField body="commentAnnotations" type="object[]" required>
      <Expandable title="properties">
        <ParamField body="annotationId" type="string">
          Custom Annotation ID. If not provided, Velt will generate a unique ID for the annotation.
        </ParamField>

        <ParamField body="location" type="object">
          <Expandable title="properties">
            <ParamField body="id" type="string" required>
              Location ID
            </ParamField>

            <ParamField body="locationName" type="string">
              Location Name
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="targetElement" type="object">
          Target Element

          <Expandable title="properties">
            <ParamField body="elementId" type="string">
              Element DOM Id. When used with `targetText`, defines the text search boundaries for locating text comments within the specified element.
            </ParamField>

            <ParamField body="targetText" type="string">
              Target Text. Provide this if you want to add comment annotation on the provided text content.
            </ParamField>

            <ParamField body="occurrence" type="number">
              Occurrence. Provide this if you want to add comment annotation on a text content.
            </ParamField>

            <ParamField body="selectAllContent" type="boolean">
              Select All Content. Provide this if you want to select and add comment annotation on the entire text content of the target elementId.
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="commentData" type="object[]" required>
          Array of Comment Data

          <Expandable title="properties">
            <ParamField body="commentId" type="number">
              Custom Comment ID. If not provided, Velt will generate a unique ID for the comment.
            </ParamField>

            <ParamField body="commentText" type="string">
              Comment content in plain text string. To tag users, wrap their userId in double curly braces like `{{userId}}`. The frontend will replace this with the user's name.
            </ParamField>

            <ParamField body="commentHtml" type="string">
              Comment content in HTML string. To tag users, wrap their userId in double curly braces like `{{userId}}`. The frontend will replace this with the user's name.
            </ParamField>

            <ParamField body="context" type="object">
              Custom key/value metadata object. This is used to store any additional information about the comment.
            </ParamField>

            <ParamField body="isCommentResolverUsed" type="boolean">
              Use this for self-hosting comments data. Set this as true if you are comments resolver data provider in the SDK.
            </ParamField>

            <ParamField body="isCommentTextAvailable" type="boolean">
              Use this for self-hosting comments data. Set this as true if this comment will have text content. Sometimes, comments might only have attachments and in that case, set this as false.
            </ParamField>

            <ParamField body="triggerNotification" type="boolean">
              When enabled, adding comments via the REST API will trigger in-app notifications, email notifications to relevant users and also trigger webhooks matching the SDK’s native notification behavior. Default: false.
            </ParamField>

            <ParamField body="triggerActivities" type="boolean">
              When enabled, adding comments via the REST API will create activity log records matching the SDK’s native activity log behavior. Default: false.
            </ParamField>

            <ParamField body="from" type="User" required>
              User object from whom the comment is added
            </ParamField>

            <ParamField body="createdAt" type="number">
              Created At timestamp (in milliseconds since epoch).
            </ParamField>

            <ParamField body="lastUpdated" type="number">
              Last Updated timestamp (in milliseconds since epoch).
            </ParamField>

            <ParamField body="taggedUserContacts" type="object[]">
              Array of tagged user contacts. Use this field to provide information about users tagged in the comment. Each userId wrapped in `{{userId}}` in commentText or commentHtml should have a corresponding entry here so the frontend can replace it with the user's name.

              <Expandable title="properties">
                <ParamField body="userId" type="string" required>
                  User ID of the tagged user. This should match the userId used in `{{userId}}` syntax within commentText or commentHtml.
                </ParamField>

                <ParamField body="contact" type="object" required>
                  Contact information for the tagged user

                  <Expandable title="properties">
                    <ParamField body="userId" type="string" required>
                      User ID of the tagged user
                    </ParamField>

                    <ParamField body="name" type="string">
                      Name of the tagged user
                    </ParamField>

                    <ParamField body="email" type="string">
                      Email of the tagged user
                    </ParamField>
                  </Expandable>
                </ParamField>
              </Expandable>
            </ParamField>

            <ParamField body="agent" type="object">
              Marks this comment as agent-authored (an agent finding or an agent reply). V2 only. When set, the server stamps `sourceType: "agent"` on the comment. When set on the root comment (`commentData[0]`), the server also generates the annotation-level agent block and stamps `sourceType: "agent"` on the annotation. Discriminated on `agentSource`.

              <Expandable title="properties">
                <ParamField body="agentSource" type="string" required>
                  Origin of the agent. Must be one of: `velt` or `external`.
                </ParamField>

                <ParamField body="agentId" type="string">
                  Agent ID. Required when `agentSource` is `velt` (a built-in agent like `spell-check`, or a custom agent ID that is verified server-side). Optional and opaque (never validated) when `agentSource` is `external`.
                </ParamField>

                <ParamField body="executionId" type="string">
                  Execution / run ID for this agent invocation.
                </ParamField>

                <ParamField body="agentName" type="string">
                  Display name for the agent. **Required when `agentSource` is `external`** (the only source of truth for an external agent's name). Not used for `velt` agents (their name is resolved server-side).
                </ParamField>

                <ParamField body="url" type="string">
                  Page URL associated with the finding.
                </ParamField>

                <ParamField body="reason" type="object" required>
                  Finding details produced by the agent. Additional custom fields are preserved.

                  <Expandable title="properties">
                    <ParamField body="title" type="string" required>
                      Short finding title.
                    </ParamField>

                    <ParamField body="description" type="string" required>
                      Finding description.
                    </ParamField>

                    <ParamField body="severity" type="string" required>
                      Severity of the finding. Must be one of: `critical`, `high`, `medium`, `low`, or `info`.
                    </ParamField>

                    <ParamField body="findingId" type="string">
                      Unique identifier for the finding.
                    </ParamField>

                    <ParamField body="findingType" type="string">
                      Type of finding. Must be one of: `text`, `pin`, or `page`.
                    </ParamField>

                    <ParamField body="issueType" type="string">
                      Custom issue classification.
                    </ParamField>

                    <ParamField body="confidence" type="number">
                      Confidence score for the finding. Integer between 0 and 100.
                    </ParamField>

                    <ParamField body="suggestion" type="string">
                      Suggested change in plain text.
                    </ParamField>

                    <ParamField body="suggestedFix" type="string">
                      Suggested fix for the finding.
                    </ParamField>

                    <ParamField body="htmlSnippet" type="string">
                      Relevant HTML snippet for the finding.
                    </ParamField>

                    <ParamField body="htmlSelector" type="string">
                      CSS/HTML selector pointing to the finding location.
                    </ParamField>

                    <ParamField body="source" type="string">
                      Provenance of the rule that triggered the finding. Must be one of: `instructions` or `knowledge`.
                    </ParamField>

                    <ParamField body="knowledgeSection" type="string">
                      Knowledge section that triggered the finding.
                    </ParamField>
                  </Expandable>
                </ParamField>
              </Expandable>
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="status" type="Status">
          Status

          <Expandable title="properties">
            <ParamField body="id" type="string" required>
              Status ID
            </ParamField>

            <ParamField body="name" type="string" required>
              Status Name
            </ParamField>

            <ParamField body="type" type="string" required>
              Type. Must be one of: `default`, `ongoing`, or `terminal`.
            </ParamField>

            <ParamField body="color" type="string" required>
              Text and comment pin color
            </ParamField>

            <ParamField body="lightColor" type="string" required>
              Background color on the status indicator
            </ParamField>

            <ParamField body="svg" type="string">
              Raw SVG of the icon. Either `svg` or `iconUrl` is required.
            </ParamField>

            <ParamField body="iconUrl" type="string">
              Icon URL. Either `svg` or `iconUrl` is required.
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="assignedTo" type="User">
          User object to whom the comment is assigned
        </ParamField>

        <ParamField body="context" type="object">
          Custom key/value metadata object
        </ParamField>

        <ParamField body="createdAt" type="number">
          Created At timestamp (in milliseconds since epoch).
        </ParamField>

        <ParamField body="lastUpdated" type="number">
          Last Updated timestamp (in milliseconds since epoch).
        </ParamField>

        <ParamField body="priority" type="object">
          Priority

          <Expandable title="properties">
            <ParamField body="id" type="string" required>
              Priority ID
            </ParamField>

            <ParamField body="color" type="string" required>
              Priority Color
            </ParamField>

            <ParamField body="name" type="string" required>
              Priority Name
            </ParamField>

            <ParamField body="lightColor" type="string" required>
              Priority Light Color
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="type" type="string">
          Annotation kind. Must be one of: `comment` or `suggestion`. This is the source of truth for the suggestion classification; agent-produced findings should set `suggestion`. Defaults to `comment` when omitted.
        </ParamField>

        <ParamField body="commentType" type="string">
          Caller-driven comment classification. Must be one of: `manual` or `chart`. Defaults to `manual` when omitted.

          <Note>
            The legacy value `commentType: "suggestion"` is still accepted and preserved for backward compatibility, but it no longer drives the suggestion classification. Use the annotation-level `type: "suggestion"` field instead.
          </Note>
        </ParamField>

        <ParamField body="visibility" type="object">
          Controls who can see the annotation. Discriminated on `type`. When omitted, the annotation defaults to `public`.

          <Expandable title="properties">
            <ParamField body="type" type="string" required>
              Visibility type. Must be one of: `public`, `organizationPrivate`, or `restricted`.

              * `public`: Visible to everyone.
              * `organizationPrivate`: Visible only to users in the specified organization. Requires `organizationId`.
              * `restricted`: Visible only to the specified users (private comment). Requires `userIds`.
            </ParamField>

            <ParamField body="organizationId" type="string">
              Organization ID whose users can see the annotation. Required when `type` is `organizationPrivate`.
            </ParamField>

            <ParamField body="userIds" type="string[]">
              User IDs allowed to see the annotation. Required (non-empty) when `type` is `restricted`.
            </ParamField>
          </Expandable>
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

## **Example Requests**

#### Add comment annotation by organizationId, documentId and location

```JSON theme={null}
{
  "data": {
    "organizationId": "yourOrganizationId",
    "documentId": "yourDocumentId",
    "commentAnnotations": [
      {
        "location": {
          "id": "yourLocationId",
          "locationName": "yourLocationName"
        },
        "targetElement": {
          "elementId": "yourElementId",
          "targetText": "Your Target Text",
          "occurrence": 1,
          "selectAllContent": false
        },
        "commentData": [
          {
            "commentText": "Sample Comment",
            "commentHtml": "<div>Sample Comment</div>",
            "from": {
              "userId": "yourUserId",
              "name": "yourUserName",
              "email": "yourUserEmail",
            }
          }
        ]
      }
    ]
  }
}
```

#### Add comment annotation with user tagging

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "commentAnnotations": [
      {
        "commentData": [
          {
            "triggerNotification": true,
            "commentText": "Hey {{user_sarah_chen}}, can you review this color scheme? I think we should use a darker shade for better contrast.",
            "commentHtml": "<p>Hey {{user_sarah_chen}}, can you review this color scheme? I think we should use a darker shade for better contrast.</p>",
            "from": {
              "userId": "user_john_smith",
              "name": "John Smith",
              "email": "john.smith@acme-corp.com"
            },
            "taggedUserContacts": [
              {
                "userId": "user_sarah_chen",
                "contact": {
                  "userId": "user_sarah_chen",
                  "name": "Sarah Chen",
                  "email": "sarah.chen@acme-corp.com"
                }
              }
            ]
          }
        ]
      }
    ]
  }
}
```

In this example, `{{user_sarah_chen}}` in the commentText will be replaced with "Sarah Chen" on the frontend, displaying as "Hey Sarah Chen, can you review this color scheme? I think we should use a darker shade for better contrast."

#### Add comment annotation with permission verification

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "verifyUserPermissions": true,
    "commentAnnotations": [
      {
        "commentData": [
          {
            "commentText": "Please review this section",
            "from": {
              "userId": "user_john_smith",
              "name": "John Smith",
              "email": "john.smith@acme-corp.com"
            }
          }
        ]
      }
    ]
  }
}
```

<Note>
  When `verifyUserPermissions` is enabled, the API verifies the user has access to the document before creating the comment annotation. If verification fails, the request will be rejected.
</Note>

#### Add comment annotation with activity tracking

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "commentAnnotations": [
      {
        "commentData": [
          {
            "commentText": "This needs review",
            "triggerNotification": true,
            "triggerActivities": true,
            "from": {
              "userId": "user-1",
              "email": "user@example.com",
              "name": "User Name"
            }
          }
        ]
      }
    ]
  }
}
```

#### Add comment annotation with access context

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "analytics-dashboard",
    "commentAnnotations": [
      {
        "context": {
          "access": {
            "entityId": "numberOfVisitors",
            "dashboardId": "myDashboard"
          }
        },
        "commentData": [
          {
            "commentText": "Traffic numbers look unusual today",
            "from": {
              "userId": "user_john_smith",
              "name": "John Smith",
              "email": "john.smith@acme-corp.com"
            }
          }
        ]
      }
    ]
  }
}
```

#### Add an agent suggestion (finding)

Set `type: "suggestion"` and attach an `agent` block to the root comment (`commentData[0]`). The server generates the annotation-level agent block and stamps `sourceType: "agent"`.

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "commentAnnotations": [
      {
        "type": "suggestion",
        "commentData": [
          {
            "commentText": "Heading has a spelling mistake: 'Welcom' should be 'Welcome'.",
            "from": {
              "userId": "spell-check",
              "name": "Spell Check Agent"
            },
            "agent": {
              "agentSource": "velt",
              "agentId": "spell-check",
              "executionId": "exec_123",
              "url": "https://example.com/design-mockup-v2",
              "reason": {
                "title": "Spelling error",
                "description": "The word 'Welcom' is misspelled.",
                "severity": "low",
                "findingType": "text",
                "confidence": 98,
                "suggestedFix": "Welcome",
                "source": "instructions"
              }
            }
          }
        ]
      }
    ]
  }
}
```

To attach a finding from an agent run through your own framework, use `agentSource: "external"` and supply your own `agentName` (and optionally `agentId` / `executionId`):

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "commentAnnotations": [
      {
        "type": "suggestion",
        "commentData": [
          {
            "commentText": "This button has insufficient color contrast.",
            "from": {
              "userId": "a11y-bot"
            },
            "agent": {
              "agentSource": "external",
              "agentName": "Accessibility Bot",
              "agentId": "a11y-bot",
              "reason": {
                "title": "Low color contrast",
                "description": "Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold.",
                "severity": "high",
                "findingType": "pin"
              }
            }
          }
        ]
      }
    ]
  }
}
```

#### Add a private (restricted) comment annotation

Use `visibility` to limit who can see the annotation.

```JSON theme={null}
{
  "data": {
    "organizationId": "acme-corp",
    "documentId": "design-mockup-v2",
    "commentAnnotations": [
      {
        "visibility": {
          "type": "restricted",
          "userIds": ["user_sarah_chen", "user_john_smith"]
        },
        "commentData": [
          {
            "commentText": "Internal note: don't ship this section yet.",
            "from": {
              "userId": "user_john_smith",
              "name": "John Smith",
              "email": "john.smith@acme-corp.com"
            }
          }
        ]
      }
    ]
  }
}
```

# Response

#### Success Response

```JSON theme={null}
{
   "result": {
       "status": "success",
       "message": "Comment Annotation(s) added successfully.",
       "data": {
           "-O0mpUziLcBwzREvZKs6": {
               "success": true,
               "annotationId": "-O0mpUziLcBwzREvZKs6",
               "commentIds": [
                   126535
               ],
               "message": "Added Successfully"
           }
       }
   }
}

```

#### Failure Response

```JSON theme={null}
{
  "error": {
    "message": "ERROR_MESSAGE",
    "status": "INVALID_ARGUMENT"
  }
}
```

<ResponseExample>
  ```js theme={null}
  {
     "result": {
         "status": "success",
         "message": "Comment Annotation(s) added successfully.",
         "data": {
             "-O0mpUziLcBwzREvZKs6": {
                 "success": true,
                 "annotationId": "-O0mpUziLcBwzREvZKs6",
                 "commentIds": [
                     126535
                 ],
                 "message": "Added Successfully"
             }
         }
     }
  }
  ```
</ResponseExample>
