GET /api/agent/crm/leads/{leadId}/booking-intents — Список намерений лида
Агент (или руководитель агентства), ведущий лид, открывает экран «объекты клиента» в карточке лида и получает список объектов-намерений: что инвестор хочет купить/забронировать и что агент ему предложил. Каждая строка показывает состояние намерения — сторону-инициатора, подтверждено ли инвестором, есть ли сделка. Дом полей — lead.md.
Назначение
Отдать агенту/руководителю агентства список намерений лида с фильтром по статусу, стороне и признаку
подтверждения. По умолчанию — активные намерения. Каждая строка несёт объект, застройщика, сторону
инициатора, флаг ConfirmedByInvestor и ссылку на сделку (если заведена) — этого достаточно, чтобы агент
понимал, по каким объектам можно двигать сделку.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | agent (JWT агента/руководителя агентства) |
| Feature group | booking-intents |
| Статус | draft |
| Документ | docs/06-services/crm/api/agent/list-booking-intents.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Агент или Руководитель агентства); Operator (Менеджер ПИН) |
| Auth policy / Permissions | crm.leads.view; инвестор этот метод не вызывает (у него свой список в карточке лида) |
| Scope (RBAC) | организация (агентство) — actor.CompanyId = lead.TenantId |
| Record-level | Актор — участник лида (обычно Executor) либо руководитель компании-владельца (AgencyCompanyId = lead.TenantId) / Менеджер ПИН |
| Tenant isolation | намерения лида в tenant агентства (lead.TenantId = AgencyCompanyId); выборка ограничена LeadId в границах доступа к лиду |
| Audit | read (собственные данные агентства; не sensitive) |
Список видит участник лида (обычно Executor) либо руководитель компании-владельца агентства
(AgencyCompanyId = lead.TenantId), либо Менеджер ПИН (platform-scope, сквозной доступ). Сам инвестор этот
метод не вызывает — у него отдельный список намерений в своей карточке лида.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/agent/crm/leads/{leadId}/booking-intents |
| Success status | 200 |
| Idempotency header | не применяется (безопасный read) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
LeadId | route | Guid | Да | != Guid.Empty; существующий лид в границах доступа актора | — |
Status | query | BookingIntentStatus? | Нет | enum строкой; неизвестное значение → VALIDATION_FAILED; default — Active | — |
Source | query | BookingIntentSource? | Нет | enum строкой InvestorAdded/AgentProposed; фильтр по стороне | — |
ConfirmedByInvestor | query | bool? | Нет | фильтр по подтверждению инвестором | — |
ActorUserId | identity claims | Guid | Да | JWT; record-level по лиду | — |
ActorAgencyCompanyId | identity claims | Guid | Да | != Guid.Empty; сверяется с lead.TenantId | Tenant isolation |
Список короткий (намерения одного лида), пагинация не требуется; сортировка — CreatedAt desc.
Известное значение фильтра (Status/Source/ConfirmedByInvestor), под которое нет данных, возвращает
пустой Items, а не ошибку. Ошибку даёт только неизвестное (не входящее в enum) значение Status/Source.
Модель ответа — список BookingIntentListItemModel
Контейнер: Items: IReadOnlyList<BookingIntentListItemModel> (сортировка CreatedAt desc).
BookingIntentListItemModel
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
IntentId | Guid | Да | LeadBookingIntentEntity.Id | — | Идентификатор намерения. |
PropertyObjectId | Guid | Да | PropertyObjectId | — | Объект намерения; карточка/цена резолвятся фронтом из chessboard. |
ProjectId | Guid | Да | ProjectId (snapshot) | — | Проект объекта. |
DeveloperCompanyId | Guid | Да | DeveloperCompanyId (snapshot) | — | Застройщик объекта. |
Source | string (enum BookingIntentSource) | Да | Source | — | InvestorAdded / AgentProposed. |
ConfirmedByInvestor | bool | Да | ConfirmedByInvestor | — | Подтверждено ли инвестором. |
ConfirmedAt | DateTimeOffset? | Нет | ConfirmedAt | — | Момент подтверждения. |
Status | string (enum BookingIntentStatus) | Да | Status | — | Active / Cancelled. |
DealId | Guid? | Нет | DealId | — | Сделка по намерению (если заведена). |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Момент добавления (ключ сортировки). |
RemovedAt | DateTimeOffset? | Нет | RemovedAt | — | Момент снятия (для Cancelled). |
Алгоритм
- Контекст и доступ. Операцию выполняет агент/руководитель агентства (permission
crm.leads.view); якорь —ActorAgencyCompanyId. - Проверка входа и предусловий.
- формат:
LeadId != Guid.Empty;Status/Source— известные значения enum, иначеVALIDATION_FAILED; - лид существует → иначе
CRM_LEAD_NOT_FOUND; - лид принадлежит агентству актора и актор — участник лида / руководитель / Менеджер ПИН → иначе
CRM_LEAD_FORBIDDEN.
- формат:
- Что читаем. Намерения по
LeadIdс фильтрамиStatus(defaultActive),Source,ConfirmedByInvestor, если заданы; сортировкаCreatedAt desc. - Результат. Список
Items(проекция по таблице выше) → HTTP200. Несуществующие значения фильтров дают пустой список, не ошибку.
Диаграмма
Ошибки и failure modes
CRM_LEAD_NOT_FOUND (лид не существует) и CRM_LEAD_FORBIDDEN (лид другого агентства/агента) — разные коды
(404/403), не маскируются под общий отказ.
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | LeadId == Guid.Empty, неизвестный Status/Source | «Проверьте параметры запроса» | no retry |
UNAUTHORIZED | 401 | нет/невалидный JWT или нет crm.leads.view | «Требуется авторизация» | no retry |
CRM_LEAD_NOT_FOUND | 404 | лид не найден | «Лид не найден» | no retry |
CRM_LEAD_FORBIDDEN | 403 | лид другого агентства/агента | «Нет доступа к лиду» | no retry |
Acceptance Criteria
- Happy path. Given участник лида без фильтров; When
GET; Then200— активные намерения лида, сортировкаCreatedAt desc. - Фильтр по стороне. Given
Source=AgentProposed; WhenGET; Then только предложенные агентом намерения. - Фильтр по подтверждению. Given
ConfirmedByInvestor=false; WhenGET; Then только неподтверждённые (ждут инвестора). - Снятые. Given
Status=Cancelled; WhenGET; Then только снятые намерения (сRemovedAt). - Чужой лид. Given лид другого агентства; When
GET; Then403 CRM_LEAD_FORBIDDEN. - Валидация. Given неизвестный
Status; WhenGET; Then400 VALIDATION_FAILED.
Совместимость и наблюдаемость
- Новые optional фильтры/поля ответа — additive; сужение enum
BookingIntentStatus/BookingIntentSource— breaking (migration клиента + rollout); неизвестный член enum на клиенте — по правилу unknown-enum. - Logs:
booking-intents listed(LeadId,ActorUserId, count); metrics:crm_booking_intents_list_total, latency p95 (SLO списков); traces — сквозной trace черезtraceparent; dashboardpin-crm.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (3): DELETE /api/investor/crm/leads/{leadId}/booking-intents/{intentId} — Инвестор снимает объект-намерение, POST /api/agent/crm/leads/{leadId}/booking-intents — Агент предлагает объект клиенту, POST /api/investor/crm/leads/{leadId}/booking-intents — Инвестор добавляет объект-намерение
- Use Cases (1): UC-CRM-016. Инвестор добавляет намерения бронирования (много объектов)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность (дом полей, INV-INTENT-1, enum-ы): lead.md; лид — lead.md.
- Методы записи намерений: propose-booking-intent.md, add-booking-intent.md, confirm-booking-intent.md, remove-booking-intent.md.
- Карта области — README. ADR: 0071 company-ownership и участники.