Звонки
Программное управление голосовыми звонками: инициировать исходящий вызов к контакту (`POST /calls/originate`) и завершить активный вызов (`POST /calls/{call}/hangup`). Это API управления звонками, а не браузерный софтфон — оно лишь ставит вызов в работу через телефонный шлюз AISAR; сам звук идёт по SIP/WebRTC вне HTTP. Вся группа доступна только при активной подписке и включённом на плане функционале телефонии (`telephony`); кроме того, вызывающий пользователь должен быть заведён как оператор телефонии (иметь провиженный SIP-эндпоинт) и обладать правом `telephony.use` в текущей компании — иначе `403 { "data": { "message": "Telephony is not available for your account." } }`. `POST /calls/originate` принимает `contact_id` и `channel_id` (оба обязательны, целые). Канал задаёт способ дозвона: канал WhatsApp/WABA звонит контакту в WhatsApp, а канал типа `sip_telephony` набирает телефонный номер контакта через SIP-транк. И контакт, и канал должны принадлежать текущей компании (иначе `404 { "data": { "message": "Contact or channel not found." } }`). Успех (200) возвращает созданную запись звонка: `{ "data": { "call": { id, direction: "outbound", status: "ringing", to_number, from_number, asterisk_channel_id, timeline_message_id } } }` — вызов создаётся в состоянии `ringing`, а в таймлайн диалога добавляется карточка звонка (`timeline_message_id`). Дальнейшие переходы статуса (`ringing → answered → completed`, либо `rejected` / `missed` / `failed`) приходят через события реального времени и вебхуки семейства `call.*`, а не в теле этого ответа. WhatsApp-звонки требуют действующего разрешения на звонок (call permission) от контакта: без него `originate` вернёт `422` с блоком `eligibility`, где `call.can_call = false`. Проверить готовность и запросить разрешение заранее можно через `GET /v1/contacts/{contact}/outbound-eligibility` и `POST /v1/contacts/{contact}/call-permission/request` (группа «Контакты»). Для SIP-каналов разрешение не требуется (`eligibility.call.permission_status = "not_required"`), нужен лишь телефонный номер у контакта — иначе `422 { "data": { "message": "Contact has no phone number to dial." } }`. Прочие исходы `originate`: `403` с блоком `eligibility` — канал или контакт не поддерживает звонок (нет WhatsApp-аккаунта, оператор не включён); `502` — телефонный шлюз не смог инициировать вызов; `422` — ошибка валидации тела (отсутствуют `contact_id` / `channel_id`). `POST /calls/{call}/hangup` завершает активный вызов. Помимо доступности телефонии, завершить вызов может только его участник (назначенный оператор либо сторона вызова) или пользователь с правом управления телефонией компании — иначе `403`. Успех: `{ "message": "Hangup requested." }` (запрос на разрыв передан в шлюз; фактическое завершение подтверждается событием `call.*`). Обратите внимание: ответ `hangup`, в отличие от `originate`, не обёрнут в `data`.
Эндпоинты
| Метод | Путь | Описание |
|---|---|---|
| POST | /v1/calls/originateИнициировать исходящий звонок контакту через WhatsApp- или SIP-канал. | Инициировать исходящий звонок контакту через WhatsApp- или SIP-канал. |
| POST | /v1/calls/{call}/hangupЗавершить активный звонок (доступно участнику вызова или менеджеру телефонии). | Завершить активный звонок (доступно участнику вызова или менеджеру телефонии). |
Примеры
Исходящий WhatsApp-звонок
Запрос
curl -X POST https://api.aisar.app/v1/calls/originate \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_id": 42,
"channel_id": 7
}'Ответ
{
"data": {
"call": {
"id": 5001,
"direction": "outbound",
"status": "ringing",
"to_number": "7700000XXXX",
"from_number": "7708550XXXX",
"asterisk_channel_id": "1720787000.4521",
"timeline_message_id": 88214
}
}
}Исходящий звонок через SIP-транк
Запрос
curl -X POST https://api.aisar.app/v1/calls/originate \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_id": 42,
"channel_id": 12
}'Ответ
{
"data": {
"call": {
"id": 5002,
"direction": "outbound",
"status": "ringing",
"to_number": "7700000XXXX",
"from_number": "7727000XXXX",
"asterisk_channel_id": "1720787100.4530",
"timeline_message_id": 88221
}
}
}WhatsApp-звонок без разрешения контакта (422)
Запрос
curl -X POST https://api.aisar.app/v1/calls/originate \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_id": 42,
"channel_id": 7
}'Ответ
{
"data": {
"message": "No valid call permission or daily attempts exhausted.",
"eligibility": {
"message": {
"window_open": true,
"window_expires_at": 1720800000,
"requires_template": false
},
"call": {
"channel_kind": "whatsapp",
"can_call": false,
"permission_status": "none",
"permission_expires_at": null,
"can_request_permission": true,
"request_cooldown_until": null,
"attempts_remaining": 5,
"has_whatsapp_account": true
}
}
}
}Завершение активного звонка
Запрос
curl -X POST https://api.aisar.app/v1/calls/5001/hangup \
-H "Authorization: Bearer YOUR_API_TOKEN"Ответ
{
"message": "Hangup requested."
}Телефония недоступна для аккаунта (403)
Запрос
curl -X POST https://api.aisar.app/v1/calls/originate \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_id": 42,
"channel_id": 7
}'Ответ
{
"data": {
"message": "Telephony is not available for your account."
}
}