> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hubtalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Отдельные звонки

> `POST /v1/calls/phone`, объект звонка, чтение, завершение и список звонков.

# Отдельные звонки

## `POST /v1/calls/phone`

Один исходящий звонок: платформа регистрирует звонок, сразу отвечает и набирает номер из устойчивой очереди набора через указанный транк, подключая опубликованного агента, когда абонент ответит.

| Поле                | Тип            | Обяз. | По умолч. | Описание                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ------------------- | -------------- | ----- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_id`          | string         | да    | —         | Имя агента. Должен быть **опубликован** и перечислен в `agents` используемой исходящей группы.                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `trunk_id`          | string         | да    | —         | Транк, через который набирать.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `to_number`         | string         | да    | —         | Номер назначения; нормализуется перед набором.                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `route_id`          | string         | нет   | `""`      | Исходящая группа транка. Пусто → разрешается из пары (`trunk_id`, `agent_id`): агент, привязанный ровно к одной группе транка, выбирает её; несколько → `409 agent_subgroup_ambiguous`; ни одной → `403 agent_not_in_subgroup`. Неизвестный `route_id` → `404 subgroup_not_found`.                                                                                                                                                                                                                              |
| `from_number`       | string         | нет   | `""`      | Номер, с которого звоним; должен быть одним из `caller_numbers` **группы**. Пустая строка отдаёт выбор номера группе — по её политике исходящего номера: `random` (по умолчанию) берёт случайный, `round_robin` обходит пул по кругу. Кандидаты — пул группы, пересечённый с пулом гранта организации; остался один номер — он и будет выбран. Группа с пустым пулом отклоняет любое значение, включая пустое (`caller_number_not_allowed`); пустое пересечение с грантом — `caller_number_outside_grant_pool`. |
| `dynamic_variables` | object         | нет   | `{}`      | Переменные, передаваемые в сценарий агента (имя клиента, данные счёта).                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `metadata`          | object         | нет   | `{}`      | Ваша непрозрачная нагрузка для сопоставления. Возвращается без изменений в объекте звонка и в **каждом** вебхуке — кладите сюда идентификатор лида.                                                                                                                                                                                                                                                                                                                                                             |
| `ring_duration`     | number \| null | нет   | `null`    | Ограничение времени дозвона в секундах; должно быть > 0. `null` — значение агента по умолчанию.                                                                                                                                                                                                                                                                                                                                                                                                                 |

**Нормализация номера.** Из `to_number` убираются все нецифровые символы (`"+1 (555) 123-4567"` → `"15551234567"`). Номер без цифр отклоняется с `400`. Если **группа** задаёт префикс набора, он добавляется к нормализованному номеру; разрешённые направления проверяются по нормализованному номеру **до** префикса.

**Порядок проверок.** Потолок очереди набора (`429 dial_queue_full`) проверяется первым, до всякой валидации. Затем синхронно и до регистрации звонка: транк и грант (404), группа и привязка агента (404/409/403), номер звонящего (400), политика направлений (403), лимиты группы (429), `ring_duration` (400) и в конце — опубликованная версия агента (409). Отказ на любом из этих шагов не оставляет звонка. Поскольку лимиты проверяются до проверки публикации, `429` не говорит вам, опубликован ли агент.

Заголовок `Idempotency-Key` здесь рекомендуется (и фактически обязателен после `429`): повтор с тем же ключом возвращает **уже созданный звонок** вместо второго набора. Ключи помнятся сутки и ограничены вашим API-ключом и эндпоинтом. Переигровка возвращает объект звонка **в том виде, в котором он был при создании** (`status: "queued"`), а не его текущее состояние — для этого опрашивайте `GET /v1/calls/{call_id}`.

<Note>
  **Успех — `202 Accepted`, а не `201`.** Звонок зарегистрирован и поставлен в очередь набора; сам набор происходит асинхронно после ответа. Сохраните `call_id`: это ключ сопоставления для опроса и для каждого вебхука.
</Note>

**Ошибки инициации звонка**

| Статус | `error.code`                             | Условие                                                                                                                                                                                                                                |
| ------ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `caller_number_not_allowed`              | `from_number` не из номеров группы                                                                                                                                                                                                     |
| 400    | `caller_number_outside_grant_pool`       | `from_number` вне пула, выданного вашей организации (только транки по гранту)                                                                                                                                                          |
| 400    | `destination_number_invalid`             | в `to_number` нет цифр                                                                                                                                                                                                                 |
| 400    | `ring_duration_invalid`                  | `ring_duration` ≤ 0                                                                                                                                                                                                                    |
| 403    | `agent_not_in_subgroup`                  | агент не привязан к группе (это же ответ и на несуществующее имя агента)                                                                                                                                                               |
| 403    | `destination_not_allowed`                | направление заблокировано политикой группы                                                                                                                                                                                             |
| 404    | `trunk_not_found` / `subgroup_not_found` | неизвестный `trunk_id` (или нет гранта на него) / неизвестный `route_id`                                                                                                                                                               |
| 409    | `agent_subgroup_ambiguous`               | агент в нескольких группах транка — пришлите `route_id`                                                                                                                                                                                |
| 409    | `agent_not_published`                    | у агента нет опубликованной версии                                                                                                                                                                                                     |
| 422    | `invalid_request`                        | тело не проходит проверку схемы; детали по полям — в `message`                                                                                                                                                                         |
| 502    | `dial_failed`                            | только если оператор отключил очередь набора (синхронный запасной режим). В обычном асинхронном режиме сбой набора — **не** HTTP-ошибка: звонок создан (`202`) и позже читается как `status: "failed"`, `status_reason: "dial_failed"` |

## Объект звонка

Приходит в `POST /v1/calls/phone`, `GET /v1/calls/{call_id}` и `POST /v1/calls/{call_id}/end`.

| Поле                                               | Тип                      | Описание                                                                                                                                                                                                                                                                                                                                                                                                                            |
| -------------------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `call_id`                                          | string                   | Идентификатор звонка (`call-out-*` исходящий, `call-in-*` входящий, `web-*` веб).                                                                                                                                                                                                                                                                                                                                                   |
| `agent_id`                                         | string                   | Имя агента.                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `flow_version`                                     | string \| number \| null | Отметка версии опубликованного сценария, на котором шёл звонок. **Тип не стабилен**: пока звонок живой — непрозрачный отпечаток-строка; после сохранения — целочисленный номер версии. Считайте непрозрачным, а для идентичности используйте `flow_version_id`.                                                                                                                                                                     |
| `flow_version_id`                                  | number \| null           | Числовой идентификатор опубликованной версии.                                                                                                                                                                                                                                                                                                                                                                                       |
| `channel`                                          | string                   | `phone` \| `web`.                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `direction`                                        | string                   | `outbound` \| `inbound` \| `web`.                                                                                                                                                                                                                                                                                                                                                                                                   |
| `status`                                           | string                   | Живое состояние: `queued` → `ringing` → `ongoing` → `ended` \| `failed`.                                                                                                                                                                                                                                                                                                                                                            |
| `call_status`                                      | string                   | Финальная корзина: `completed` \| `not_connected` \| `error`; `""` до финализации.                                                                                                                                                                                                                                                                                                                                                  |
| `status_reason`                                    | string                   | Уточнённая причина внутри корзины; `""` до финализации.                                                                                                                                                                                                                                                                                                                                                                             |
| `error_code`                                       | string \| null           | Код ошибки оператора связи или телефонии, если платформа его получила; иначе `null`. Диагностическое.                                                                                                                                                                                                                                                                                                                               |
| `message_left`                                     | boolean                  | Сработало определение автоответчика и агент оставил сообщение.                                                                                                                                                                                                                                                                                                                                                                      |
| `started_at`                                       | number \| null           | Начало попытки (для телефонных звонков — до набора).                                                                                                                                                                                                                                                                                                                                                                                |
| `answered_at`                                      | number \| null           | Момент, когда абонент ответил; `null` для недозвонов и веб-звонков.                                                                                                                                                                                                                                                                                                                                                                 |
| `media_started_at`                                 | number \| null           | Когда пошёл аудиопоток. Диагностическое.                                                                                                                                                                                                                                                                                                                                                                                            |
| `talk_from`, `talk_to`                             | number \| null           | Границы интервала разговора, по которому считается `duration`. Диагностическое.                                                                                                                                                                                                                                                                                                                                                     |
| `ended_at`                                         | number \| null           | Конец звонка; `null`, пока звонок живой.                                                                                                                                                                                                                                                                                                                                                                                            |
| `duration`                                         | number \| null           | Время разговора в секундах от `answered_at`. `null` для недозвонов, для `error/not_finalized` и пока идёт дозвон; у живого звонка — прошедшее время разговора.                                                                                                                                                                                                                                                                      |
| `transcript`                                       | array                    | Упорядоченные реплики: `{"role": "user"\|"assistant", "text": string, "interrupted"?: true, "pending"?: true, "unspoken"?: true, "ts"?: number, "spoke_at"?: number}`. `unspoken: true` означает, что реплика не прозвучала вообще — её перебили до первого звука; абонент её не слышал, и в контекст модели и пост-аналитики она не идёт. Ключ добавлен без смены версии: разборщики, которые о нём не знают, продолжают работать. |
| `dynamic_variables`                                | object                   | Текущие переменные разговора: ваши начальные значения плюс собранные во время звонка. На **недозвоне** здесь лежат значения, переданные вами для контакта: пустой объект не является признаком недозвона — для этого читайте `call_status`.                                                                                                                                                                                         |
| `metadata`                                         | object                   | Ваша нагрузка, без изменений.                                                                                                                                                                                                                                                                                                                                                                                                       |
| `analysis_status`                                  | string                   | `""` (не начат) \| `pending` \| `processing` \| `done` \| `skipped` \| `failed` \| `interrupted`. Терминальные: `done`, `skipped`, `failed`, `interrupted`.                                                                                                                                                                                                                                                                         |
| `call_analysis`                                    | object \| null           | Только когда `analysis_status` равен `done`: `summary`, `sentiment`, `custom_fields`, `call_successful`.                                                                                                                                                                                                                                                                                                                            |
| `health`                                           | string \| null           | Техническое здоровье звонка: `ok` \| `warning` \| `error`; `null` до финализации. Отражает проблемы провайдеров и инфраструктуры, **а не** качество разговора.                                                                                                                                                                                                                                                                      |
| `health_reasons`                                   | array                    | Коды причин вердикта, см. глоссарий ниже.                                                                                                                                                                                                                                                                                                                                                                                           |
| `events`                                           | array                    | События звонка без деталей: `{"type", "turn", "ts", "title"?, "status"?}` (инциденты наблюдателя, переводы и подобные моменты). Дополнительное, диагностическое.                                                                                                                                                                                                                                                                    |
| `cost`                                             | object \| null           | `{"total_microusd", "analytics_microusd"}` — в ответах вашего ключа `null`.                                                                                                                                                                                                                                                                                                                                                         |
| `recording_url`                                    | string                   | Устойчивый адрес скачивания, когда запись готова; до этого `""`.                                                                                                                                                                                                                                                                                                                                                                    |
| `recording`                                        | object \| null           | Метаданные записи: `{"id", "status", "channels", "format", "duration", "size", "expires_at"}`. `status` — `recording` \| `processing` \| `ready` \| `failed` \| `deleted`; `channels` — `stereo` \| `mono`.                                                                                                                                                                                                                         |
| `from_number`, `to_number`, `trunk_id`, `route_id` | string                   | Только телефонные звонки.                                                                                                                                                                                                                                                                                                                                                                                                           |

**Жизненный цикл:** `queued` (набор инициирован, дозвона ещё нет) → `ringing` → `ongoing` (абонент ответил) → `ended` (разговор состоялся, включая сообщения на автоответчик и переводы) либо `failed`.

<Warning>
  **`failed` сам по себе не означает, что абонент не ответил.** Обычно он приходит с `call_status: "not_connected"` (`no_answer`, `busy`, `dial_failed`, …), но сбой платформы тоже читается как `failed` с `call_status: "error"` (`internal_error`, `not_finalized`, `agent_unavailable`). Всегда читайте `call_status` / `status_reason`.
</Warning>

Веб-звонки пропускают `ringing` и `failed`: они идут `queued` → `ongoing` (первая реплика истории) → `ended`.

Асинхронные поля заполняются после завершения звонка: `call_analysis` — когда закончится пост-анализ, `recording_url` — когда финализируется запись. Вебхук `call_analyzed` сообщает, когда готово и то, и другое, — опрос это запасной путь, а не основной механизм.

## Чтение и завершение звонка

`GET /v1/calls/{call_id}` возвращает полный объект звонка; на неизвестный идентификатор — `404 call_not_found`.

`POST /v1/calls/{call_id}/end` завершает живой звонок — телефонный звонок, ещё стоящий в очереди набора, отменяется до набора — и возвращает объект звонка.

<Warning>
  `end` идемпотентен только **внутри окна живого реестра**: вызов на звонке, который закончился менее 5 минут назад, вернёт объект без ошибки; после этого запись в реестре исчезает и эндпоинт отвечает `404 call_not_found`, хотя `GET /v1/calls/{call_id}` по-прежнему возвращает сохранённый объект. Не используйте `end` как способ прочитать состояние.
</Warning>

`GET /v1/calls/{call_id}/turns` отдаёт метрики задержек по тёрнам завершённого телефонного или веб-звонка: массив `{"turn", "metrics": {"vad_ms", "asr_ms", "eou_wait_ms", "classifier_ms", "llm_ms", "tts_ms", "turn_ms", …, "estimated"?: true}, "interrupted"?: true, "merged_from"?: number}`. Диагностическая поверхность для нагрузочных тестов; `404` на неизвестные идентификаторы и на сессии, не являющиеся звонком.

`merged_from` появляется у тёрна абонента, чью фразу расколола пауза и которую [склеили обратно](/ru/v4/platform/campaign-history): это число обрывков, а фазы тёрна взяты у первого из них. Ключ аддитивный: клиент, который его не читает, разбирает ответ обычным образом. Метрики `llm_first_sentence_ms` в ответе нет.

## `GET /v1/calls` — список звонков

| Параметр                   | Примечания                                                                                                 |
| -------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `agent_id`                 | фильтр по агенту                                                                                           |
| `direction`                | `outbound` \| `inbound` \| `web`. **Не валидируется**: неизвестное значение даёт пустой список, а не `400` |
| `status`                   | только `ongoing` или `ended` (иное → `400 invalid_filter_value`); `ended` включает неудавшиеся звонки      |
| `call_status`              | `completed` \| `not_connected` \| `error` (иное → `400 invalid_filter_value`)                              |
| `status_reason`            | точное совпадение, например `agent_hangup` (неизвестное значение → `400 invalid_filter_value`)             |
| `health`                   | `ok` \| `warning` \| `error` — «только проблемные звонки» одним кликом: `health=error`                     |
| `health_reason`            | точное совпадение кода. Не валидируется: неизвестный код даёт пустой список                                |
| `from_number`, `to_number` | ⚠️ применяются **после** постраничности, только к текущей странице                                         |
| `since`, `until`           | границы по началу звонка, секунды эпохи, включительно                                                      |
| `limit`                    | 1–200, по умолчанию 50; вне диапазона → `422 invalid_request`                                              |
| `cursor`                   | курсор из предыдущего ответа; повреждённый → `400 invalid_cursor`                                          |

Ответ: `{calls: [...], has_more, next_cursor}`. Элементы списка — сводки без транскрипта; полный объект — `GET /v1/calls/{call_id}`. Идите по страницам, передавая `next_cursor` как `cursor`, пока `has_more` не станет `false`. В списке только телефонные и веб-звонки; тестовые сессии редактора и чаты в него не попадают.

<Warning>
  `from_number` / `to_number` — фильтры уровня страницы: страница может содержать меньше `limit` элементов (в том числе ноль), пока `has_more` всё ещё `true`. Не считайте пустую страницу концом результатов. Для сверки по номерам лучше фильтровать по своей `metadata` на своей стороне.
</Warning>
