# Переписка лида

Читайте оба направления переписки в рамках одной кампании и проверяйте, за какой период данные полны.

Новые возможности включаются для кабинета по запросу.

## Что входит в переписку

Запрос `GET /v1/leads/{id}/messages?view=conversation` возвращает доступные сообщения с первого подтвержденного нашего сообщения этому лиду в этой кампании, включая само первое сообщение. Входят шаги кампании, ручные исходящие сообщения и отдельные входящие сообщения, в том числе сообщения с вложением без текста.

Лид означает участие контакта в одной кампании. Более ранняя переписка и сообщения другой кампании не входят в ответ. Если принадлежность сообщения этой переписке не установлена, API не приписывает его лиду по догадке. У лида может быть несколько диалогов с разными аккаунтами: полнота каждого находится в `meta.dialogs`.

Сохраняется первая доступная копия сообщения. Правки и удаления не отслеживаются. Уже удаленное до сохранения сообщение может отсутствовать даже в проверенном диапазоне. `complete` не означает, что восстановлены все когда-либо существовавшие сообщения.

## Запрос и поля сообщения

Нужны право `messages:read` и включенный у ключа «Текст сообщений». Без `view` маршрут сохраняет прежний формат ответа и прежние курсоры.

```bash
curl -G https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31/messages \
  -H "Authorization: Bearer $MELIORA_API_KEY" \
  -d view=conversation -d limit=50
```

Ответ имеет поля `object`, `data`, `has_more`, `next_cursor`, `meta`. У каждого сообщения есть:

- `id`: устойчивый идентификатор. Объединяйте повторные результаты по нему.
- `workspace_id`, `campaign_id`, `lead_id`, `account_id`: контекст сообщения.
- `direction`: `inbound` или `outbound`; `author`: `campaign`, `manager`, `lead` или `unknown`.
- `state`: `sent`, `received` или `unknown`. Неизвестный исход не означает неудачную отправку.
- `sent_at`: время сообщения или `null`; `observed_at`: когда оно было сохранено.
- `media_kind`: вид содержимого; `attachment`: описание вложения или `null`.
- `text`, `text_truncated`, `text_unavailable`, `text_unavailable_reason`: сохраненный текст и его доступность.

Текст ограничен 4000 символами. `text_truncated=true` означает усечение, `false` означает полный текст, `null` означает, что это неизвестно. При недоступном тексте `text=null`; причина: `not_saved`, `ownership_unproven`, `source_conflict` или `purged`. Недоступность текста и полнота набора сообщений оцениваются отдельно.

Полные схемы и вымышленные ответы находятся в [справочнике переписки](https://docs.melioralab.io/reference/messages/#listLeadMessages).

## Вложения

`attachment` содержит только `{kind, caption}`. Виды: `photo`, `video`, `voice`, `video_note`, `file`, `sticker`, `other`. Аудиофайл относится к `file`, неподдержанный вид к `other`. Подпись ограничена 4000 символами или равна `null`; признаки доступности и усечения текста относятся и к подписи. Если текст недоступен, подпись тоже равна `null`.

Файлы, ссылки для скачивания, имена файлов и содержимое карточек контактов не выдаются.

## Как читать полноту

`meta.completeness` описывает весь доступный диапазон лида, а `meta.dialogs[].completeness` описывает каждый диалог отдельно.

- `overall`, `inbound`, `outbound`: общая полнота и полнота двух направлений. Значения: `complete`, `partial`, `unknown`.
- `boundary_at`: время первого подтвержденного нашего сообщения в кампании. Это начало диапазона, включая первое сообщение.
- `checked_at`: когда диапазон проверен; `checked_through`: до какого времени включительно он проверен. Это разные величины. `null` означает, что значение неизвестно.
- `available_from`, `available_to`: границы доступных сохраненных данных. Они сами по себе не доказывают полноту.
- `observed_at`: время чтения сведений о полноте.
- `gaps`: до 50 интервалов возможной неполноты с полями `id`, `from`, `to`, `state`. При незакрытом интервале `to=null`.
- `gaps_truncated=true`: список разрывов сокращен; пустой список разрывов не доказывает полноту.

`complete` означает, что оба направления проверены от начала до `checked_through` на момент `checked_at`. Новые сообщения после проверки этим обещанием не покрываются. `partial` означает известную неполную часть, например разрыв или недоступный период. `unknown` означает, что данных для вывода недостаточно, например не установлена начальная граница.

Разрыв имеет состояние `open`, `recovering`, `recovered`, `unrecoverable` или `unknown`. `recovered` означает, что этот разрыв закрыт; для общего вывода все равно смотрите `overall`.

## Что делать при partial

1. Сохраните полученные сообщения и отметку неполноты. Не показывайте отсутствие сообщения как доказательство, что его не было.
2. Дочитайте все страницы текущего обхода. `has_more=false` означает только конец страниц сохраненных данных.
3. Позже начните новый обход без `cursor`, сохранив `view=conversation`, и объедините сообщения по `id`. Сравните полноту и границы проверки у каждого диалога.
4. Если разрыв остается `unrecoverable`, храните отметку неполноты. Если полнота долго не меняется, обратитесь в [поддержку](https://docs.melioralab.io/support/) с `Request-Id`.

При `unknown` также сохраняйте неопределенность и повторяйте чтение позже, не считая историю полной.

## Курсор и snapshot_stale

Первая страница фиксирует набор данных для обхода. Страницы идут от ранних сообщений к поздним; курсор продолжает тот же набор. Сообщение, добавленное позже, в том числе более раннее по времени, появится в новом обходе.

Передавайте `view=conversation` на каждой странице. Менять `limit` можно: по умолчанию 50, максимум 100. Курсор привязан к лиду, представлению и охвату ключа; курсор прежнего представления сюда не подходит. Срок курсора 15 минут: при 410 `cursor_expired` начните заново, при 400 `cursor_mismatch` проверьте параметры и охват.

Если хотя бы на одной странице пришло `meta.snapshot_stale=true`, после начала обхода появились новые факты. Дочитайте текущий обход, затем начните новый без курсора и объедините результат по `id`. `snapshot_stale=false` не заменяет проверку `meta.completeness`.

## Входящие события и вебхуки

`message.received` означает одно отдельное входящее сообщение, в том числе без текста. `data.message.id` совпадает с идентификатором в переписке. `data.backfilled=true` означает восстановленный пропуск; время сообщения остается исходным. События не заменяют чтение всей доступной переписки.

Для ленты выберите [каталог `2b`](https://docs.melioralab.io/events-polling/#catalog-2b) или `type=message.received`. В ленте текст и подпись можно запросить через `include_text=true`. [Новые вебхуки](https://docs.melioralab.io/webhooks/#new-event-types) передают только метаданные; текст читайте этим маршрутом API.
