# Лиды

Лид: участие одного контакта в одной кампании. Если один человек есть в двух кампаниях, это два лида с разными `id`.

Базовый адрес: https://api.melioralab.io/v1. Ключ передается в заголовке `Authorization: Bearer mel_live_...`.

Маршруты:

- `GET /v1/leads`: Список лидов
- `GET /v1/leads/{id}`: Получить лида

## Объект Lead

Лид: участие одного контакта в одной кампании. Один и тот же человек в двух кампаниях дает два разных лида.

| Поле | Тип | Описание |
|---|---|---|
| `object` | string | Тип объекта, всегда `lead`. |
| `id` | string (uuid) | Идентификатор лида (участия в кампании). |
| `workspace_id` | string (uuid) | Пространство кампании. |
| `campaign_id` | string (uuid) | Кампания, в которой участвует контакт. |
| `username` | string или null | Ник Telegram без `@`. Может быть `null`. Не уникален: используйте `fields.record_id` для связи с CRM. |
| `status` | string | Статус лида в кампании. Значения: `new`, `contacted`, `completed`, `replied`, `blocked`, `failed`, `suspended`, `deleted`, `opted_out`, `unknown`. |
| `external_id` | null | Зарезервировано для следующих версий. В v1 всегда `null`. |
| `fields` | object | Разрешенные поля карточки контакта. |
| `fields.record_id` | string или null | Идентификатор записи во внешней CRM, переданный при загрузке контактов. `null`, если `record_id_status` не `valid`. Может отсутствовать. |
| `fields.first_name` | string или null | Имя. Может отсутствовать. |
| `fields.last_name` | string или null | Фамилия. Может отсутствовать. |
| `fields.company` | string или null | Компания. Может отсутствовать. |
| `fields.position` | string или null | Должность. Может отсутствовать. |
| `fields.website` | string или null | Сайт. Может отсутствовать. |
| `fields.source` | string или null | Источник контакта. Может отсутствовать. |
| `fields.utm_source` | string или null | Метка `utm_source`. Может отсутствовать. |
| `fields.utm_medium` | string или null | Метка `utm_medium`. Может отсутствовать. |
| `fields.utm_campaign` | string или null | Метка `utm_campaign`. Может отсутствовать. |
| `fields_truncated` | boolean | `true`, если часть значений полей сокращена или скрыта из-за ограничений размера. |
| `record_id_status` | string | Насколько надежен `fields.record_id`: `valid`, `missing`, `conflict` или `unknown`. Значения: `valid`, `missing`, `conflict`, `unknown`. |
| `first_contact_account` | object или null | Аккаунт, который отправил первое сообщение. `null`, если неизвестно. |
| `first_contact_account.id` | string (uuid) | Идентификатор аккаунта. |
| `first_contact_account.username` | string или null | Ник аккаунта без `@`. Может быть `null`. |
| `first_contact_account.name` | string или null | Отображаемое имя аккаунта. Может быть `null`. |
| `first_message_sent_at` | string (date-time) или null | Когда отправлено первое сообщение. `null`, если отправка не подтверждена. |
| `first_reply_at` | string (date-time) или null | Когда пришел первый ответ. `null`, если ответа не было. |
| `delivery_state` | string | Состояние доставки ближайшего или последнего сообщения цепочки. Значения: `waiting_queue`, `queued`, `held`, `sent`, `failed`, `unknown`. |
| `automation_state` | string | Состояние автоматической цепочки для этого лида. У лидов без запланированного сообщения, например ответивших, сейчас часто `unknown`. Значения: `running`, `paused`, `held`, `stopped`, `completed`, `unknown`. |
| `reason` | object | Почему цепочка ждет, если это известно. |
| `reason.reason_code` | string | Код причины. Значения: `none`, `schedule_window`, `daily_limit`, `queue_pending`, `telegram_wait`, `account_unavailable`, `account_restricted`, `campaign_paused`, `subscription_inactive`, `manual_hold`, `automation_stopped`, `service_unavailable`, `unknown`. |
| `reason.reason_observed_at` | string (date-time) или null | Когда причина была зафиксирована. `null`, если неизвестно. |
| `reason.next_attempt_at` | string (date-time) или null | Когда ожидается следующая попытка. `null`, если срок неизвестен. |
| `reason.requires_customer_action` | boolean или null | Нужны ли действия в кабинете, чтобы отправка продолжилась. `null`, если неизвестно. |
| `created_at` | string (date-time) | Когда контакт добавлен в кампанию. |
| `observed_at` | string (date-time) | Время чтения данных сервером. |

