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

# Управление звонками WhatsApp

> Настройте WhatsApp Calling, обрабатывайте сигнализацию входящих и исходящих вызовов, обрабатывайте события звонков и скачивайте записи или транскрипции.

## Что это такое

YCloud WhatsApp Calling API управляет сигнализацией голосовых вызовов между пользователем WhatsApp и бизнес-номером телефона. Ваше приложение обменивается данными SDP через YCloud, в то время как ваша реализация WebRTC управляет аудиосоединением.

Звонки могут инициироваться в обоих направлениях:

* **Инициированные пользователем:** Пользователь WhatsApp звонит вашей компании. Ваше приложение получает offer и принимает или отклоняет вызов.
* **Инициированные компанией:** Ваше приложение создает offer и запрашивает у YCloud звонок пользователю WhatsApp.

<Info>
  Calling API обрабатывает сигнализацию вызова, а не медиастек WebRTC. Ваше приложение отвечает за настройку peer connection, захват и воспроизведение звука, генерацию SDP и освобождение ресурсов WebRTC.
</Info>

## Карта API

API Calling и события Webhook следуют одному и тому же жизненному циклу, но не образуют единую последовательность, применимую к каждому звонку. Выполните общую настройку, а затем следуйте процессу для вызовов, инициированных пользователем или компанией. Используйте ID звонка, `wacid`, для сопоставления каждой операции и события.

### Общая настройка

| API | Когда используется | Что происходит дальше |
| - | - | - |
| [`GET settings`](/api-reference/whatsapp-phone-numbers/retrieve-phone-number-settings) или [`POST settings`](/api-reference/whatsapp-phone-numbers/save-phone-number-settings) | Перед обработкой звонков или при изменении настроек Calling и захвата. | Настройте Webhook и подготовьте вашу реализацию WebRTC, затем следуйте процессу в зависимости от направления звонка. |

### Звонки, инициированные пользователем

| Порядок | API или событие | Что происходит дальше |
| - | - | - |
| 1 | [`whatsapp.call.connect`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-connect-webhook-examples) | Получите SDP-предложение, `phoneId` и `wacid`, затем создайте SDP-ответ. |
| 2 (необязательно) | [`POST /whatsapp/calls/preAccept`](/api-reference/whatsapp-calling/pre-accept-a-call) | Если вы планируете принять звонок, отправьте SDP-ответ для подготовки медиапути. Это не отвечает на звонок. |
| 3 | [`POST /whatsapp/calls/accept`](/api-reference/whatsapp-calling/accept-a-call) или [`POST /whatsapp/calls/reject`](/api-reference/whatsapp-calling/reject-a-call) | Выберите одно: примите звонок с SDP-ответ или отклоните его. |

### Звонки, инициированные компанией

| Порядок | API или событие | Что происходит дальше |
| - | - | - |
| 1 | [`POST /whatsapp/calls/connect`](/api-reference/whatsapp-calling/connect-a-call) | Отправьте свой SDP-предложение, начните звонок и сохраните возвращенный `wacid`. |
| 2 | [`whatsapp.call.connect`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-connect-webhook-examples) | Получите удаленный SDP-ответ и примените его к тому же WebRTC peer connection. |
| 3 | [`whatsapp.call.status.updated`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-status-update-webhook-examples) | Отслеживайте `RINGING`, `ACCEPTED` или `REJECTED`. Это событие может приходить неоднократно по мере изменения состояния попытки. |

### Общее завершение звонка

| API или событие | Когда используется | Что происходит дальше |
| - | - | - |
| [`POST /whatsapp/calls/terminate`](/api-reference/whatsapp-calling/terminate-a-call) | Необязательно. Вызывайте, когда вашему приложению необходимо завершить активный входящий или исходящий вызов. | Не закрывайте запись о звонке в ожидании финального события. |
| [`whatsapp.call.terminate`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-terminate-webhook-examples) | Получите его для финального результата звонка. | Зафиксируйте окончательный статус (`COMPLETED` или `FAILED`) и длительность, затем освободите оставшиеся ресурсы звонка. |

