
## Привязать запись

`POST /v1/timeline-logs/:id/bind`

Привязывает существующую лог-запись к дополнительной CRM-сущности — в её таймлайне появится та же запись. Используется, когда одно событие должно быть видно сразу в нескольких карточках: сделке, контакте, компании.

## Параметры

| Параметр | Тип | Обяз. | Описание |
|----------|-----|:-----:|---------|
| `id` (path) | number | да | ID лог-записи |

## Поля запроса (body)

| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|---------|
| **`entityId`** | number | да | ID сущности, к которой привязываем |
| **`entityTypeId`** | number | да* | Числовой тип CRM-сущности. См. [Типы сущностей](/docs/timeline-logs#типы-родительских-сущностей) |
| **`entityType`** | string | да* | Строковый код типа: `"lead"`, `"deal"`, `"contact"`, `"company"`, `"quote"`, `"order"`, `"smart_invoice"`, `"dynamic_<entityTypeId>"` |

*Передайте **либо** `entityTypeId`, **либо** `entityType` — Вайбкод сам подставит нужное в B24-вызов. Если переданы оба, `entityType` имеет приоритет.

## Поддерживаемые типы

| `entityTypeId` | `entityType` |
|:---:|---|
| 1 | `lead` |
| 2 | `deal` |
| 3 | `contact` |
| 4 | `company` |
| 7 | `quote` |
| 14 | `order` |
| 31 | `smart_invoice` |
| ≥ 128 | `dynamic_<entityTypeId>` (произвольный смарт-процесс) |

Любой `entityTypeId` < 128, отсутствующий в таблице, возвращает `400 INVALID_ENTITY_TYPE_ID`. Список смарт-процессов портала — [`GET /v1/smart-processes`](/docs/entities/smart-processes/list).

## Примеры

### curl — личный ключ

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/timeline-logs/5012/bind \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "entityTypeId": 3, "entityId": 50 }'
```

### curl — OAuth-приложение

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/timeline-logs/5012/bind \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "entityTypeId": 3, "entityId": 50 }'
```

### JavaScript — личный ключ

```javascript
// Привязать к контакту
const res = await fetch('https://vibecode.bitrix24.tech/v1/timeline-logs/5012/bind', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ entityTypeId: 3, entityId: 50 }),
})

const { success } = await res.json()

// Альтернатива — указать тип строкой (полезно для смарт-процессов)
await fetch('https://vibecode.bitrix24.tech/v1/timeline-logs/5012/bind', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ entityType: 'dynamic_174', entityId: 1 }),
})
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/timeline-logs/5012/bind', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ entityTypeId: 3, entityId: 50 }),
})

const { success } = await res.json()
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|---------|
| `success` | boolean | Всегда `true` при успехе |
| `data.bound` | boolean | Всегда `true` — подтверждение привязки |

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

```json
{
  "success": true,
  "data": { "bound": true }
}
```

## Пример ответа при ошибке

400 — `entityTypeId` без отображения на `ENTITY_TYPE`:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_ENTITY_TYPE_ID",
    "message": "entityTypeId=99 has no ENTITY_TYPE mapping. Use 1=lead, 2=deal, 3=contact, 4=company, 7=quote, 14=order, 31=smart_invoice, or ≥128 for smart-processes."
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 400 | `INVALID_PARAMS` | Не передан `entityId` или ни `entityTypeId`, ни `entityType` |
| 400 | `INVALID_ENTITY_TYPE_ID` | `entityTypeId` < 128 и нет в таблице маппинга |
| 422 | `BITRIX_ERROR` | Битрикс24 отклонил запрос (например, `entityType` неизвестен или сущность не существует) |
| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |
| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |

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

## Известные особенности

**Родительская сущность уже привязана автоматически.** При создании лог-записи через [`POST /v1/timeline-logs`](/docs/timeline-logs/logs/create) для `entityTypeId+entityId` запись уже считается привязанной к этой паре. `bind` нужен только для **дополнительных** сущностей.

**Регистр строкового `entityType` не важен.** Вайбкод приводит к нижнему регистру — `"DEAL"`, `"Deal"`, `"deal"` дают одинаковый результат. В ответах [`bindings.list`](/docs/timeline-logs/bindings/list) тип всегда возвращается в нижнем регистре.

**Привязка к одной и той же паре идемпотентна на уровне B24.** Повторный `bind` к уже привязанной сущности — операция отдаст `200 + {bound: true}` без ошибки. В списке привязок дубликат не появится.

**Нельзя привязать к произвольному типу.** Если в `entityType` передать что-то отсутствующее в B24-словаре (`"random"`, `"foo"`), B24 вернёт `ENTITY_TYPE is not defined or invalid.` — Вайбкод пробросит это как `422 BITRIX_ERROR`. Проверка списка типов на стороне Вайбкод идёт только по `entityTypeId`-маппингу.

**Не валидирует существование `:id` лог-записи.** Если в пути передать `id` несуществующей лог-записи — `bind` всё равно вернёт `200 + {bound: true}` без ошибки. Привязка не создастся, потому что нет источника. Перед `bind` стоит убедиться, что лог-запись существует ([`GET /v1/timeline-logs/:id`](/docs/timeline-logs/logs/get)) или использовать `id`, только что вернувшийся из [`POST /v1/timeline-logs`](/docs/timeline-logs/logs/create).

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

- [Отвязать](/docs/timeline-logs/bindings/unbind)
- [Список привязок](/docs/timeline-logs/bindings/list)
- [Создать запись](/docs/timeline-logs/logs/create)
