## Отправить обращение

`POST /v1/feedback`

Создаёт обращение в трекере обратной связи. Принимает любой действительный ключ — портал и автор определяются по владельцу ключа. Тело передаётся плоско, без обёртки `fields`.

## Поля запроса (body)

| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|----------|
| `category` | string | да | Одно из: `BUG`, `SUGGESTION`, `DOCS`, `CHAT`, `BOTS`, `OTHER`. Регистр не важен |
| `title` | string | да | Заголовок, 3–200 символов |
| `body` | string | да | Описание, 10–20000 символов. Нижняя граница снимается, если приложены вложения через `attachmentIds` |
| `context` | object | нет | Произвольный JSON до 10 КБ — эндпоинт, тело запроса, `requestId`, версия инструмента |
| `attachmentIds` | array | нет | До 5 идентификаторов заранее загруженных вложений. Как загрузить — [Загрузить вложение](./attachments.md) |

### Категории

| Категория | Когда выбирать |
|-----------|----------------|
| `BUG` | Поведение расходится с документацией или ломается |
| `SUGGESTION` | Предложение улучшения или новой возможности |
| `DOCS` | Документация неполная, устарела или расходится с поведением API |
| `CHAT` | Обратная связь по чат-платформе — сообщения, диалоги, файлы |
| `BOTS` | Обратная связь по бот-платформе — регистрация, события, команды |
| `OTHER` | Не подходит под остальные категории |

## Примеры

### curl — личный ключ

```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",
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

### curl — OAuth-приложение

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/feedback \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -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",
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

### JavaScript — личный ключ

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/feedback', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    category: 'BUG',
    title: 'POST /v1/deals/search возвращает 500 при пустом filter',
    body: 'При вызове POST /v1/deals/search с телом {"filter":{}} приходит 500. Ожидал пустой массив или 400.',
    context: { endpoint: '/v1/deals/search', httpStatus: 500, requestId: 'req_abc123' },
  }),
})

const { data } = await res.json()
console.log('Номер обращения:', 'VB-' + data.id.slice(0, 8))
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/feedback', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    category: 'BUG',
    title: 'POST /v1/deals/search возвращает 500 при пустом filter',
    body: 'При вызове POST /v1/deals/search с телом {"filter":{}} приходит 500. Ожидал пустой массив или 400.',
    context: { endpoint: '/v1/deals/search', httpStatus: 500, requestId: 'req_abc123' },
  }),
})

const { data } = await res.json()
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | Всегда `true` при успехе |
| `data.id` | string | UUID обращения. Номер для человека — `VB-` плюс первые 8 символов |
| `data.category` | string | Категория обращения |
| `data.title` | string | Заголовок |
| `data.status` | string | Начальный статус — всегда `NEW` |
| `data.createdAt` | string | Дата создания в формате ISO 8601 |

## Пример ответа

```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"
  }
}
```

## Пример ответа при ошибке

400 — нарушена валидация:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "title must be 3-200 characters"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `VALIDATION_ERROR` | `category`, `title`, `body` или `context` не прошли валидацию |
| 409 | `DUPLICATE_FEEDBACK` | Похожее обращение уже отправлено недавно. В `error.existingTicketId` — существующее обращение |
| 409 | `OPEN_TICKET_EXISTS` | По этой теме уже есть открытое обращение. В `error.existingTicketId` — оно же |
| 429 | `FEEDBACK_QUOTA_EXCEEDED` | Превышена суточная квота на создание обращений (на портал или на ключ). Возвращает заголовок `Retry-After` и поля `scope`, `limit`, `retryAfter` |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 429 | `RATE_LIMITED` | Больше 5 запросов в минуту с одного ключа |

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

## Известные особенности

**Источник фиксируется автоматически.** У обращений через API поле `source` равно `api`, у отправленных с формы в кабинете — `ui`. Портал и автор берутся из владельца ключа, вводить их вручную не нужно.

**Контекст ускоряет разбор.** В `context` кладут `requestId`, версии, тело запроса и ответ сервера — до 10 КБ произвольного JSON. С полным контекстом обращение разбирается без уточняющих вопросов.

**Защита от дублей и квота.** Похожий по содержанию запрос отклоняется как `409 DUPLICATE_FEEDBACK`, а открытое обращение с тем же заголовком — как `409 OPEN_TICKET_EXISTS`. В обоих случаях `error.existingTicketId` указывает на существующее обращение, если у ключа есть к нему доступ. Отдельно действует суточная квота на портал и на ключ — при превышении приходит `429 FEEDBACK_QUOTA_EXCEEDED` с заголовком `Retry-After`. Это не то же самое, что лимит 5 запросов в минуту (`429 RATE_LIMITED`).

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

- [Список обращений](/docs/feedback/list)
- [Загрузить вложение](/docs/feedback/attachments)
- [Обратная связь](/docs/feedback)
- [Ошибки](/docs/errors)