### Необязательная обработка медиа

| API или событие | Когда используется | Что происходит дальше |
| - | - | - |
| [`whatsapp.call.recording.updated`](/ru/api-reference/webhooks/test-webhooks) или [`whatsapp.call.transcription.updated`](/ru/api-reference/webhooks/test-webhooks) | Когда захват включен и обработка завершена. | Если событие сообщает о `AVAILABLE`, прочитайте его `mediaAssetId`. Результат `FAILED` является окончательным для этого ресурса. |
| [`GET /whatsapp/calls/media/{mediaAssetId}`](/api-reference/whatsapp-calling/download-call-media) | Только после того, как соответствующее событие сообщит о `AVAILABLE`. | Скачайте файл записи или транскрипции. |

В этих таблицах описан рабочий процесс приложения. Они не гарантируют, что Webhook будут доставлены в том же порядке, что и строки. Сопоставляйте события по `wacid` и обрабатывайте повторную доставку идемпотентно.

## Перед началом работы

Перед отправкой запроса к Calling подготовьте следующее:

1. API-ключ аккаунта YCloud. Передавайте его в заголовке `X-API-Key`. См. раздел [Аутентификация](/ru/api-reference/guides/api-fundamentals/authentication).
2. WhatsApp Business Account и бизнес-номер телефона, зарегистрированный в YCloud.
3. Включенная функция Calling для этого номера телефона.
4. Реализация аудио WebRTC, которая может создавать и применять SDP-предложения и ответы.
5. Эндпоинт Webhook YCloud, подписанный на события Calling, используемые вашей интеграцией. См. раздел [Настройка Webhook](/ru/api-reference/guides/api-fundamentals/configure-webhooks).
6. Разрешение пользователя на вызовы, если оно требуется для исходящего вызова от имени компании.

Свяжитесь с представителем YCloud, чтобы включить доступ к Calling API. Для исходящих
вызовов соблюдайте [актуальные требования Calling](/ru/documentation/calling/overview#business-initiated-calls-outbound),
включая уровень обмена сообщениями Business Portfolio на 2 000 клиентов и поддерживаемые
страны для бизнес-номеров. Прежний порог в 1 000 переписок заменен
текущими требованиями.

В примерах ниже используются следующие переменные окружения:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export YCLOUD_API_KEY="YOUR_API_KEY"
export WABA_ID="YOUR_WABA_ID"
export BUSINESS_PHONE_NUMBER="+16315551111"
```

Храните API-ключ на своем сервере. Не добавляйте его в код браузерных или мобильных приложений.

## Как это работает

Сначала настройте рабочий номер телефона компании. Затем выполните обмен SDP в соответствии с направлением вызова. Ответы API подтверждают отдельные операции сигнализации, а события webhook сообщают об изменениях состояния и конечном результате. Если включена фиксация, отдельные события сообщают о готовности записи или расшифровки к скачиванию.

## Запрос

## Настройка рабочего номера телефона компании

Настройки вызовов и записи привязаны к конкретному бизнес-номеру телефона WhatsApp. Настройте их перед началом обработки вызовов.

### Чтение настроек Calling

Используйте [`GET /whatsapp/phoneNumbers/{wabaId}/{phoneNumber}/settings`](/api-reference/whatsapp-phone-numbers/retrieve-phone-number-settings), чтобы проверить, включены ли вызовы и виден ли значок вызова:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request GET \
  "https://api.ycloud.com/v2/whatsapp/phoneNumbers/$WABA_ID/$BUSINESS_PHONE_NUMBER/settings?type=calling" \
  --header "X-API-Key: $YCLOUD_API_KEY"
```

Если опустить `type`, YCloud вернет ответ с настройками Calling.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "calling": {
    "id": "19213232132",
    "status": "ENABLED",
    "iconVisibility": "DEFAULT"
  }
}
```

| Поле | Значения | Описание |
| - | - | - |
| `calling.id` | Строка | Идентификатор бизнес-номера телефона WhatsApp. |
| `calling.status` | `ENABLED`, `DISABLED` | Включены ли вызовы для этого номера телефона. |
| `calling.iconVisibility` | `DEFAULT`, `DISABLE_ALL` | Использует ли WhatsApp стандартное поведение для значка вызова или скрывает все значки вызова. |

### Включение Calling

Сохраните настройки Calling с помощью [`POST /whatsapp/phoneNumbers/{wabaId}/{phoneNumber}/settings`](/api-reference/whatsapp-phone-numbers/save-phone-number-settings), прежде чем начинать принимать или совершать вызовы:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.ycloud.com/v2/whatsapp/phoneNumbers/$WABA_ID/$BUSINESS_PHONE_NUMBER/settings" \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "calling": {
      "status": "ENABLED",
      "iconVisibility": "DEFAULT"
    }
  }'
```

