API v1
SipCloud Partner API · v2

Документация для разработчиков

REST-API для интеграций: история звонков и записи разговоров через защищённый HTTPS. Только чтение, доступ ограничен клиентом и IP-адресом. АТС наружу не открывается.

HTTPS · Bearer-токен JSON · REST Read-only

Введение

Partner API отдаёт данные телефонии клиента напрямую вашему сервису — без доступа к самой АТС.

Все запросы идут на базовый адрес по HTTPS. Ответы — в формате JSON (кроме аудиофайлов записей). Методы — только GET (интеграция на чтение).

Базовый URL

Каждый ключ привязан к одному клиенту (tenant) и работает только с согласованного IP-адреса. Ключ и whitelist выдаёт оператор SipCloud.

  • Read-only. Изменить что-либо через API нельзя.
  • Изоляция по клиенту. Ключ видит только данные своего клиента.
  • Whitelist по IP. Запросы принимаются только с указанных адресов.
Base URL
https://sipcloud.uz/partner/v1
Пример запроса
curl https://sipcloud.uz/partner/v1/ping \
  -H "Authorization: Bearer $KEY"
200 · Ответ
{
  "ok": true,
  "tenant": "ChoCo",
  "scopes": ["calls:read", "recordings:read"]
}

Аутентификация

API использует Bearer-токен. Передавайте ключ в заголовке Authorization с каждым запросом:

Формат ключа

Ключ имеет вид sck_…. Он read-only, привязан к клиенту и разрешён только с согласованного IP. Альтернативно можно передавать ключ в заголовке X-Api-Key.

Храните ключ в секрете и на стороне сервера. Не публикуйте его в браузере, репозитории или логах. При компрометации ключ мгновенно отзывается и перевыпускается оператором.
Заголовок
Authorization: Bearer sck_your_key_here
401 · без ключа
{
  "ok": false,
  "error": "missing api key (Authorization: Bearer …)"
}

Ошибки и коды

Успех — HTTP 200. Ошибки возвращаются с соответствующим кодом и телом { "ok": false, "error": "…" }.

КодЗначение
200Успех.
400Некорректные параметры (например, формат даты).
401Нет / неверный / отозванный ключ.
403IP не в whitelist или не хватает scope.
404Объект не найден / запись ещё не готова.
5xxВременная ошибка — повторите с экспоненциальной задержкой.
403 · IP не разрешён
{
  "ok": false,
  "error": "ip not allowed",
  "your_ip": "203.0.113.7"
}
403 · scope
{
  "ok": false,
  "error": "scope required: recordings:read"
}
GET/partner/v2/calls

Звонки v2 — один внешний звонок = одна запись

Рекомендуемый метод. Технические участки АТС (очередь, обзвон сотрудников, переводы) собраны в одну внешнюю сессию. Счётчики совпадают с кабинетом и Excel. Параметры from, to, limit, cursor — как в v1. В ответе: schema_version, unit=external_call, tz, filters, as_of.

ПолеОписание
normalized_call_idID внешнего звонка в SipCloud — первичный ключ на вашей стороне.
source_ids.linkedidКорневой идентификатор звонка на АТС.
directionin · out · internal
outcomeВходящий: answered (контакт с сотрудником) · missed · failed. Исходящий: answered · noanswer · busy · failed.
reasonПричина пропуска: no_answer · queue_abandon · ivr.
client, line, queueНомер клиента (только цифры, 998…), номер компании, очередь.
operator, offered, offersКто ответил (или звонил); кому предлагался звонок; число предложений.
wait_sec, talk_secОжидание до ответа сотрудника; разговор без двойного счёта параллельных участков.
revisionРастёт, если звонок пересчитан (поздний участок, поздний ответ).
Запрос
curl "https://sipcloud.uz/partner/v2/calls?from=2026-10-01&to=2026-10-01" \
  -H "Authorization: Bearer $KEY"
200 · Ответ
{ "ok": true, "schema_version": 2, "unit": "external_call",
  "tz": "Asia/Tashkent", "count": 1, "next_cursor": null,
  "calls": [{ "normalized_call_id": 1574,
    "source_ids": {"linkedid": "mikopbx-1790742279.34094"},
    "direction": "in", "outcome": "answered", "reason": null,
    "client": "998903268886", "queue": "2200106", "operator": "101",
    "offered": ["100","101","102","103"], "offers": 4,
    "wait_sec": 16, "talk_sec": 139, "revision": 1,
    "recording_url": "https://sipcloud.uz/partner/v1/recording?uid=…" }] }
GET/partner/v1/calls

История звонков

Возвращает список звонков клиента за период, отсортированный по времени. Поддерживает постраничную выборку по курсору.

Query-параметры

ПараметрТипОписание
fromdateНачало периода YYYY-MM-DD. По умолчанию — вчера. TZ Asia/Tashkent.
todateКонец периода YYYY-MM-DD. По умолчанию — сегодня.
limitint1–1000. По умолчанию 200.
cursorstringКурсор из next_cursor предыдущего ответа.

Поля звонка

