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

# Agent Comments

> Let your AI agents leave comments and findings directly in Velt by calling the Comments REST APIs.

## Overview

Agent comments let your AI agents participate in collaboration the same way humans do: by leaving comments and findings anchored to a document. Instead of building a separate UI for agent output, your agent calls the same REST APIs that power the [Comments](https://velt.dev/docs/async-collaboration/comments/overview.md) feature, and the findings render in the standard Velt comments experience.

Any agent that can make an HTTP request can do this: a custom agent you create with the [Review Agents API](https://velt.dev/docs/api-reference/rest-apis/v2/agents/create.md), or an external agent running in your own framework (LangChain, CrewAI, a cron job, etc.).

## How it works

1. **Your agent runs** and produces a finding (a spelling error, an accessibility issue, a code-review note, etc.).
2. **Your agent calls the [Add Comment Annotations API](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/add-comment-annotations.md)** with an `agent` block attached to the root comment.
3. **The finding renders in Velt** as a [suggestion](https://velt.dev/docs/async-collaboration/suggestions/overview.md) with **Accept** and **Reject** buttons that humans can review.
4. **Your app reacts to the outcome**: read agent comments back with the [Get Comment Annotations API](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/get-comment-annotations-v2.md), or subscribe to the `suggestionAccepted` / `suggestionRejected` events in your frontend.

## Properties

* An agent comment is a regular comment annotation with an `agent` block attached to its root comment. There is no separate agent-comments API or data store. You add, read, update, and delete agent comments with the same [Comment Annotations and Comments REST APIs](#backend-apis).
* When you attach an `agent` block to the root comment (`commentData[0]`), the server stamps `sourceType: "agent"` on both that comment and the annotation, and generates the annotation-level `agent` block. When you attach it to a reply instead, only that comment is marked as agent-authored; the annotation root stays a normal comment.
* Set the annotation `type` to `"suggestion"` so the finding is classified as an agent suggestion and renders with **Accept** / **Reject** buttons instead of as a regular comment.
* Agent comments support two origins: `velt` (a built-in agent like `spell-check`, or a custom agent you create with the [Review Agents API](https://velt.dev/docs/api-reference/rest-apis/v2/agents/create.md); the `agentId` is verified server-side) and `external` (an agent running in your own framework, where the `agentName` you supply is the source of truth).
* When a reviewer accepts or rejects a finding, the outcome is emitted on the comment element as the `suggestionAccepted` / `suggestionRejected` events, so you can apply the change to your own data or trigger follow-up logic.

## APIs

### Backend APIs

#### Add Agent Comments

* Add an agent comment using the Add Comment Annotations REST API. Attach an `agent` block to the root comment (`commentData[0]`) and set the annotation `type` to `"suggestion"`. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/add-comment-annotations.md)
* See [The agent object](#the-agent-object) below for all the fields you can send.

The most common path is an agent running in your own framework. Use `agentSource: "external"` and supply your own `agentName`:

```bash cURL expandable theme={null}
curl -X POST 'https://api.velt.dev/v2/commentannotations/add' \
  -H 'x-velt-api-key: YOUR_API_KEY' \
  -H 'x-velt-auth-token: YOUR_AUTH_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "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",
                "executionId": "run_8f21",
                "url": "https://example.com/design-mockup-v2",
                "reason": {
                  "title": "Low color contrast",
                  "description": "Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold.",
                  "severity": "high",
                  "findingType": "pin"
                }
              }
            }
          ]
        }
      ]
    }
  }'
```

```javascript Node.js expandable theme={null}
const response = await fetch('https://api.velt.dev/v2/commentannotations/add', {
  method: 'POST',
  headers: {
    'x-velt-api-key': process.env.VELT_API_KEY,
    'x-velt-auth-token': process.env.VELT_AUTH_TOKEN,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    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',
                executionId: 'run_8f21',
                url: 'https://example.com/design-mockup-v2',
                reason: {
                  title: 'Low color contrast',
                  description:
                    'Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold.',
                  severity: 'high',
                  findingType: 'pin',
                },
              },
            },
          ],
        },
      ],
    },
  }),
});

const result = await response.json();
```

```python Python expandable theme={null}
import os
import requests

response = requests.post(
    "https://api.velt.dev/v2/commentannotations/add",
    headers={
        "x-velt-api-key": os.environ["VELT_API_KEY"],
        "x-velt-auth-token": os.environ["VELT_AUTH_TOKEN"],
        "Content-Type": "application/json",
    },
    json={
        "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",
                                "executionId": "run_8f21",
                                "url": "https://example.com/design-mockup-v2",
                                "reason": {
                                    "title": "Low color contrast",
                                    "description": "Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold.",
                                    "severity": "high",
                                    "findingType": "pin",
                                },
                            },
                        }
                    ],
                }
            ],
        }
    },
)

result = response.json()
```

> [!TIP]
>
> For a built-in agent or a custom agent created with the [Review Agents API](https://velt.dev/docs/api-reference/rest-apis/v2/agents/create.md), use `agentSource: "velt"` and pass its `agentId` instead. The agent's name is resolved server-side.

#### Reply as an Agent

* Add an agent-authored reply to an existing comment annotation using the Add Comments REST API with an `agent` block on the comment. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comments/add-comments.md)
* Annotation-level fields such as `type` are set when the annotation is created. They are not accepted on this endpoint.

#### Get Agent Comments

* Get agent comments using the Get Comment Annotations REST API with agent-specific filters. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/get-comment-annotations-v2.md)
* Only one agent filter may be supplied per request.

| Filter | Description |
| - | - |
| `agentId` | Annotations created by a specific agent. |
| `executionId` | Annotations from a specific agent run. Also returns comments from up to 29 earlier runs whose findings this run kept unchanged. |
| `agentType` | Annotations of a given agent type: `built-in`, `custom`, or `external`. |
| `agentSource` | `velt` or `external`. |
| `agentSuggestions` | When `true`, returns only fresh (unaccepted) agent suggestions. |
| `agentComments` | When `true`, returns all agent annotations regardless of status. |

```bash cURL theme={null}
curl -X POST 'https://api.velt.dev/v2/commentannotations/get' \
  -H 'x-velt-api-key: YOUR_API_KEY' \
  -H 'x-velt-auth-token: YOUR_AUTH_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "organizationId": "acme-corp",
      "documentId": "design-mockup-v2",
      "agentId": "a11y-bot"
    }
  }'
```

* Agent annotations in the response carry `type: "suggestion"` and `sourceType: "agent"` at the annotation root, an annotation-root `agent` block, and an `agent` block on each agent-authored comment (`comments[].agent`).
* To retrieve individual comments within a specific annotation, use the Get Comments REST API. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comments/get-comments.md)

#### Update Agent Comments

* Update annotation-level fields (status, assignee, location, etc.) using the Update Comment Annotations REST API. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/update-comment-annotations.md)
* Update the content of individual comments within an annotation using the Update Comments REST API. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comments/update-comments.md)

#### Delete Agent Comments

* Delete entire agent comment threads using the Delete Comment Annotations REST API. Filter by `annotationIds` or `userIds` (e.g. your agent's user ID), or use the combinable agent filters — `agentId`, `agentSuggestions`, and `agentUrls` — to delete e.g. one agent's still-pending suggestions for specific pages. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comment-annotations/delete-comment-annotations.md)
* Delete individual comments within a thread using the Delete Comments REST API. [Learn more](https://velt.dev/docs/api-reference/rest-apis/v2/comments-feature/comments/delete-comments.md)

### Frontend APIs

#### Handle Accept / Reject Events

Because agent findings render with **Accept** and **Reject** buttons on the comment dialog, the outcome is emitted on the **comment element**. Subscribe to the [`suggestionAccepted` and `suggestionRejected` events](https://velt.dev/docs/async-collaboration/comments/customize-behavior#event-subscription) to apply the change to your own data or trigger follow-up logic. The SDK records the outcome and persists the suggestion. Applying the change is your code's job.

```tsx React theme={null}
import { useEffect } from 'react';
import { useCommentEventCallback } from '@veltdev/react';

export function AgentSuggestionListener() {
  const accepted = useCommentEventCallback('suggestionAccepted');
  const rejected = useCommentEventCallback('suggestionRejected');

  useEffect(() => {
    if (accepted) {
      // accepted.commentAnnotation contains the agent finding
      console.log('Suggestion accepted', accepted.commentAnnotation);
    }
  }, [accepted]);

  useEffect(() => {
    if (rejected) {
      console.log('Suggestion rejected', rejected.rejectReason);
    }
  }, [rejected]);

  return null;
}
```

```javascript Other Frameworks theme={null}
const commentElement = Velt.getCommentElement();

commentElement.on('suggestionAccepted').subscribe(({ commentAnnotation }) => {
  // commentAnnotation contains the agent finding
  console.log('Suggestion accepted', commentAnnotation);
});

commentElement.on('suggestionRejected').subscribe(({ commentAnnotation, rejectReason }) => {
  console.log('Suggestion rejected', rejectReason);
});
```

> [!TIP]
>
> For the full suggestion lifecycle (statuses, applying `newValue`, and the other suggestion-stream events), see the [Suggestions](https://velt.dev/docs/async-collaboration/suggestions/overview.md) guide.

## The agent object

Attach an `agent` object to the root comment (`commentData[0]`) when [adding an agent comment](#add-agent-comments), or to any comment when [replying as an agent](#reply-as-an-agent). It is discriminated on `agentSource`:

| Field | Required | Description |
| - | - | - |
| `agentSource` | Yes | Origin of the agent. One of `velt` or `external`. |
| `agentId` | Yes | The agent's ID. Must be non-empty. Verified server-side for `velt` agents; opaque (never validated) for `external` agents. |
| `agentName` | Required for `external` | Display name for the agent. The only source of truth for an external agent's name. (For `velt` agents the name is resolved server-side.) |
| `executionId` | No | Execution / run ID for this agent invocation. |
| `url` | No | Page URL associated with the finding. |
| `reason` | Yes | Finding details (`title`, `description`, `severity`, `findingType`, `confidence`, `suggestedFix`, etc.). Custom fields are preserved. |

### The reason object

The `reason` object carries the finding's details. Here's each field with a description and an example value.

| Field | Required | Type | Description | Example |
| - | - | - | - | - |
| `title` | Yes | string | Short finding title, used as a quick label for the issue. | `"Low color contrast"` |
| `description` | Yes | string | Fuller explanation of what the agent found. | `"Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold."` |
| `severity` | Yes | string | How serious the finding is. One of `critical`, `high`, `medium`, `low`, `info`. | `"high"` |
| `findingId` | No | string | Your own unique ID for the finding, useful for dedup/tracking. | `"finding_a11y_0427"` |
| `findingType` | No | string | What kind of target the finding is on. One of `text`, `pin`, `page`. | `"pin"` |
| `issueType` | No | string | Custom classification you define for your own taxonomy. | `"accessibility"` |
| `confidence` | No | number | How confident the agent is. Integer 0–100. | `92` |
| `suggestion` | No | string | Suggested change in plain text (human-readable advice). | `"Darken the button background to at least #1A1A1A."` |
| `suggestedFix` | No | string | The concrete fix value to apply. | `"#1A1A1A"` |
| `htmlSnippet` | No | string | The relevant chunk of HTML where the issue lives. | `"<button class='cta'>Buy now</button>"` |
| `htmlSelector` | No | string | CSS/HTML selector pointing to the finding's location. | `".cta-primary > button"` |
| `source` | No | string | Where the triggering rule came from. One of `instructions`, `knowledge`. | `"knowledge"` |
| `knowledgeSection` | No | string | Which knowledge section fired (pairs with `source: "knowledge"`). | `"brand-guidelines/accessibility"` |

> [!NOTE]
>
> Don't conflate the two "fix" fields: `suggestion` is prose meant for a human to read in the comment, while `suggestedFix` is the actual replacement value your code applies. In the color-contrast example above, `suggestion` explains the change ("Darken the button background to at least #1A1A1A.") while `suggestedFix` is just the value itself (`"#1A1A1A"`).

Putting it together, a fully-populated `reason` looks like:

```json theme={null}
"reason": {
  "title": "Low color contrast",
  "description": "Contrast ratio is 2.1:1, below the 4.5:1 WCAG AA threshold.",
  "severity": "high",
  "findingId": "finding_a11y_0427",
  "findingType": "pin",
  "issueType": "accessibility",
  "confidence": 92,
  "suggestion": "Darken the button background to at least #1A1A1A.",
  "suggestedFix": "#1A1A1A",
  "htmlSnippet": "<button class='cta'>Buy now</button>",
  "htmlSelector": ".cta-primary > button",
  "source": "knowledge",
  "knowledgeSection": "brand-guidelines/accessibility"
}
```

Only the first three (`title`, `description`, `severity`) are required. Everything else is optional, and any extra custom fields you add beyond this list are preserved by the server.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.