Ответ содержит сохраненный объект `calling`. Перед обработкой реальных вызовов завершите настройку Webhook и сессий WebRTC.

### Настройка записи и расшифровки

Параметры фиксации применяются к новым вызовам, созданным через API. Вы можете включить запись, расшифровку или обе эти функции.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.ycloud.com/v2/whatsapp/phoneNumbers/$WABA_ID/$BUSINESS_PHONE_NUMBER/settings" \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "capture": {
      "recordingEnabled": true,
      "transcriptionEnabled": true,
      "purpose": "quality_assurance",
      "announcementLanguage": "en_US"
    }
  }'
```

| Поле | Тип | Обязательно | Описание |
| - | - | - | - |
| `capture.recordingEnabled` | Логическое | Да | Включает или отключает запись разговора. |
| `capture.transcriptionEnabled` | Логическое | Да | Включает или отключает расшифровку разговора. |
| `capture.purpose` | Строка | Условно | Обязательно, если включена любая из опций фиксации. Не более 250 символов. |
| `capture.announcementLanguage` | Строка | Условно | Обязательно, если включена любая из опций фиксации. Поддерживаемые значения: `en`, `en_US`, `en_AU`, `en_CA`, `en_GB`, `en_IN`, `en_NZ`, `nl`, `fr`, `de`, `hi`, `it`, `kn`, `pt`, `es`, `es_ES`, `te`, `vi`. |

Чтобы прочитать параметры фиксации, используйте `type=capture`:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request GET \
  "https://api.ycloud.com/v2/whatsapp/phoneNumbers/$WABA_ID/$BUSINESS_PHONE_NUMBER/settings?type=capture" \
  --header "X-API-Key: $YCLOUD_API_KEY"
```

Вы можете включить `calling` и `capture` в один запрос `POST`. После проверки доступа к номеру телефона YCloud попытается сохранить каждый раздел независимо. В случае ошибки при сохранении одного раздела второй уже может быть сохранен. Прочитайте обе настройки после ошибки и повторите попытку только для того раздела, который все еще требует обновления.

## Обработка вызова, инициированного пользователем

