## Add a comment

`POST /v1/feedback/:id/comments`

Adds a comment to a ticket. Attachments can be added to a comment.

## Parameters

| Parameter | Type | Required | Description |
|-----------|------|:--------:|-------------|
| `id` (path) | string | yes | Ticket UUID. Get it from the [ticket list](./list.md) or the [create](./submit.md) response |

## Request fields (body)

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `body` | string | yes | Comment text, 2–20000 characters |
| `status` | string | no | New ticket status, applied together with a comment on someone else's ticket. Ignored in the author's comment on their own ticket. Values as in [update](./update.md) |
| `attachmentIds` | array | no | Up to 5 IDs of pre-uploaded attachments. To upload one, see [Upload an attachment](./attachments.md) |

## Examples

### curl — personal key

```bash
curl -X POST https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/comments \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Reproduced with an empty filter; attached the request log." }'
```

### curl — OAuth application

```bash
curl -X POST https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/comments \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Reproduced with an empty filter; attached the request log." }'
```

### JavaScript — personal key

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/comments',
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({ body: 'Reproduced with an empty filter; attached the request log.' }),
  },
)
const { data } = await res.json()
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/comments',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ body: 'Reproduced with an empty filter; attached the request log.' }),
  },
)
const { data } = await res.json()
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` on success |
| `data` | object | The created comment |
| `data.id` | string | Comment UUID. [Get a ticket](./get.md) returns the same ID in `comments[].id` |
| `data.authorType` | string | Who left the comment: `USER` or `PLATFORM` |
| `data.body` | string | Comment text |
| `data.createdAt` | string | Creation date (ISO 8601) |
| `data.feedbackStatus` | string | Ticket status after the comment |
| `data.previousStatus` | string | Ticket status before the comment |

## Response example

```json
{
  "success": true,
  "data": {
    "id": "61ae2a00-a7ac-416f-85cc-99873c767d3b",
    "authorType": "USER",
    "body": "Reproduced with an empty filter; attached the request log.",
    "createdAt": "2026-04-19T11:00:00.000Z",
    "feedbackStatus": "NEW",
    "previousStatus": "NEW"
  }
}
```

## Error response example

403 — comment without the `vibe:feedback` scope:

```json
{
  "success": false,
  "error": {
    "code": "FEEDBACK_SCOPE_REQUIRED",
    "message": "Requires management key or vibe:feedback scope"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `VALIDATION_ERROR` | `body` shorter than 2 or longer than 20000 characters, or `attachmentIds` has more than 5 items |
| 403 | `FEEDBACK_SCOPE_REQUIRED` | Comment on someone else's ticket without the `vibe:feedback` scope. The ticket's author can comment without the scope — see "Known specifics" |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is read-only — see [access rights](/docs/access-rights) |
| 404 | `NOT_FOUND` | The ticket does not exist or is not accessible to the key |
| 409 | `FEEDBACK_CLOSED` | An author comment on a permanently closed ticket (`ARCHIVED` or `WITHDRAWN`). An author comment reopens a `RESOLVED` ticket — see "Known specifics" |
| 429 | `RATE_LIMITED` | More than 20 comments per minute. The counter is shared per key owner, not per key. The current limit is returned in the `x-ratelimit-limit` header. The `Retry-After` header says how many seconds to wait before retrying |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |

For the full list of common API errors, see [Errors](/docs/errors).

## Known specifics

**A comment on someone else's ticket changes the status.** Such a comment is recorded as a platform reply (`authorType: PLATFORM`) and by default moves the ticket to `AWAITING_USER`. A different status is set with the `status` field in the body — values as in [update](./update.md). The `feedbackStatus` and `previousStatus` fields in the response show the status after and before the comment.

**The author comments on their own ticket without the scope.** If the request uses the same key that created the ticket, the comment is accepted without the `vibe:feedback` scope: the author check runs before the scope check. The scope is only needed for comments on other people's tickets.

**The author is the person, not the key.** A ticket created by another of your keys, or filed from your Vibecode account, is also yours: the comment is recorded as a user message (`authorType: USER`), the `resolution` field is not overwritten, and a supplied `status` is ignored — statuses are changed through [update](./update.md). One condition: such a key needs the `vibe:feedback` scope — without the scope, only the key that created the ticket counts as yours. Application keys and management keys fall outside this rule entirely: for them, the key owner and the person writing are different people.

**An author comment brings a resolved ticket back into the queue.** If the ticket is `RESOLVED`, your comment moves it to `NEEDS_REVIEW` and clears the closure metadata (`resolvedAt`, `resolvedBy`). The resolution text is kept. `ARCHIVED` and `WITHDRAWN` stay closed — comments on them still return `409 FEEDBACK_CLOSED`.

**An author comment changes the status only when a reply is expected.** From `AWAITING_USER`, the ticket moves to `NEEDS_REVIEW`. On the other open statuses — `NEW`, `REVIEWING`, `NEEDS_REVIEW` — the status stays the same: the ticket is already with the team. The `updatedAt` field is refreshed in any case, so a queue sorted by it will show a fresh reply regardless of status.

**Only a closing comment fills `resolution`.** A comment with a target status of `RESOLVED` or `ARCHIVED` writes its body into `resolution`. With any other status, the field is left unchanged. The text of such a comment still reaches the author by email and is visible in the thread.

## See also

- [Get a ticket](/docs/feedback/get)
- [Update a ticket](/docs/feedback/update)
- [Upload an attachment](/docs/feedback/attachments)
- [Feedback](/docs/feedback)
