# Обратная связь

Программный канал обратной связи Вайбкод. AI-модели, интеграции и внешние инструменты отправляют сообщения об ошибках, предложения и вопросы прямо из кода — с контекстом ошибки, окружением и шагами воспроизведения. Обращения, отправленные через API, попадают в тот же трекер, что и обращения с формы в кабинете.

**Базовый URL:** `https://vibecode.bitrix24.tech/v1` | **Авторизация:** `X-Api-Key`

[Уровни доступа](#уровни-доступа) | [Быстрый старт](#быстрый-старт) | [Полный пример](#полный-пример) | [Справочник эндпоинтов](#справочник-эндпоинтов) | [Коды ошибок](#коды-ошибок)

## Уровни доступа

Права зависят от того, есть ли у ключа скоуп `vibe:feedback`.

| Ключ | Создание | Чтение | Обновление и комментарии |
|------|----------|--------|--------------------------|
| Обычный ключ (`vibe_api_` / `vibe_app_`) | да | свои обращения | только отзыв своего обращения |
| Ключ со скоупом `vibe:feedback` | да | все обращения своего портала | да, в рамках своего портала |

Скоуп `vibe:feedback` не выдаётся ключу автоматически — его включают явно при создании или обновлении ключа. Скоуп даёт расширенный доступ на чтение и обновление всех обращений портала, а не только своих. Выдаётся адресно — например, ключу, который разбирает очередь обращений в службе поддержки.

## Быстрый старт

Отправка обращения об ошибке с контекстом запроса:

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/feedback \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "BUG",
    "title": "POST /v1/deals/search возвращает 500 при пустом filter",
    "body": "При вызове POST /v1/deals/search с телом {\"filter\":{}} приходит 500. Ожидал пустой массив или 400.",
    "context": {
      "endpoint": "/v1/deals/search",
      "request": {"filter": {}},
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

Ответ:

```json
{
  "success": true,
  "data": {
    "id": "a1b2c3d4-1111-2222-3333-444455556666",
    "category": "BUG",
    "title": "POST /v1/deals/search возвращает 500 при пустом filter",
    "status": "NEW",
    "createdAt": "2026-04-19T10:30:00.000Z"
  }
}
```

Номер обращения, который видит пользователь и поддержка — это `VB-` плюс первые 8 символов `id`. Для примера выше это `VB-a1b2c3d4`. Сохраните его, чтобы сослаться на обращение в переписке.

## Полный пример

Отправка обращения, проверка его в списке и отзыв, если оно создано по ошибке.

```javascript
const BASE = 'https://vibecode.bitrix24.tech/v1'
const headers = { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' }

// 1. Отправить обращение
const created = await fetch(`${BASE}/feedback`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    category: 'DOCS',
    title: 'Опечатка в разделе про фильтрацию',
    body: 'На странице /docs/filtering в примере пропущена закрывающая скобка.',
  }),
}).then(r => r.json())

const ticketId = created.data.id
console.log('Обращение создано:', 'VB-' + ticketId.slice(0, 8))

// 2. Найти его в своём списке
const list = await fetch(`${BASE}/feedback?category=DOCS&limit=5`, { headers })
  .then(r => r.json())
console.log('Всего обращений категории DOCS:', list.total)

// 3. Отозвать, если создано по ошибке
await fetch(`${BASE}/feedback/${ticketId}`, {
  method: 'PATCH',
  headers,
  body: JSON.stringify({ status: 'WITHDRAWN' }),
})
```

## Справочник эндпоинтов

| Метод | Путь | Описание |
|-------|------|----------|
| POST | [`/v1/feedback`](/docs/feedback/submit) | Отправить обращение |
| GET | [`/v1/feedback`](/docs/feedback/list) | Список обращений с фильтрами и пагинацией |
| GET | [`/v1/feedback/:id`](/docs/feedback/get) | Одно обращение по ID |
| PATCH | [`/v1/feedback/:id`](/docs/feedback/update) | Обновить статус и резолюцию, отозвать обращение |
| POST | [`/v1/feedback/:id/comments`](/docs/feedback/comments) | Добавить комментарий к обращению |
| POST | [`/v1/feedback/attachments`](/docs/feedback/attachments) | Загрузить вложение к обращению или комментарию |

## Windows / PowerShell и UTF-8

Кириллица в `title`, `body` или `resolution` может превратиться в знаки вопроса (`?`), если запрос отправляется из Windows PowerShell без явной сериализации в UTF-8. Это не проблема отображения на стороне сервера — кириллические байты теряются ещё до отправки HTTP-запроса, на стороне клиента.

**Причина.** По умолчанию PowerShell перекодирует строку из параметра `-Body` (`Invoke-WebRequest` / `Invoke-RestMethod`) в системную кодировку `windows-1251`, и кириллица теряется ещё до сборки запроса. Заголовок `Content-Type: charset=utf-8` здесь не помогает — к моменту его применения исходные байты уже потеряны.

**Решение.** Передавайте тело запроса массивом байтов UTF-8.

```powershell
# 1. Кодировка вывода консоли — на кодирование тела запроса не влияет
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# 2. Собрать JSON и преобразовать его в массив UTF-8 байтов
$body = @{
  category = 'DOCS'
  title    = 'Опечатка в документации'
  body     = 'В разделе про фильтрацию в примере пропущена закрывающая скобка.'
} | ConvertTo-Json -Compress

$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)

# 3. Передать в -Body массив байтов (не строку) и указать кодировку в Content-Type
Invoke-WebRequest `
  -Uri 'https://vibecode.bitrix24.tech/v1/feedback' `
  -Method POST `
  -Headers @{
    'X-Api-Key'    = 'YOUR_API_KEY'
    'Content-Type' = 'application/json; charset=utf-8'
  } `
  -Body $bytes
```

**Распространённые ошибки:**

- Сохранять `.ps1` с UTF-8 BOM — старые версии PowerShell могут не разобрать сам скрипт.
- Передавать в `-Body` строку (`-Body $body`) вместо массива байтов (`-Body $bytes`) — строка повторно перекодируется через системную кодировку.
- Полагаться только на `Content-Type: application/json; charset=utf-8` без `UTF8.GetBytes` — этот заголовок не восстанавливает потерянные байты, а лишь объявляет серверу заявленную кодировку тела.

**Node.js (`fetch`) и Python (`requests`)** кодируют тело в UTF-8 сами, дополнительных шагов не требуется. Проблема специфична для PowerShell.

## Коды ошибок

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `VALIDATION_ERROR` | `category`, `title`, `body` или `context` не прошли валидацию |
| 400 | `INVALID_FILTER_VALUE` | В списке передано неизвестное значение `status` или `category` |
| 400 | `NO_FILE` | В запрос на загрузку вложения не передан файл |
| 402 | `ACCOUNT_FROZEN` | Баланс заморожен. Приходит на загрузку, скачивание и удаление вложения и на обновление обращения — создание, список, чтение и комментарий работают и под заморозкой |
| 403 | `FEEDBACK_SCOPE_REQUIRED` | Обновление или комментарий без скоупа `vibe:feedback` |
| 404 | `NOT_FOUND` | Обращение не существует или недоступно ключу |
| 409 | `FEEDBACK_CLOSED` | Действие автора над закрытым обращением. Комментарий — на `ARCHIVED` и `WITHDRAWN` (на `RESOLVED` комментарий автора обращение переоткрывает); отзыв — на `RESOLVED`, `ARCHIVED` и `WITHDRAWN` |
| 409 | `DUPLICATE_FEEDBACK` | Похожее обращение уже отправлено недавно |
| 409 | `OPEN_TICKET_EXISTS` | По теме уже есть открытое обращение |
| 413 | `IMAGE_TOO_LARGE` | Файл вложения превышает 10 МБ |
| 429 | `FEEDBACK_QUOTA_EXCEEDED` | Превышена суточная квота на создание обращений (портал или ключ), с заголовком `Retry-After` |
| 429 | `RATE_LIMITED` | Больше 5 запросов в минуту на создание обращения или загрузку вложения |

**Переписка работает и при замороженном балансе.** Заморозка платёжного аккаунта отвечает `402 ACCOUNT_FROZEN`, но четыре операции обращений из-под неё выведены: создать обращение, получить список, открыть обращение и ответить команде. Сообщить о проблеме можно ровно тогда, когда это нужнее всего, и продолжить разговор, если поддержка попросит уточнений.

Под заморозкой остаются загрузка, скачивание и удаление вложения, а также обновление обращения — там приходит `402 ACCOUNT_FROZEN`. Суточные квоты на создание обращений заморозка не отменяет.

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Смотрите также

- [Отправить обращение](/docs/feedback/submit)
- [Ключи и авторизация](/docs/keys-auth)
- [Ошибки](/docs/errors)