![Последовательность вызова, инициированного пользователем](https://files.readme.io/65fa96a2414cfde54dbf36c30af6e6392ca36093d478674c23547879a14f9c4c-image.png)

При вызове, инициированном пользователем, WhatsApp отправляет SDP-предложение. Ваше приложение отвечает на этот offer, а затем принимает или отклоняет вызов.

### 1. Получение события подключения

Подпишитесь на [`whatsapp.call.connect`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-connect-webhook-examples). В событии, инициированном пользователем, `direction` имеет значение `USER_INITIATED` и содержит SDP `offer`.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "evt_call_connect_123",
  "type": "whatsapp.call.connect",
  "apiVersion": "v2",
  "createTime": "2024-01-01T12:00:00.000Z",
  "callingConnect": {
    "id": "6757b723960b25543b9ecc66",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEF",
    "phoneId": "461269257068832",
    "from": "+6281361905133",
    "to": "+6283138205150",
    "direction": "USER_INITIATED",
    "dialTime": 1733826430000,
    "sdpType": "offer",
    "sdp": "SDP_OFFER"
  }
}
```

Сохраните `callingConnect.wacid` и `callingConnect.phoneId` вместе. Примените полученный SDP-предложение к вашему пиринговому соединению WebRTC и создайте SDP-ответ.

### 2. Предварительный прием вызова (pre-accept)

Вызовите pre-accept после создания SDP-ответ, но до того, как оператор примет вызов. Это подготовит медиатракт и позволит избежать прерываний звука в момент ответа на вызов.

**Эндпоинт:** [`POST /whatsapp/calls/preAccept`](/api-reference/whatsapp-calling/pre-accept-a-call)

| Поле | Тип | Обязательно | Описание |
| - | - | - | - |
| `phoneId` | Строка | Да | Идентификатор бизнес-номера телефона из события подключения. |
| `wacid` | Строка | Да | Идентификатор вызова WhatsApp из события подключения. |
| `sdpType` | Строка | Да | Должно быть `answer`. |
| `sdp` | Строка | Да | SDP-ответ, созданный вашей реализацией WebRTC. |

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.ycloud.com/v2/whatsapp/calls/preAccept \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "phoneId": "461269257068832",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEF",
    "sdpType": "answer",
    "sdp": "SDP_ANSWER"
  }'
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEF",
  "success": true
}
```

После успешного предварительного принятия удерживайте звонок в состоянии вызова или готовности. Предварительное принятие не означает ответ на звонок со стороны пользователя.

### 3. Примите вызов

Когда оператор отвечает, отправьте те же `phoneId`, `wacid`, тип SDP и ответ SDP на эндпоинт принятия вызова.

**Эндпоинт:** [`POST /whatsapp/calls/accept`](/api-reference/whatsapp-calling/accept-a-call)

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.ycloud.com/v2/whatsapp/calls/accept \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "phoneId": "461269257068832",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEF",
    "sdpType": "answer",
    "sdp": "SDP_ANSWER"
  }'
```

Поля запроса и формат ответа такие же, как и при предварительном принятии. После успешного ответа используйте состояние подключения WebRTC для оценки готовности медиаданных и дождитесь события `whatsapp.call.terminate`, чтобы узнать окончательный результат вызова.

Документированное окно для принятия входящего вызова составляет около 30–60 секунд после
Webhook-события connect. Примите вызов без задержки: неотвеченный звонок завершается со стороны пользователя
уведомлением **Not Answered** и Webhook-событием terminate.

Даже если подключение WebRTC уже установлено, запускайте аудио только после того,
как запрос на принятие вернет HTTP `200`. Более ранний запуск может привести к обрезке первых
слов, а слишком поздний — к тишине.

### Отклонение вместо принятия

Если оператор не может принять входящий звонок, отклоните его вместо создания активной сессии.

**Эндпоинт:** [`POST /whatsapp/calls/reject`](/api-reference/whatsapp-calling/reject-a-call)

| Поле | Тип | Обязательное | Описание |
| - | - | - | - |
| `phoneId` | Строка | Да | Идентификатор бизнес-номера телефона из события connect. |
| `wacid` | Строка | Да | Идентификатор звонка WhatsApp из события connect. |

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.ycloud.com/v2/whatsapp/calls/reject \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "phoneId": "461269257068832",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEF"
  }'
```

Ответ возвращается в стандартном формате Calling response. Освободите локальное пиринговое соединение после запроса и все равно обработайте последующее событие завершения для этого `wacid`, если оно поступит.

## Инициирование звонка со стороны бизнеса

При звонке, инициированном бизнесом, ваше приложение создает SDP-предложение (offer) и отправляет его в YCloud.

### Получение разрешения на звонок