Пример:

```json
{
  "object": "lead",
  "id": "5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31",
  "workspace_id": "3f6a9d21-8b4c-4e7f-a102-5c9e8d7b6a43",
  "campaign_id": "4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14",
  "username": "example_user",
  "status": "replied",
  "external_id": null,
  "fields": {
    "record_id": "crm-10482",
    "first_name": "Анна",
    "company": "ООО Пример",
    "position": "Руководитель отдела продаж",
    "utm_source": "partner"
  },
  "fields_truncated": false,
  "record_id_status": "valid",
  "first_contact_account": {
    "id": "1d9f6b28-4c3e-4a75-8b19-5e7a2c0f6d83",
    "username": "example_sender",
    "name": "Мария из Примера"
  },
  "first_message_sent_at": "2026-10-26T07:30:00.000Z",
  "first_reply_at": "2026-10-26T09:12:00.000Z",
  "delivery_state": "sent",
  "automation_state": "unknown",
  "reason": {
    "reason_code": "none",
    "reason_observed_at": null,
    "next_attempt_at": null,
    "requires_customer_action": null
  },
  "created_at": "2026-10-25T14:00:00.000Z",
  "observed_at": "2026-10-27T09:15:42.000Z"
}
```

## Список лидов

`GET /v1/leads`

Лиды (участия контактов в кампаниях) в охвате ключа, от старых к новым по `created_at`.

Право: `leads:read`. Вес запроса: 2.

### Параметры

| Параметр | Где | Тип | Обязательный | Описание |
|---|---|---|---|---|
| `limit` | строка запроса | integer | нет | Сколько объектов вернуть на странице. От 1 до 100. По умолчанию `50`. |
| `cursor` | строка запроса | string | нет | Значение `next_cursor` из предыдущего ответа. Остальные фильтры должны совпадать с первым запросом. |
| `workspace_id` | строка запроса | string (uuid) | нет | Только объекты этого пространства. |
| `campaign_id` | строка запроса | string (uuid) | нет | Только объекты этой кампании. |
| `username` | строка запроса | string | нет | Точный ник Telegram. Знак `@` в начале и регистр не важны. |
| `status` | строка запроса | string | нет | Только лиды с этим статусом. Значения: `new`, `contacted`, `completed`, `replied`, `blocked`, `failed`, `suspended`, `deleted`, `opted_out`, `unknown`. |
| `delivery_state` | строка запроса | string | нет | Только лиды с этим состоянием доставки. Значения `waiting_queue` и `failed` сейчас не выдаются, фильтр по ним вернет пустой список. Значения: `waiting_queue`, `queued`, `held`, `sent`, `failed`, `unknown`. |

### Ответ

Список: `object`, `data`, `has_more`, `next_cursor`. Пагинация: https://docs.melioralab.io/md/pagination.md

### Особые ошибки

- `cursor_mismatch` (400): Курсор поврежден, выдан для другого маршрута или фильтров, либо права и охват ключа изменились.
- `scope_required` (403): Ключ не имеет права на это чтение. Для текстов нужен также включенный «Текст сообщений».
- `not_found` (404): Объект не найден или находится вне охвата ключа. Неизвестный путь тоже дает 404.
- `cursor_expired` (410): Срок курсора истек: 15 минут для списков, 30 суток для ленты событий.

Общие ошибки: https://docs.melioralab.io/md/errors.md

### Пример запроса

```bash
curl -G https://api.melioralab.io/v1/leads \
  -H "Authorization: Bearer $MELIORA_API_KEY" \
  -d campaign_id=4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14 \
  -d limit=50
```

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

