# Опросы

Опросы в Ленте через API **не создаются**: в REST API Битрикс24 создание опроса в Ленте официально не поддерживается. Существующие опросы можно читать и управлять ими. В чате опрос можно создать через API.

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

## Доступ

Все операции требуют скоуп `vote`. При создании в личном диалоге, когда `dialogId` — ID пользователя или `me`, дополнительно нужен `im`, для `chatN` достаточно `vote`. Права текущего пользователя в Битрикс24 продолжают действовать. Опросом чата управляет автор сообщения, в Ленте нужны права управления постом.

| Ключ | Чтение | Запись |
| --- | --- | --- |
| Обычный ключ «только чтение» | Все четыре GET-операции | Запрещена |
| Ключ Коворка «портал только для чтения» | Запрещено | Запрещена |
| Ключ компании | Запрещено | Запрещена |

Все четыре GET-операции читают опрос и подписывают пользователя на события Битрикс24, поэтому для ключа Коворка с запретом записи в портал они недоступны. Методы опросов также недоступны через [пакетные запросы](/docs/batch).

## Как найти опрос

В [списке постов](/docs/feed/posts/list) (`GET /v1/posts`) выберите записи с `hasPoll: true`. Затем передайте ID постов в `GET /v1/polls?postIds=42`.

В [сообщениях чата](/docs/chats/messages) (`GET /v1/chats/:dialogId/messages`) опрос отмечен полем `data.messages[].params.componentId = "VoteMessage"`. Значение `params.componentParams.id` — это `voteId`, номер опроса Битрикс24. Для поиска прикрепления передавайте **ID сообщения**, а не `voteId`, в `GET /v1/polls?messageIds=42`.

В полученном объекте `id` — идентификатор прикрепления (`pollId` в путях API). Он отличается от `voteId`, ID поста и ID сообщения. Если прикрепление не распознано, `source` может отсутствовать.

## Объект опроса

| Поле | Тип | Описание |
| --- | --- | --- |
| `id` | number | ID прикрепления для `pollId` |
| `voteId` | number | Номер опроса Битрикс24 |
| `source` | object | Необязательно: `{type: "post", postId}` или `{type: "chatMessage", messageId}` |
| `status` | string | `active` или `finished` |
| `anonymous` | boolean | Анонимный опрос |
| `allowRevote` | boolean | Разрешено переголосование |
| `resultsHidden` | boolean | Признак скрытия результатов в Битрикс24 |
| `resultsVisible` | boolean | Доступны ли итоги текущему пользователю по правилам Битрикс24 |
| `votersCount` | number \| null | Счётчик участников |
| `questions` | array | Только активные вопросы и ответы, в порядке сортировки Битрикс24 |
| `questions[].id` | number | ID вопроса |
| `questions[].text` | string | Текст вопроса |
| `questions[].multipleChoice` | boolean | Можно выбрать несколько ответов |
| `questions[].required` | boolean | Ответ обязателен |
| `questions[].votesCount` | number \| null | Счётчик голосов вопроса |
| `questions[].answers` | array | Варианты ответа |
| `questions[].answers[].id` | number | ID ответа для голосования и списка участников |
| `questions[].answers[].text` | string | Текст ответа |
| `questions[].answers[].votesCount` | number \| null | Счётчик голосов ответа |
| `questions[].answers[].percent` | number \| null | Процент из Битрикс24 |
| `myVote.voted` | boolean | Голосовал ли текущий пользователь |
| `myVote.answerIds` | number[] | Выбранные текущим пользователем ответы |
| `permissions.canVote` | boolean | Можно проголосовать |
| `permissions.canRevote` | boolean | Можно переголосовать |
| `permissions.canManage` | boolean | Можно управлять опросом |

## Результаты и анонимность