Перед совершением вызова необходимо получить разрешение пользователя на звонок. Интерактивный
запрос разрешения можно отправить в течение активного окна клиентской поддержки:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "from": "+16315551111",
  "to": "+16315552222",
  "type": "interactive",
  "interactive": {
    "type": "call_permission_request",
    "action": { "name": "call_permission_request" },
    "body": { "text": "May we call you to help with your order?" }
  }
}
```

Отправьте это тело запроса на `POST /v2/whatsapp/messages/sendDirectly` или поставьте его в очередь с помощью
`POST /v2/whatsapp/messages`.

Вы также можете создать шаблон разрешения на звонки. Например, отправьте это тело
на `POST /v2/whatsapp/templates`, а затем дождитесь одобрения:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "wabaId": "WABA_ID",
  "name": "call_permission_request_template",
  "language": "en_US",
  "category": "UTILITY",
  "components": [
    {
      "type": "BODY",
      "text": "May we call you about order {{1}}?",
      "example": { "body_text": [["ORDER_123"]] }
    },
    { "type": "call_permission_request" }
  ]
}
```

Отправьте одобренный шаблон с соответствующим параметром тела:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "from": "+16315551111",
  "to": "+16315552222",
  "type": "template",
  "template": {
    "name": "call_permission_request_template",
    "language": { "code": "en_US", "policy": "deterministic" },
    "components": [
      { "type": "body", "parameters": [{ "type": "text", "text": "ORDER_123" }] }
    ]
  }
}
```

Когда параметр `callback_permission_status` включен в настройках вызовов
телефонного номера, звонок от пользователя может предоставить разрешение на обратный вызов. Пользователь также может
предоставить постоянное разрешение на звонки в профиле компании.

Ответы с разрешениями приходят в виде событий `whatsapp.inbound_message.received`. Проверяйте
объект `interactive.call_permission_reply`, а не просто факт доставки сообщения
с запросом разрешения:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "whatsapp.inbound_message.received",
  "whatsappInboundMessage": {
    "from": "+16315552222",
    "to": "+16315551111",
    "type": "interactive",
    "interactive": {
      "type": "call_permission_reply",
      "call_permission_reply": {
        "response": "accept",
        "is_permanent": true,
        "response_source": "user_action"
      }
    }
  }
}
```

| Поле | Значение |
| - | - |
| `response` | Принял или отклонил пользователь запрос на разрешение. |
| `is_permanent` | Является ли разрешение постоянным, а не временным. |
| `expiration_timestamp` | Срок действия временного разрешения, если указан. |
| `response_source` | Был ли ответ результатом действия пользователя или отправлен автоматически. |

Не инициируйте звонок после отказа или по истечении срока действия разрешения. Ошибка Meta
`138006` означает, что у бизнес-номера нет необходимого разрешения на совершение звонков.
Подробности об ошибках провайдера см. в разделе [Ошибки Calling от Meta](https://developers.facebook.com/documentation/business-messaging/whatsapp/calling/reference/errors).

### 1. Создайте SDP-предложение (offer)

Создайте локальное WebRTC пиринговое соединение и добавьте аудиотрек. Сгенерируйте SDP-предложение (offer), установите его в качестве локального описания (local description) и дождитесь завершения этой операции перед отправкой предложения в YCloud.

### 2. Установите соединение для вызова

**Эндпоинт:** [`POST /whatsapp/calls/connect`](/api-reference/whatsapp-calling/connect-a-call)

| Поле | Тип | Обязательное | Описание |
| - | - | - | - |
| `from` | Строка | Да | Зарегистрированный бизнес-номер телефона в формате E.164. |
| `to` | Строка | Условное | Номер телефона пользователя в формате E.164. Обязателен, если не указан `recipient`. |
| `recipient` | Строка | Условное | BSUID пользователя или родительский BSUID. Обязателен, если не указан `to`. |
| `sdpType` | Строка | Да | Должно быть `offer`. |
| `sdp` | Строка | Да | SDP-предложение (offer), созданное вашей реализацией WebRTC. |

Укажите как минимум одно из полей: `to` или `recipient`. Если переданы оба, YCloud использует `to` и проигнорирует `recipient`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.ycloud.com/v2/whatsapp/calls/connect \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "from": "+16315551111",
    "to": "+16315552222",
    "sdpType": "offer",
    "sdp": "SDP_OFFER"
  }'
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABE",
  "success": true
}
```

Сразу сохраните возвращенный `wacid`. Статус `success: true` означает, что операция подключения принята в обработку; это не означает, что пользователь ответил на звонок.

### 3. Примените ответ и отслеживайте попытку

YCloud отправляет [`whatsapp.call.connect`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-connect-webhook-examples) для вызова. Для исходящего звонка, инициированного бизнесом, событие имеет `direction: BUSINESS_INITIATED` и содержит удаленный SDP `answer`. Примените этот ответ в качестве удаленного описания (remote description) для того же peer connection.

Подпишитесь на [`whatsapp.call.status.updated`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-status-update-webhook-examples), чтобы отслеживать попытку:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "evt_676e5ab57a9cb742d02d7646",
  "type": "whatsapp.call.status.updated",
  "apiVersion": "v2",
  "createTime": "2024-12-27T07:41:28.422Z",
  "callingStatusUpdated": {
    "wabaId": "188234691048809",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABE",
    "phoneId": "461269257068832",
    "status": "RINGING",
    "recipientPhone": "+16315552222"
  }
}
```

