Внешнее API
Документ описывает внешние API SmartCPD, поддерживаемые в текущем релизе:
- Prometheus API для чтения метрик;
- Markup API для загрузки фактических результатов вызовов из клиентского контура;
- IDM API v2 для управления сотрудниками и получения ролей;
- Audit API v2 для чтения платформенного журнала аудита.
Авторизация#
Для авторизации во внешних API используются управляемые API-ключи SmartCPD.
Для подготовки доступа необходимо:
- Создать в портале отдельную роль для интеграции.
- Назначить роли привилегии, необходимые вызываемым методам.
- В разделе
API ключисоздать API-ключ и назначить ему подготовленную роль. - Скопировать выданный токен и сохранить его в защищенном хранилище интеграции.
Токен отображается только при создании или ротации API-ключа и передается в каждом запросе:
Authorization: Bearer API_KEYAPI-ключ создается в контексте клиента. Доступ к tenant-scoped методам ограничивается клиентом API-ключа и его tenant scope. Доступ к каждому методу дополнительно ограничивается привилегиями назначенных ролей.
Для управления API-ключами пользователю портала требуются следующие привилегии:
| Операция | Привилегия |
|---|---|
| Просмотр API-ключей | apiKey.canSee |
| Создание, изменение, ротация, отзыв и удаление API-ключей | apiKey.canEdit |
| Назначение ролей API-ключу | employee.role.canEdit |
| Назначение административных ролей | employee.role.canEdit и employee.role.admin.canEdit |
Общие коды ошибок авторизации:
| Код | Описание |
|---|---|
401 Unauthorized | Bearer-токен отсутствует, недействителен, отозван или просрочен |
403 Forbidden | У API-ключа нет требуемой привилегии или доступа к указанному клиенту |
Prometheus API#
Prometheus API предоставляет доступ к метрикам SmartCPD через публичный адрес портала и путь /prometheus/.
Требуемая привилегия: metrics.canSee.
Пример запроса:
curl -ik \
-H "Authorization: Bearer API_KEY" \
'https://portal.fqdn/prometheus/api/v1/query?query=up'Публично доступны только методы Prometheus, расположенные под /prometheus/api/.
Markup API#
Markup API загружает фактические результаты вызовов от операторов или внешних систем заказчика и связывает их с вызовами CDR.
Требуемая привилегия: cdr.canEdit.
Разметка может связываться с вызовом:
- по SIP Call-ID;
- по Custom Call ID;
- по номеру клиента и времени вызова.
Методы#
| Метод | Назначение |
|---|---|
POST /api/cdr/v1/markup | Загрузка JSON для клиента API-ключа |
POST /api/cdr/v1/markup/csv | Загрузка CSV для клиента API-ключа |
POST /api/cdr/v1/tenant/{tenant_inn}/markup | Загрузка JSON для указанного клиента |
POST /api/cdr/v1/tenant/{tenant_inn}/markup/csv | Загрузка CSV для указанного клиента |
Методы с tenant_inn дополнительно проверяют доступ API-ключа к указанному клиенту.
Загрузка JSON#
POST /api/cdr/v1/markup
Authorization: Bearer API_KEY
Content-Type: application/jsonТело запроса содержит непустой массив записей:
[
{
"call_id": "51f737c0-4de5-0139-ba9f-0ac31e3255d2",
"call_id_type": "sip",
"ts": "2026-07-17T11:29:05Z",
"result": "human",
"original_result": "человек принял звонок",
"source": "operator-system"
}
]Состав полей, их обязательность, формат и допустимые значения приведены в едином справочнике «Поля разметки».
Ограничения запроса:
- размер тела — не более
10 MiB; - количество записей — не более
10 000; - размер каждого строкового поля — не более
4 KiB; - дополнительные неизвестные поля не допускаются;
- все временные значения преобразуются и сохраняются в UTC.
Записи обрабатываются последовательно. Полностью успешный запрос возвращает 200 OK:
{
"accepted_count": 2
}Если часть записей отклонена или не обработана после внутренней ошибки, API возвращает 207 Multi-Status:
{
"accepted_count": 1,
"rejected": [
{
"index": 2,
"record": {
"call_id": "missing-call",
"call_id_type": "sip",
"ts": "2026-07-17T11:30:00Z",
"result": "robot"
},
"reason": "not_found"
}
],
"unprocessed": []
}index — номер записи в исходном массиве, начиная с 1. rejected содержит записи, отклоненные по ожидаемой причине. unprocessed содержит текущую и оставшиеся записи, которые не удалось обработать после внутренней ошибки.
Статусы и все значения reason описаны в справочнике «Коды обработки».
Коды ответа JSON-загрузки:
| Код | Описание |
|---|---|
200 OK | Все записи приняты |
207 Multi-Status | Есть отклоненные или не обработанные записи |
400 Bad Request | Некорректный JSON, пустой массив или нарушение структуры запроса |
401 Unauthorized | API-ключ отсутствует или недействителен |
403 Forbidden | Нет cdr.canEdit или доступа к указанному клиенту |
413 Content Too Large | Превышен лимит тела, количества записей или размера поля |
415 Unsupported Media Type | Content-Type отличается от application/json |
500 Internal Server Error | Обработка не началась из-за внутренней ошибки |
Загрузка CSV#
POST /api/cdr/v1/markup/csv
Authorization: Bearer API_KEY
Content-Type: text/csv; charset=utf-8Состав обязательных столбцов и правила заполнения значений приведены в справочнике «Поля разметки». Дополнительные столбцы, кроме служебных upload_status и reason, не поддерживаются.
CSV должен быть в кодировке UTF-8. Другие кодировки, включая Windows-1251, не поддерживаются; файл нужно пересохранить в UTF-8 до отправки. Параметр charset не выполняет перекодирование. При невалидном UTF-8 сервер возвращает 400 Bad Request с JSON-полем error, содержащим invalid UTF-8 string и расположение проблемного поля; ни одна запись из файла не обрабатывается.
Для CSV действуют те же ограничения тела, количества записей, размера полей и допустимых значений, что и для JSON. Дополнительно размер одной строки CSV ограничен 64 KiB. До начала загрузки файл полностью проверяется на корректность структуры и кодировки UTF-8.
Ответы 200 OK и 207 Multi-Status имеют формат CSV и содержат только отклоненные и не обработанные строки. В ответ добавляются или заменяются столбцы:
| Столбец | Значение |
|---|---|
upload_status | rejected или unprocessed |
reason | Причина обработки из справочника «Коды обработки» |
Полученный файл можно исправить и загрузить повторно.
Заголовки успешного ответа и ответа с частичным результатом:
| Заголовок | Описание |
|---|---|
Content-Type: text/csv; charset=utf-8 | Формат ответа |
Content-Disposition: attachment; filename="markup-problems.csv" | Имя возвращаемого файла |
X-Accepted-Count | Количество принятых строк |
X-Rejected-Count | Количество отклоненных строк |
X-Unprocessed-Count | Количество не обработанных строк |
Коды ответа CSV-загрузки:
| Код | Описание |
|---|---|
200 OK | Все строки приняты; возвращается CSV без проблемных строк |
207 Multi-Status | Возвращается CSV с отклоненными или не обработанными строками |
400 Bad Request | Невалидная кодировка UTF-8, некорректная структура CSV или отсутствует обязательный столбец |
401 Unauthorized | API-ключ отсутствует или недействителен |
403 Forbidden | Нет cdr.canEdit или доступа к указанному клиенту |
413 Content Too Large | Превышен лимит тела, количества строк, строки или поля |
415 Unsupported Media Type | Content-Type отличается от text/csv |
500 Internal Server Error | Обработка не началась из-за внутренней ошибки |
IDM API v2#
IDM API v2 управляет учетными записями сотрудников и предоставляет сведения о ролях. API поддерживает on-premise и multitenant/cloud-сценарии.
Операции с сотрудниками выполняются в контексте клиента, заданного параметром tenant_inn. API-ключ должен иметь доступ к указанному клиенту.
Методы и привилегии#
| Метод | Назначение | Привилегия |
|---|---|---|
GET /api/v2/tenant/{tenant_inn}/idm/users | Получение сотрудников клиента | employee.canSee |
GET /api/v2/idm/roles | Получение ролей | role.canSee |
POST /api/v2/tenant/{tenant_inn}/idm/employee | Создание сотрудника | employee.canEdit |
PATCH /api/v2/tenant/{tenant_inn}/idm/employee | Изменение сотрудника | employee.canEdit |
POST /api/v2/tenant/{tenant_inn}/idm/employee/block | Блокировка сотрудника | employee.canEdit |
POST /api/v2/tenant/{tenant_inn}/idm/employee/unblock | Разблокировка сотрудника | employee.canEdit |
POST /api/v2/tenant/{tenant_inn}/idm/employee/password | Смена пароля сотрудника | employee.password.canEdit |
Для назначения обычных ролей сотруднику дополнительно требуется employee.role.canEdit. Для назначения административных ролей также требуется employee.role.admin.canEdit.
Получение сотрудников#
GET /api/v2/tenant/{tenant_inn}/idm/users
Authorization: Bearer API_KEYОтвет содержит сотрудников указанного клиента:
[
{
"login": "user@example.com",
"name": "Иванов Иван Иванович",
"status": "active",
"roles": ["support"],
"privileges": ["employee.canSee"],
"last_login_time": "2026-07-17T10:30:00Z",
"last_change_password": "2026-07-10T08:15:00Z",
"login_ip_address": "192.0.2.10"
}
]Возможные статусы сотрудника:
active— активный сотрудник.not_active— неактивный сотрудник (административный статус).banned— заблокирован. Устанавливается методом блокировки IDM API и снимается только методом разблокировки IDM API.temporary_banned— временно заблокирован. Устанавливается системой безопасности после превышения лимита неуспешных попыток входа. Снимается при успешном входе после тайм-аута, вручную через IDM APIunblockили через UI пользователем с привилегиейemployee.canEditв пределах доступного tenant.
banned и temporary_banned — это security-статусы. Для temporary_banned в UI предусмотрено отдельное действие «Снять временную блокировку»; banned через UI не снимается.
Получение ролей#
GET /api/v2/idm/roles
Authorization: Bearer API_KEYПример ответа:
[
{
"id": 21,
"name": "idm-api",
"privileges": [
"employee.canSee",
"employee.canEdit",
"employee.password.canEdit",
"employee.role.canEdit",
"role.canSee"
]
}
]Создание сотрудника#
POST /api/v2/tenant/{tenant_inn}/idm/employee
Authorization: Bearer API_KEY
Content-Type: application/jsonПример запроса:
{
"username": "Иванов Иван",
"email": "user@example.com",
"tel": "79999999999",
"password": "SHA512_HASH",
"agreed": true,
"email_deal": false,
"roles": ["support"]
}password содержит SHA-512-хеш пароля в шестнадцатеричном представлении. После создания сотрудник должен сменить пароль при первом входе.
Если пароль при создании сотрудника не соответствует требованиям безопасности, сервер возвращает 422 Unprocessable Entity с JSON-полем message, содержащим причину отказа («Слишком слабый пароль…»). Сотрудник не создаётся.
Поле roles содержит имена ролей, возвращаемые методом получения ролей. При успешном создании API возвращает:
{
"code": 0
}Изменение сотрудника#
PATCH /api/v2/tenant/{tenant_inn}/idm/employee
Authorization: Bearer API_KEY
Content-Type: application/jsonПоиск сотрудника выполняется по обязательному полю login. Остальные поля задают новые значения:
{
"login": "user@example.com",
"username": "Иванов Петр",
"tel": "79999999998",
"roles": ["support"]
}roles содержит полный итоговый список ролей сотрудника. Пароль и системные поля этим методом не изменяются.
Блокировка сотрудника#
POST /api/v2/tenant/{tenant_inn}/idm/employee/block
Authorization: Bearer API_KEY
Content-Type: application/json
{
"login": "user@example.com"
}После успешной блокировки статус сотрудника становится banned («Заблокирован»).
Разблокировка сотрудника#
POST /api/v2/tenant/{tenant_inn}/idm/employee/unblock
Authorization: Bearer API_KEY
Content-Type: application/json
{
"login": "user@example.com"
}После успешной разблокировки статус сотрудника становится active («Активный»).
Смена пароля сотрудника#
POST /api/v2/tenant/{tenant_inn}/idm/employee/password
Authorization: Bearer API_KEY
Content-Type: application/json
{
"login": "user@example.com",
"password": "SHA512_HASH"
}password содержит SHA-512-хеш нового пароля в шестнадцатеричном представлении. После выполнения операции сотрудник должен сменить пароль при следующем входе.
Коды ответа IDM API#
| Код | Описание |
|---|---|
200 OK | Операция выполнена |
400 Bad Request | Некорректный запрос |
401 Unauthorized | API-ключ отсутствует или недействителен |
403 Forbidden | Нет требуемой привилегии или доступа к клиенту |
404 Not Found | Клиент, сотрудник или роль не найдены |
409 Conflict | Конфликт состояния или уникальности данных |
422 Unprocessable Entity | Запрос не соответствует схеме или пароль при создании сотрудника не соответствует требованиям безопасности |
500 Internal Server Error | Внутренняя ошибка сервиса |
Изменения сотрудников и паролей записываются в журнал аудита.
Audit API v2#
В текущем релизе внешний Audit API предоставляет доступ только к платформенному журналу аудита. Tenant-scoped доступ к аудиту не входит в поддерживаемый внешний контракт.
Требуемая привилегия: audit.canSee.
Получение платформенного журнала#
GET /api/v2/platform/audit
Authorization: Bearer API_KEYПараметры запроса:
| Параметр | Описание |
|---|---|
page | Номер страницы, начиная с 1; значение по умолчанию — 1 |
per_page | Количество записей на странице от 1 до 200; значение по умолчанию — 10 |
text | Поиск по содержимому записи, не более 500 символов |
from_date, from_time | Начало временного интервала |
to_date, to_time | Конец временного интервала |
Пример ответа:
{
"items": [
{
"id": 125,
"operation": "change_roles",
"entity": "employee",
"body": "изменение ролей сотрудника user@example.com",
"object_inn": "1234567890",
"object_type": "employee",
"datetime": "2026-07-17T10:30:00Z",
"username": "integration@example.com"
}
]
}Коды ответа:
| Код | Описание |
|---|---|
200 OK | Запрос выполнен |
401 Unauthorized | API-ключ отсутствует или недействителен |
403 Forbidden | Нет привилегии audit.canSee |
422 Unprocessable Entity | Некорректные параметры запроса |
500 Internal Server Error | Внутренняя ошибка сервиса |