Итоги закрыты при `resultsHidden: true` или пока опрос активен, пользователь может голосовать и ещё не голосовал. Исключение — автор опроса, если Битрикс24 сообщает об авторстве: так бывает в опросах чатов на поддерживающих версиях портала. Права управления постом или сообщением итоги не открывают. Автору опроса Ленты скрытые итоги доступны в интерфейсе Битрикс24. При недостающих данных о правах итоги закрываются, а завершённый опрос без скрытия итогов доступен. При `resultsVisible: false` поля `votersCount`, `questions[].votesCount`, `questions[].answers[].votesCount` и `percent` равны `null`, а у ответов `voters: []` и `votersComplete: false` во всех ответах API с объектом опроса.

В анонимном опросе проголосовавших не видно никому, включая автора и администратора: счётчики доступны, если `resultsVisible: true`. В результатах `voters` всегда пуст и `votersComplete` равен `false`. Для открытого опроса результаты содержат ограниченную выборку участников по каждому ответу, полный список читается отдельной операцией. Счётчики не кэшируются.

Отчёт Excel и ссылки на итоги через API не отдаются. Выгрузить отчёт можно в интерфейсе Битрикс24.

## Безопасное выполнение записи

`idempotencyKey` при создании — только UUID. Повтор с тем же ключом и тем же телом на поддерживающей версии портала возвращает уже созданный опрос в `data.poll`. Поле `data.voteId` при повторе приходит новым числом, которое не находит ни один опрос, поэтому номер опроса берите из `data.poll.voteId`. Новый опрос — новый ключ. Битрикс24 не сравнивает тела: не меняйте вопрос, ответы или настройки при повторе. Если ключ не задан, сервер генерирует новый UUID для каждого запроса. Защита от дублей зависит от версии портала.

Платформа не повторяет записи автоматически. При `502 POLL_CREATE_OUTCOME_UNKNOWN` сначала посмотрите чат: сообщение могло появиться. При других `502 …_OUTCOME_UNKNOWN` прочитайте опрос и решите, нужна ли повторная запись. Не создавайте новый ключ сразу после неизвестного исхода.

При переголосовании в Ленте, если опрос правят в момент голоса, Битрикс24 может стереть прежний голос — так же, как в интерфейсе. После ошибки проверьте `myVote`.

## Удаление

Отдельного удаления опроса нет. Удалите содержащий его [пост](/docs/feed/posts/delete) или [сообщение](/docs/chats/messages/delete) существующим маршрутом.

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

Прочитать опрос по ID прикрепления:

```bash
curl -H "X-Api-Key: YOUR_API_KEY" "https://vibecode.bitrix24.tech/v1/polls/25"
```

Поля ответа — [Получить опрос](/docs/polls/get).

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

Создать опрос в чате, проголосовать, прочитать итоги и остановить опрос:

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

async function call(method, path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method,
    headers: body ? headers : { 'X-Api-Key': headers['X-Api-Key'] },
    body: body ? JSON.stringify(body) : undefined,
  })
  const json = await res.json()
  if (!json.success) throw new Error(`${res.status} ${json.error.code}: ${json.error.message}`)
  return json
}

// 1. Создать опрос в чате
const created = await call('POST', '/chats/chat5/polls', {
  question: 'When?',
  answers: ['Now', 'Later'],
  idempotencyKey: crypto.randomUUID(),
})
const poll = created.data.poll
console.log('Опрос', poll.id, 'в сообщении', created.data.messageId)

// 2. Проголосовать за первый ответ
const firstAnswer = poll.questions[0].answers[0].id
await call('POST', `/polls/${poll.id}/vote`, { answerIds: [firstAnswer] })

// 3. Прочитать итоги
const results = await call('GET', `/polls/${poll.id}/results`)
for (const answer of results.data.questions[0].answers) {
  console.log(answer.text, answer.votesCount, `${answer.percent}%`)
}