| Статус | Значение | Рекомендуемое действие |
| - | - | - |
| `RINGING` | Идет вызов пользователя. | Сохраняйте попытку открытой и продолжайте ожидание. |
| `ACCEPTED` | Пользователь принял вызов. | Используйте состояние WebRTC-соединения, чтобы подтвердить готовность медиа. |
| `REJECTED` | Пользователь отклонил вызов. | Остановите попытку и освободите локальные ресурсы WebRTC. |

Сделайте обработку событий идемпотентной, чтобы повторная доставка не дублировала действия оператора, списание средств или очистку ресурсов.

## Завершение активного вызова

Вызывайте terminate, когда вашему приложению необходимо завершить активный входящий или исходящий вызов.

**Эндпоинт:** [`POST /whatsapp/calls/terminate`](/api-reference/whatsapp-calling/terminate-a-call)

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.ycloud.com/v2/whatsapp/calls/terminate \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "phoneId": "461269257068832",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABE"
  }'
```

Поля запроса совпадают с запросом на отклонение (reject). Успешный ответ подтверждает, что YCloud обработал операцию завершения. Сохраняйте запись вызова открытой до получения окончательного события завершения или до тех пор, пока ее не закроет ваша собственная политика восстановления.

## Ответ

Все пять эндпоинтов сигналинга возвращают ответ одинаковой структуры:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABE",
  "success": true
}
```

`wacid` идентифицирует вызов, связанный с операцией. `success: true` подтверждает, что операция сигналинга прошла успешно; это не подтверждает, что другой участник ответил или что вызов завершен. Для этих результатов используйте состояние WebRTC и события Webhook вызовов.

## Обработка финального события вызова