```json
{
  "object": "list",
  "data": [
    {
      "object": "lead",
      "id": "5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31",
      "workspace_id": "3f6a9d21-8b4c-4e7f-a102-5c9e8d7b6a43",
      "campaign_id": "4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14",
      "username": "example_user",
      "status": "replied",
      "external_id": null,
      "fields": {
        "record_id": "crm-10482",
        "first_name": "Анна",
        "company": "ООО Пример",
        "position": "Руководитель отдела продаж",
        "utm_source": "partner"
      },
      "fields_truncated": false,
      "record_id_status": "valid",
      "first_contact_account": {
        "id": "1d9f6b28-4c3e-4a75-8b19-5e7a2c0f6d83",
        "username": "example_sender",
        "name": "Мария из Примера"
      },
      "first_message_sent_at": "2026-10-26T07:30:00.000Z",
      "first_reply_at": "2026-10-26T09:12:00.000Z",
      "delivery_state": "sent",
      "automation_state": "unknown",
      "reason": {
        "reason_code": "none",
        "reason_observed_at": null,
        "next_attempt_at": null,
        "requires_customer_action": null
      },
      "created_at": "2026-10-25T14:00:00.000Z",
      "observed_at": "2026-10-27T09:15:42.000Z"
    },
    {
      "object": "lead",
      "id": "8c1e5a37-9d2f-4b74-a6e8-2f9b3c7d5a40",
      "workspace_id": "3f6a9d21-8b4c-4e7f-a102-5c9e8d7b6a43",
      "campaign_id": "4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14",
      "username": "sample_contact",
      "status": "contacted",
      "external_id": null,
      "fields": {
        "record_id": null,
        "first_name": "Игорь"
      },
      "fields_truncated": false,
      "record_id_status": "missing",
      "first_contact_account": {
        "id": "1d9f6b28-4c3e-4a75-8b19-5e7a2c0f6d83",
        "username": "example_sender",
        "name": "Мария из Примера"
      },
      "first_message_sent_at": "2026-10-26T07:42:00.000Z",
      "first_reply_at": null,
      "delivery_state": "queued",
      "automation_state": "running",
      "reason": {
        "reason_code": "queue_pending",
        "reason_observed_at": "2026-10-27T08:58:10.000Z",
        "next_attempt_at": "2026-10-27T10:00:00.000Z",
        "requires_customer_action": false
      },
      "created_at": "2026-10-25T14:00:01.000Z",
      "observed_at": "2026-10-27T09:15:42.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "v1.k1.Qm9hcmRfMWZ4.c3RyaWN0bHlfb3BhcXVl.dGFnX2V4YW1wbGU.bWFjX2V4YW1wbGVfdmFsdWU"
}
```

## Получить лида

`GET /v1/leads/{id}`

Один лид по идентификатору. Лид вне охвата ключа дает 404.

Право: `leads:read`. Вес запроса: 1.

### Параметры

| Параметр | Где | Тип | Обязательный | Описание |
|---|---|---|---|---|
| `id` | путь | string (uuid) | да | Идентификатор лида. |

### Ответ

Объект `Lead`.

### Особые ошибки

- `scope_required` (403): Ключ не имеет права на это чтение. Для текстов нужен также включенный «Текст сообщений».
- `not_found` (404): Объект не найден или находится вне охвата ключа. Неизвестный путь тоже дает 404.

Общие ошибки: https://docs.melioralab.io/md/errors.md

### Пример запроса

```bash
curl https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31 \
  -H "Authorization: Bearer $MELIORA_API_KEY"
```

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

```json
{
  "object": "lead",
  "id": "5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31",
  "workspace_id": "3f6a9d21-8b4c-4e7f-a102-5c9e8d7b6a43",
  "campaign_id": "4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14",
  "username": "example_user",
  "status": "replied",
  "external_id": null,
  "fields": {
    "record_id": "crm-10482",
    "first_name": "Анна",
    "company": "ООО Пример",
    "position": "Руководитель отдела продаж",
    "utm_source": "partner"
  },
  "fields_truncated": false,
  "record_id_status": "valid",
  "first_contact_account": {
    "id": "1d9f6b28-4c3e-4a75-8b19-5e7a2c0f6d83",
    "username": "example_sender",
    "name": "Мария из Примера"
  },
  "first_message_sent_at": "2026-10-26T07:30:00.000Z",
  "first_reply_at": "2026-10-26T09:12:00.000Z",
  "delivery_state": "sent",
  "automation_state": "unknown",
  "reason": {
    "reason_code": "none",
    "reason_observed_at": null,
    "next_attempt_at": null,
    "requires_customer_action": null
  },
  "created_at": "2026-10-25T14:00:00.000Z",
  "observed_at": "2026-10-27T09:15:42.000Z"
}
```