// 4. Остановить опрос
await call('POST', `/polls/${poll.id}/stop`)
const finished = await call('GET', `/polls/${poll.id}`)
console.log('Статус:', finished.data.status)
```

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

| Метод | Путь | Bitrix24 метод | Описание |
| --- | --- | --- | --- |
| POST | [`/v1/chats/:dialogId/polls`](/docs/polls/create) | vote.Integration.Im.send | Создать опрос в чате |
| GET | [`/v1/polls/:pollId`](/docs/polls/get) | vote.AttachedVote.get | Получить опрос |
| GET | [`/v1/polls`](/docs/polls/list) | vote.AttachedVote.getMany | Найти опросы по постам или сообщениям |
| GET | [`/v1/polls/:pollId/results`](/docs/polls/results) | vote.AttachedVote.getWithVoted | Результаты опроса |
| GET | [`/v1/polls/:pollId/answers/:answerId/voters`](/docs/polls/voters) | vote.AttachedVote.getAnswerVoted | Список проголосовавших за ответ |
| POST | [`/v1/polls/:pollId/vote`](/docs/polls/vote) | vote.AttachedVote.vote | Проголосовать или переголосовать |
| DELETE | [`/v1/polls/:pollId/vote`](/docs/polls/recall) | vote.AttachedVote.recall | Отозвать голос |
| POST | [`/v1/polls/:pollId/stop`](/docs/polls/stop) | vote.AttachedVote.stop | Остановить опрос |
| POST | [`/v1/polls/:pollId/resume`](/docs/polls/resume) | vote.AttachedVote.resume | Возобновить опрос |

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

### Ошибки опросов

| HTTP | Код | Описание |
| --- | --- | --- |
| 400 | `INVALID_ANSWERS` | Набор `answerIds` не подходит к вопросам опроса |
| 403 | `POLL_ACCESS_DENIED` | Битрикс24 запретил операцию с опросом |
| 403 | `CHAT_ACCESS_DENIED` | Нет доступа к чату, в котором создаётся опрос |
| 403 | `RESULTS_HIDDEN` | Итоги закрыты по правилам Битрикс24 |
| 404 | `POLL_NOT_FOUND` | Опрос не найден или недоступен ключу |
| 404 | `ANSWER_NOT_FOUND` | Битрикс24 явно отказал в доступе к списку участников ответа |
| 404 | `CHAT_NOT_FOUND` | Не удалось определить личный диалог |
| 409 | `POLL_FINISHED` | Битрикс24 сообщил, что опрос завершён |
| 409 | `POLL_CREATE_IN_PROGRESS` | Создание с этим `idempotencyKey` ещё выполняется |
| 409 | `IDEMPOTENCY_KEY_CONFLICT` | `idempotencyKey` уже использован другим пользователем или в другом чате |
| 502 | `POLL_CREATE_OUTCOME_UNKNOWN`, `POLL_VOTE_OUTCOME_UNKNOWN`, `POLL_RECALL_OUTCOME_UNKNOWN`, `POLL_STOP_OUTCOME_UNKNOWN`, `POLL_RESUME_OUTCOME_UNKNOWN` | Запись ушла в Битрикс24, а исход не подтверждён. Порядок действий — раздел «Безопасное выполнение записи» выше |

### Системные ошибки

| HTTP | Код | Описание |
| --- | --- | --- |
| 400 | `INVALID_PARAMS` | Некорректный ID в пути или параметр запроса |
| 401 | `TOKEN_MISSING` | Нет пользовательской сессии Битрикс24 |
| 403 | `SCOPE_DENIED` | Нет скоупа `vote` или, для личного диалога, `im` |
| 403 | `COWORK_READONLY` | Операция запрещена для ключа Коворка «портал только для чтения» |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | Ключ разрешает только чтение |
| 422 | `BITRIX_ERROR` | Битрикс24 отклонил запрос |

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

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

- [Список постов](/docs/feed/posts/list)
- [Сообщения](/docs/chats/messages)
- [Поиск чатов](/docs/chats/discovery)
- [Пакетные вызовы](/docs/batch)