[`whatsapp.call.terminate`](/ru/api-reference/guides/examples/webhook-examples/whatsapp-calling-terminate-webhook-examples) — это терминальное событие жизненного цикла вызова.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "evt_6757b889a5a42d369ef48481",
  "type": "whatsapp.call.terminate",
  "apiVersion": "v2",
  "createTime": "2024-12-10T03:42:01.822Z",
  "callingTerminate": {
    "id": "6757b889960b25543b9ecc67",
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABIYIEFENjB",
    "phoneId": "461269257068832",
    "from": "+6281361905133",
    "to": "+6283138205150",
    "direction": "USER_INITIATED",
    "startTime": 1733734738000,
    "endTime": 1733734771000,
    "duration": 33,
    "status": "COMPLETED"
  }
}
```

| Поле | Описание |
| - | - |
| `wacid` | ID вызова, используемый для сопоставления события с вашей записью о вызове. |
| `direction` | `USER_INITIATED` или `BUSINESS_INITIATED`. |
| `startTime`, `endTime` | Временные метки Unix в миллисекундах. |
| `duration` | Длительность вызова в секундах. |
| `status` | Финальный результат: `COMPLETED` или `FAILED`. |
| `errorCode` | Числовой код ошибки, представленный в виде строки, если вызов завершился сбоем. |

При получении этого события завершите запись вызова и освободите оставшиеся ресурсы WebRTC. Более ранний ответ API не подтверждает, что вызов завершен.

## Получение записей и транскрипций

Если запись включена, обработка медиа продолжается после завершения жизненного цикла вызова. Запись и транскрипция имеют отдельные финальные события:

| Событие | Свойство полезной нагрузки | Результат |
| - | - | - |
| [`whatsapp.call.recording.updated`](/ru/api-reference/webhooks/test-webhooks) | `callingRecording` | Запись имеет статус `AVAILABLE` или окончательно завершилась ошибкой (`FAILED`). |
| [`whatsapp.call.transcription.updated`](/ru/api-reference/webhooks/test-webhooks) | `callingTranscription` | Транскрипция имеет статус `AVAILABLE` или окончательно завершилась ошибкой (`FAILED`). |

В следующем примере показана доступная запись:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "evt_call_recording_01JZ8K4V7H3P6Q9R2T5W8X1Y4Z",
  "type": "whatsapp.call.recording.updated",
  "apiVersion": "v2",
  "createTime": "2026-08-04T08:00:00.000Z",
  "callingRecording": {
    "wacid": "wacid.HBgNNjI4MTM2MTkwNTEzMxUCABE",
    "phoneId": "461269257068832",
    "mediaAssetId": "66b1f0c2e4b05c2d8f1a3b47",
    "status": "AVAILABLE"
  }
}
```

Оба свойства полезной нагрузки используют одинаковые поля:

| Поле | Описание |
| - | - |
| `wacid` | ID вызова, связанный с медиаресурсом. |
| `phoneId` | ID бизнес-номера телефона, связанного с вызовом. |
| `mediaAssetId` | ID ресурса YCloud, используемый API загрузки медиа. |
| `status` | `AVAILABLE` или `FAILED`. |
| `error.code` | Постоянный код сбоя обработки. Присутствует, когда `status` равно `FAILED`. |
| `error.retryable` | Может ли повторная попытка вышестоящей операции с медиа увенчаться успехом. Присутствует, когда `status` равно `FAILED`. |

### Скачивание доступного ресурса

Вызывайте медиа-эндпоинт только после того, как соответствующее событие сообщит `AVAILABLE`.

**Эндпоинт:** [`GET /whatsapp/calls/media/{mediaAssetId}`](/api-reference/whatsapp-calling/download-call-media)

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request GET \
  "https://api.ycloud.com/v2/whatsapp/calls/media/66b1f0c2e4b05c2d8f1a3b47" \
  --header "X-API-Key: $YCLOUD_API_KEY" \
  --output calling-media.ogg
```

Эндпоинт возвращает файл целиком в виде вложения и не поддерживает скачивание по диапазонам байтов (byte-range). Записи используют `.ogg`; транскрипции используют `.json`.

Скачать ресурс может только владеющий им тенант YCloud. Ресурс остается доступным в течение 30 дней с момента создания. Отсутствующие, недоступные, истекшие или чужие ресурсы возвращают HTTP 404.

## Создание надежного приемника Webhook

Подпишите ваш эндпоинт на события, необходимые для вашей интеграции:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "enabledEvents": [
    "whatsapp.call.connect",
    "whatsapp.call.status.updated",
    "whatsapp.call.terminate",
    "whatsapp.call.recording.updated",
    "whatsapp.call.transcription.updated"
  ]
}
```

Для каждого запроса:

1. Сохраняйте исходное тело запроса (raw body) и проверяйте `YCloud-Signature`, прежде чем доверять событию.
2. Сохраняйте событие в постоянное хранилище или помещайте надежную задачу в очередь.
3. Своевременно возвращайте успешный ответ `2xx`.
4. Выполняйте дедупликацию по `id` верхнего уровня события.
5. Сопоставляйте данные вызова по `wacid`; сохраняйте `phoneId` вместе с ними для последующих операций.
6. Обрабатывайте связанные события, поступающие с небольшим интервалом, и учитывайте возможность повторной доставки.

