# Связанные данные (include)

Загрузка связанных сущностей вместе с основными записями в одном запросе. Вместо отдельных вызовов для получения контакта, компании или ответственного — передайте параметр `include`, и Вайбкод вернёт всё в поле `_included`.

> Параметр `include` опциональный и работает в трёх местах:
> - `GET /v1/{entity}/{id}?include=...` — получение одной записи
> - `GET /v1/{entity}?include=...` — получение списка
> - `POST /v1/{entity}/search` — поиск (`include` в теле запроса как массив строк)

## Как использовать

Перечислите имена связей через запятую:

### curl

```bash
curl "https://vibecode.bitrix24.tech/v1/deals/741?include=contact,company" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### JavaScript

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/deals/741?include=contact,company', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { data } = await res.json()

// data._included.contacts — массив контактов сделки
// data._included.company  — компания (объект или null)
```

Связанные данные появятся в поле `_included` каждой записи:

```json
{
  "success": true,
  "data": {
    "id": 741,
    "title": "Поставка оборудования",
    "contactId": 71,
    "companyId": 15,
    "_included": {
      "contacts": [
        {
          "id": 71,
          "name": "Иван",
          "lastName": "Петров",
          "phone": [{ "VALUE": "+79161234567" }],
          "isPrimary": true,
          "sort": 10
        }
      ],
      "company": {
        "id": 15,
        "title": "ООО Ромашка",
        "industry": "IT"
      }
    }
  }
}
```

## Два типа связей

### Один-к-одному

Сущность содержит поле-ссылку на связанный объект (например, `companyId` у контакта). Результат — один объект или `null`.

```
GET /v1/leads/50?include=contact
```

```json
{
  "data": {
    "id": 50,
    "title": "Заявка с сайта",
    "contactId": 71,
    "_included": {
      "contact": {
        "id": 71,
        "name": "Иван",
        "lastName": "Петров"
      }
    }
  }
}
```

Если поле-ссылка пустое или равно 0, возвращается `null`:

```json
{
  "_included": {
    "contact": null
  }
}
```

### Много-ко-многим

Связь через промежуточную таблицу. Результат — массив объектов с дополнительными полями связи.

```
GET /v1/deals/741?include=contact
```

```json
{
  "_included": {
    "contacts": [
      {
        "id": 71,
        "name": "Иван",
        "lastName": "Петров",
        "isPrimary": true,
        "sort": 10,
        "roleId": 0
      },
      {
        "id": 85,
        "name": "Мария",
        "lastName": "Сидорова",
        "isPrimary": false,
        "sort": 20,
        "roleId": 0
      }
    ]
  }
}
```

Обратите внимание: имя связи в запросе — `contact` (ед. число), а в ответе — `contacts` (мн. число). Вайбкод автоматически плюрализует имя для массивов.

Дополнительные поля связи (`isPrimary`, `sort`, `roleId`) добавляются к каждому объекту — их значения приходят из промежуточной таблицы, а не из самой сущности.

## Использование в списках и поиске

### curl — список

```bash
curl "https://vibecode.bitrix24.tech/v1/deals?limit=10&include=company" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### JavaScript — список

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/deals?limit=10&include=company', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { data, meta } = await res.json()

for (const deal of data) {
  console.log(deal.title, '—', deal._included.company?.title ?? 'без компании')
}
// meta.includeSkipped === true, если записей > 200
```

Ответ:

```json
{
  "success": true,
  "data": [
    {
      "id": 741,
      "title": "Поставка оборудования",
      "companyId": 15,
      "_included": {
        "company": { "id": 15, "title": "ООО Ромашка" }
      }
    },
    {
      "id": 742,
      "title": "Техподдержка",
      "companyId": 15,
      "_included": {
        "company": { "id": 15, "title": "ООО Ромашка" }
      }
    }
  ],
  "meta": { "total": 150, "hasMore": true }
}
```

Вайбкод дедуплицирует запросы: если несколько записей ссылаются на одну компанию, она загружается один раз.

### curl — поиск

В поиске `include` передаётся в теле запроса как массив.

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/deals/search" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": { "stageId": "NEW", "amount": { "$gte": 100000 } },
    "include": ["contact", "company"],
    "limit": 50
  }'
```

### JavaScript — поиск

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/deals/search', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    filter: { stageId: 'NEW', amount: { $gte: 100000 } },
    include: ['contact', 'company'],
    limit: 50,
  }),
})
const { data } = await res.json()
```

## Несколько связей в одном запросе

Можно запросить до 3 связей одновременно:

```
GET /v1/quotes/18?include=deal,contact,company
```

```json
{
  "_included": {
    "deal": { "id": 741, "title": "Поставка оборудования" },
    "contact": { "id": 71, "name": "Иван", "lastName": "Петров" },
    "company": { "id": 15, "title": "ООО Ромашка" }
  }
}
```

## Лимиты

| Параметр | Значение |
|----------|---------|
| Максимум связей в запросе | 3 |
| Максимум записей для include | 200 |

Если в списке или поиске больше 200 записей, include **пропускается** и в `meta` добавляется флаг:

```json
{
  "data": [ ... ],
  "meta": {
    "hasMore": true,
    "includeSkipped": true
  }
}
```

Чтобы получить связанные данные для больших выборок — уменьшите `limit` до 200 или меньше.

## Доступные связи по сущностям

Узнать доступные include для конкретной сущности можно через эндпоинт полей:

```bash
curl "https://vibecode.bitrix24.tech/v1/deals/fields" \
  -H "X-Api-Key: YOUR_API_KEY"
```

В ответе будет поле `include` со списком доступных связей:

```json
{
  "success": true,
  "data": {
    "fields": { ... },
    "include": ["contact", "company"]
  }
}
```

Доступные связи зависят от сущности. Какие именно include поддерживает конкретная сущность — указано в документации каждого метода и в ответе `GET /v1/{entity}/fields` (поле `include`).

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 400 | `INCLUDE_LIMIT_EXCEEDED` | Запрошено более 3 связей |
| 400 | `INVALID_INCLUDE` | Связь не найдена. Ответ содержит список доступных связей |
| 403 | `SCOPE_DENIED` | У ключа нет права на связанную сущность |

Если у сущности вообще нет связей (например, `departments`), любой `include` вернёт `INVALID_INCLUDE` с пустым списком `Available:`.

### Права

`include` не расширяет полномочия ключа. Право проверяется у обеих сторон связи: у сущности, которую вы читаете, и у той, которую достаёт связь. Стороны связи не всегда требуют одного права — например, `responsible` у задач и `owner` у рабочих групп достают сотрудника и потому требуют `user`, а `catalog` у разделов товаров требует `catalog`. Не хватает права у второй стороны — запрос отклоняется целиком с `SCOPE_DENIED`, и в тексте названо и недостающее право, и связь, из-за которой оно понадобилось.

Связи внутри одного права дополнительного права не требуют.

Пример ошибки:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_INCLUDE",
    "message": "Unknown include 'manager'. Available: contact, company, quote"
  }
}
```

Пример отказа по правам:

```json
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "Include 'owner' reads users and requires 'user' scope"
  }
}
```

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

- [Entity API](./entity-api.md) — CRUD-операции, пагинация, агрегация
- [Фильтрация](./filtering.md) — три синтаксиса фильтров
- [Batch API](./batch.md) — объединение вызовов
- [Справочник API](./api-reference.md) — полный список сущностей
- [Коды ошибок](./errors.md) — справочник ошибок платформы