ПолеОписание
uniqueidУникальный ID звонка (первичный ключ на вашей стороне).
calldateВремя начала, ISO 8601 со смещением.
directionin · out · internal.
src / dstНомер вызывающего / вызываемого.
operatorВнутренний номер оператора.
dispositionANSWERED · NOANSWER · BUSY · FAILED.
billsec / durationСекунды разговора / всего.
has_recordingtrue, если доступна запись.
recording_urlСсылка на аудио (или null).
curl -G https://sipcloud.uz/partner/v1/calls \
  -H "Authorization: Bearer $KEY" \
  --data-urlencode "from=2026-09-18" \
  --data-urlencode "to=2026-09-18" \
  --data-urlencode "limit=500"
$ch = curl_init("https://sipcloud.uz/partner/v1/calls?from=2026-09-18&to=2026-09-18");
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => ["Authorization: Bearer {$KEY}"],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
import requests

r = requests.get(
    "https://sipcloud.uz/partner/v1/calls",
    headers={"Authorization": f"Bearer {KEY}"},
    params={"from": "2026-09-18", "to": "2026-09-18", "limit": 500},
)
data = r.json()
const res = await fetch(
  "https://sipcloud.uz/partner/v1/calls?from=2026-09-18&to=2026-09-18",
  { headers: { Authorization: `Bearer ${KEY}` } }
);
const data = await res.json();
200 · Ответ
{
  "ok": true,
  "count": 200,
  "next_cursor": "eyJjZCI6Ii4uLiJ9",
  "calls": [
    {
      "uniqueid": "mikopbx-1789234256.63825_1b15st",
      "calldate": "2026-09-18T17:30:56+05",
      "direction": "in",
      "src": "781295555",
      "dst": "100",
      "operator": "100",
      "did": "555157500",
      "trunk": "",
      "disposition": "ANSWERED",
      "billsec": 42,
      "duration": 55,
      "has_recording": true,
      "recording_url": "https://sipcloud.uz/partner/v1/recording?uid=mikopbx-1789234256.63825_1b15st"
    }
  ]
}
GET/partner/v1/recording

Запись разговора

Отдаёт аудиофайл записи (audio/mpeg или audio/wav). Поддерживается заголовок Range — можно докачивать и перематывать.

Query-параметры

ПараметрТипОписание
uidобязателенstringuniqueid звонка из ответа /calls.
Скачивайте записи для звонков с has_recording: true. Если запись ещё загружается на портал, вернётся 404 recording not available yet (обычно готова через несколько минут после звонка; хранение 14 дней).
Скачать запись
curl -G https://sipcloud.uz/partner/v1/recording \
  -H "Authorization: Bearer $KEY" \
  --data-urlencode "uid=$UID" \
  -o call.mp3
200 · audio/mpeg (bytes)
# бинарный аудиопоток; сохраняется в call.mp3
# заголовки: Content-Type, Accept-Ranges: bytes
GET/partner/v1/ping

Проверка доступа

Проверяет ключ и возвращает клиента и доступные scope. Удобно для healthcheck интеграции.

Запрос
curl https://sipcloud.uz/partner/v1/ping \
  -H "Authorization: Bearer $KEY"
200 · Ответ
{ "ok": true, "tenant": "ChoCo",
  "scopes": ["calls:read", "recordings:read"] }

Вебхуки

SipCloud отправляет POST с JSON на ваш URL. События: call.ended — один раз на завершённый внешний звонок (примерно через 90 с после окончания, когда собраны все участки); call.updated — если звонок пересчитан позже (растёт revision); recording.ready — запись загружена (session_id, recording_uid).

Заголовки и подпись

X-Event-IdID события. Повторная доставка приходит с тем же ID — обрабатывайте идемпотентно.
X-TimestampВремя подписи (unix). Отклоняйте запросы старше 5 минут — защита от повторов.
X-SignatureHMAC-SHA256(secret, X-Timestamp + "." + тело) в hex.
X-AttemptНомер попытки. Ответ не 2xx — до 5 попыток с паузой 1 мин, 5 мин, 30 мин, 2 ч.
Тело события
{ "event_id": 812345, "type": "call.ended",
  "tenant_id": "…", "session_id": 1574, "revision": 1,
  "schema_version": 2, "occurred_at": "2026-10-01T12:00:00+00",
  "data": { "direction": "in", "outcome": "answered",
            "client": "998903268886", "operator": "101",
            "wait_sec": 16, "talk_sec": 139, "…": "…" } }

Инкрементальная выгрузка

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

  • Запросите /calls за нужный период.
  • Внутри периода листайте по cursor, передавая next_cursor из ответа, пока он не станет null.
  • Сохраняйте максимальный обработанный calldate и uniqueid — дедуплицируйте по uniqueid.
  • Для звонков с has_recording: true скачивайте аудио по recording_url.
Пагинация по курсору
cursor, seen = None, set()
while True:
    r = requests.get(BASE + "/calls",
        headers={"Authorization": f"Bearer {KEY}"},
        params={"from": DAY, "to": DAY, "limit": 1000, "cursor": cursor}).json()
    for c in r["calls"]:
        if c["uniqueid"] in seen:      # дедуп
            continue
        seen.add(c["uniqueid"])
        process(c)
    cursor = r["next_cursor"]
    if not cursor:                     # данные закончились
        break

Быстрый старт

1

Получите ключ

Оператор SipCloud выдаёт read-only ключ и добавляет IP вашего сервера в whitelist.

2

Проверьте доступ

Вызовите /ping с заголовком Authorization: Bearer — убедитесь, что клиент и scope верны.

3

Забирайте данные

Периодически тяните /calls с курсором и скачивайте записи по recording_url.