Сведения о создании эндпоинтов, проверке подписи и механизмах доставки приведены в разделе [Настройка Webhook](/ru/api-reference/guides/api-fundamentals/configure-webhooks). Полные сгенерированные примеры можно найти на странице [Примеры полезной нагрузки Webhook](/ru/api-reference/guides/examples/webhook-examples/webhook-payload-examples).

## Обработка ошибок и восстановление

Эндпоинты для звонков используют стандартный формат ответа об ошибке YCloud API. Структуру ответа и рекомендации по повторным попыткам см. в разделе [Обработка ошибок](/ru/api-reference/guides/api-fundamentals/handle-errors).

Используйте эти проверки при типичных сбоях Calling:

| Ситуация | Что проверить | Восстановление |
| - | - | - |
| Ошибка валидации запроса | Обязательные идентификаторы, формат E.164, тип SDP, содержимое SDP или поля захвата. | Исправьте запрос. Не отправляйте повторно неизмененные данные. |
| Недопустимый адресат соединения | Требуется хотя бы один из параметров: `to` или `recipient`. Если указаны оба, приоритет отдается `to`. | Передайте действительный номер телефона в формате E.164 или BSUID. |
| Функция Calling недоступна | Владение номером телефона, регистрацию, настройки Calling, разрешения и поддерживаемые направления. | Исправьте конфигурацию или разрешения перед повторной попыткой. |
| Meta отклоняет сигнализацию | Изучите возвращенные сведения об ошибке, включая `whatsappApiError`, если они присутствуют. | Учитывайте возможность повтора ошибки и устраните исходную причину на стороне источника. |
| Ошибка при комбинированном сохранении настроек | Возможно, параметр `calling` или `capture` уже был сохранен. | Прочитайте обе настройки, затем повторите попытку только для той части, которая все еще требует обновления. |
| Загрузка медиа возвращает 400 | Был отправлен непустой заголовок `Range`. | Запросите файл полностью без `Range`. |
| Загрузка медиа возвращает 404 | Файл отсутствует, еще не готов, срок его действия истек или он принадлежит другому тенанту. | Проверьте статус события, тенант, идентификатор файла и 30-дневное окно доступности. |
| Повторный вызов Webhook | Одно и то же событие было доставлено повторно. | Верните `2xx` и пропустите повторную бизнес-обработку по событию `id`. |

Тайм-аут запроса не означает сбой операции сигнализации. Перед повторной попыткой сопоставьте запрос с событиями Webhook и текущим локальным состоянием звонка. Действие уже могло достичь WhatsApp.

## Чек-лист интеграции

* Включите функцию Calling для нужного корпоративного номера телефона.
* Настройте и протестируйте все необходимые подписки на Webhook для звонков.
* Проверяйте подписи Webhook и выполняйте дедупликацию событий.
* Храните `wacid`, `phoneId`, направление и текущее состояние вместе.
* Интерпретируйте API `success` как принятие операции в обработку, а не как финальный результат звонка.
* Используйте `preAccept` только для подготовки; вызывайте `accept` для ответа.
* Завершайте вызовы по событию `whatsapp.call.terminate`.
* Загружайте записанные медиафайлы только после события `AVAILABLE` и в течение 30 дней.
* Освобождайте ресурсы WebRTC при отклонении, завершении, ошибке или локальном тайм-ауте.
* Не записывайте API-ключи, полные SDP или идентификаторы участников в общие журналы приложения.

<CardGroup cols={2}>
  <Card title="Справочник по Calling API" icon="phone" href="/api-reference/whatsapp-calling/connect-a-call">
    Ознакомьтесь с точными схемами запросов и ответов для каждого эндпоинта Calling.
  </Card>

  <Card title="Примеры полезной нагрузки Webhook" icon="webhook" href="/ru/api-reference/guides/examples/webhook-examples/webhook-payload-examples">
    Ознакомьтесь с полными примерами событий Calling, сгенерированными на основе спецификации Webhook.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.