Полное описание API
1 Основная информация
Система предоставляет программный интерфейс (публичный API) для интеграции с внешними системами, скриптами и другими инструментами. Публичный API позволяет автоматизировать получение ключевых метаданных платформы.
Публичный API разделён на две базовые поверхности:
| Поверхность | Базовый путь | Модули | Аудитория |
| Публичный RMD API | /rmd-api/v1 | rmd-api | Внешние интеграции, AI-агенты |
| Публичный DF API | /df-api/v1 | df-api (витрины, подключения, группы измерений, таблицы фактов, связи, сводный экспорт РПИ) | Внешние интеграции, AI-агенты |
Базовые принципы, применяемые ко всем вызовам публичного API:
| Принцип | Описание |
| Версионирование в URL | Публичный API версионируется через путь URL (/v1/). Внутри одной мажорной версии контракт расширяется аддитивно — новые опциональные поля могут появляться, существующие не удаляются и не переименовываются |
| Версионирование данных | Каждый эндпоинт, работающий с РПИ, таблицами фактов, витринами или подключениями, привязан к версии проекта |
| Read-only | Публичный API не изменяет данных, не выполняет запросов в подключённых СУБД и не раскрывает учётных данных СУБД |
| Контроль лицензии | Весь публичный API доступен только при действующей лицензии вызывающей компании |
| Аудит | Каждая попытка аутентификации в публичном API — успешная или неуспешная — записывается в журнал аудита |
Базовый URL зависит от среды развертывания:
| Среда | Базовый URL |
|---|---|
| On-Premise | https://${YOUR_SERVER_HOST}/api |
| Cloud | https://api.[URL системы в облаке] |
Примечание:
${YOUR_SERVER_HOST}— это адрес вашего сервера, указанный в переменной окруженияSERVER_HOST.
Для всех запросов к API требуется передача заголовка с API-ключом:
| Заголовок | Тип | Обязательный | Описание |
|---|---|---|---|
X-Api-Key |
string |
Да | Ваш API-ключ, полученный при создании |
2 Аутентификация и авторизация
2.1 API-ключ
Публичный API использует долгоживущие API-ключи, генерируемые через управление пользователями. У каждого пользователя в любой момент времени активно не более одного ключа; генерация нового замещает предыдущий. Plaintext ключа показывается оператору один раз и не сохраняется — в таблице api_key хранится только bcrypt-хэш.
ApiKeyGuard (применён ко всем эндпоинтам rmd-api/v1 и df-api/v1) выполняет следующие проверки в указанном порядке на каждом запросе:
-
Наличие API-ключа. Если заголовок X-Api-Key отсутствует → 401 API_KEY.KEY_MISSING.
-
Валидность API-ключа. Переданный ключ bcrypt-сравнивается с каждым сохранённым хэшем; при несовпадении → 401 API_KEY.INVALID_KEY.
-
Статус пользователя. Если найденный пользователь не в статусе ENABLED → 403 API_KEY.ACCOUNT_LOCKED.
-
IP-фильтрация. IP клиента (из X-Forwarded-For, иначе из соединения) проверяется по белому и чёрному спискам компании. Заблокировано → 403 API_KEY.IP_BLOCKED.
-
Наличие компании. Если у пользователя нет компании → 403 API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE.
-
Действующая лицензия. Вызывается LicenseService.checkLimitedAndThrow(companyId). При сбое → 403 API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE. См. раздел 5.
-
Аудит. Запись об успехе или неудаче записывается в журнал аудита (тип API_ACCESS, действие API_ACCESS_SUCCESSFUL / API_ACCESS_FAILED).
2.2 Роли
Публичный API доступен только на чтение для всех ролей — RolesGuard к нему не применяется, поскольку каждая операция — чтение. Роль пользователя по-прежнему важна для аутентификации ApiKeyGuard (пользователь должен быть в статусе ENABLED).
2.3 IP-фильтрация
IP-фильтрация реализована в CompanyService.checkIp(user, ip) и вызывается на каждом запросе публичного API в составе ApiKeyGuard.
Компания может включить белый список IP, чёрный список IP или оба. Каждый список поддерживает либо точные IPv4-адреса, либо CIDR-диапазоны. При включённом белом списке отклоняются запросы с IP, не входящих в список; при включённом чёрном — запросы с IP, входящих в список. Неуспешные проверки IP пишутся в журнал аудита как API_ACCESS / API_ACCESS_FAILED.
2.4 Throttling
Лимит запросов на API-ключ для публичного API находится в стадии реализации. После реализации успешные ответы будут содержать заголовки X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, а превышение лимита будет возвращать 429 Too Many Requests с кодом RATE_LIMIT_EXCEEDED.
2.5 Контроль лицензии
ApiKeyGuard вызывает LicenseService.checkLimitedAndThrow после аутентификации и IP-фильтра. Вся поверхность публичного API отклоняется при отсутствии действующей лицензии. Возвращаемая ошибка одинакова независимо от конкретной причины: 403 Forbidden с кодом API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE. См. раздел 5 для условий и порядка восстановления.
2.6 Транспортная безопасность
Все эндпоинты публичного API должны обслуживаться по HTTPS с TLS 1.2 или выше. Шаблон on-premises-развёртывания поднимает nginx с терминированием TLS перед бэкендом; сам бэкенд слушает обычный HTTP внутри доверенной сети.
API-ключи хранятся как bcrypt-хэши и никогда не возвращаются в ответах. Полный список полей, не возвращаемых ответами, см. в разделе 6.
3 Общие соглашения по запросам и ответам
3.1 Формат запроса
| Свойство | Значение |
| Content-Type | application/json для JSON-тел |
| Charset | UTF-8 |
| Заголовок аутентификации | X-Api-Key: |
| Язык | Опциональный Accept-Language; query-параметр language имеет приоритет |
3.2 Пагинация
List-эндпоинты используют единую форму пагинации. Параметры query:
| Параметр | Тип | По умолчанию | Описание |
| page | integer | 1 | Номер страницы (нумерация с 1) |
| pageSize | integer | 20 | Максимум 100 на публичном API |
| Параметры фильтра | разные | — | Конкретные фильтры на эндпоинт (описаны inline) |
Конверт ответа (list-эндпоинты DF API):
{ "data_marts": [ /* items */ ], "pagination": { "page": 1, "pageSize": 20, "total": 45, "total_pages": 3 } }
Конверт ответа (list-эндпоинты RMD API):
{ "measures": [ /* items */ ], "pagination": { "total": 42, "page": 1, "pageSize": 20, "totalPages": 3 } }
3.3 Локализация
Ответы публичного API учитывают query-параметр language (ru или en). Локализуются только описательные текстовые значения (метки статусов, типов, выпадающих списков); ключи JSON всегда на английском.
3.4 Временные метки
Все временные метки в ответах используют формат ISO 8601 (YYYY-MM-DDTHH:MM:SSZ).
3.5 Форматы вывода
Публичный RMD API и эндпоинты модели данных / экспорта РПИ публичного DF API поддерживают ?format=json (по умолчанию) и ?format=xlsx. При xlsx тело ответа имеет вид { "downloadUrl": "<подписанная ссылка S3, действует 15 минут>" }. Эндпоинты витрин и подключений в DF API возвращают только JSON. Некорректное значение format на эндпоинте DF API возвращает 400 Bad Request с кодом DF_API.INVALID_FORMAT.
4 Обработка ошибок
Публичный API использует единый JSON-конверт:
{ "error": { "code": "string", "message": "string", "details": {} } }
Стандартные HTTP-коды:
| Код | Значение |
| 200 | Успех |
| 400 | Bad Request — нарушение валидации, параметра или бизнес-правила |
| 401 | Unauthorized — отсутствует или некорректен API-ключ |
| 403 | Forbidden — IP заблокирован, учётка заблокирована, лицензия невалидна |
| 404 | Not Found — ресурс не существует или недоступен вызывающему |
| 429 | Too Many Requests — превышен лимит (планируется) |
| 500 | Internal Server Error |
Полный каталог кодов — в разделе 10.
5 Проверка лицензии
Проверка лицензии — единственная точка контроля, решающая, разрешён ли публичный API-доступ компании. Она вызывается ApiKeyGuard после аутентификации и IP-фильтра.
LicenseService.checkLimitedAndThrow(companyId) выбрасывает 403 Forbidden при выполнении любого из условий для компании вызывающего:
| Условие | Описание |
| У компании отсутствует запись лицензии | license равен null |
| Тип лицензии — NO_LICENSE | Заглушка, назначаемая по умолчанию до активации |
| Статус лицензии — INVALID | Истекла, не была валидирована или отозвана |
| Статус лицензии — SUSPENDED | Превышены лимиты лицензии (пользователи, проекты, версии) |
Публичный API мапит все эти условия в одну ошибку, чтобы не раскрывать детали состояния лицензии:
| HTTP | Код | Тело |
| 403 | API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE | { "error": { "code": "API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE", "message": "API is not available without a valid license", "details": {} } } |
Неуспешная попытка доступа из-за невалидной лицензии записывается в журнал аудита (тип API_ACCESS, действие API_ACCESS_FAILED).
Чтобы восстановить доступ к API, администратор компании (или, в облачных инсталляциях, Super Admin) должен загрузить действующую лицензию через экран управления лицензиями и удостовериться, что компания не выходит за лимиты по пользователям, проектам и версиям соответствующего тарифа.
6 Ограничения безопасности
Следующие поля никогда не включаются в ответы публичного API:
| Поле | Причина |
| password | Учётные данные подключения к СУБД и пароль пользователя |
| ssl_certificate, caCertificateFileName, clientCertificateFileName | Сертификатный материал |
| ssl_key, clientPrivateKeyFileName | Закрытые ключи |
| token, apiKey, key | JWT, refresh-токены, API-ключи, сессионные токены |
| connection_string | Может содержать встроенные учётные данные |
| signature (сущность License) | HMAC лицензии; никогда не возвращается клиенту |
| Любые пользовательские поля учётных данных | Чувствительные значения, заданные пользователем |
Идентификаторы, намеренно раскрываемые как несекретный контекст:
| Поле | Причина допуска |
| username (подключение СУБД) | Имя пользователя СУБД — необходимо для построения SQL, не является секретом |
| host, port, database, schema | Сетевые координаты, известные любому, у кого есть доступ на чтение модели данных |
Шифрование данных в покое:
| Поле | Хранилище | Шифр |
| API-ключ | api_key.key | bcrypt |
| Пароль удалённой СУБД | remote_db.password | AES (ключ управляется приложением) |
| SSL-файлы удалённой СУБД | объектное хранилище (MinIO/S3) | server-side encryption |
| Подпись лицензии | license.signature | HMAC; проверяется, не возвращается |
7 Публичный RMD API (rmd-api/v1)
Базовый путь: /rmd-api/v1. Аутентификация: X-Api-Key. Все эндпоинты — read-only и контролируются лицензией компании (раздел 5).
7.1 Список проектов
GET /projects
Параметры query: page, pageSize, format.
Ответ (200 OK):
{ "projects": [ { "id": 12, "name": "Sales Analytics", "description": "Production sales warehouse" } ], "pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 } }
7.2 Список версий
GET /projects/{id}/versions
Параметры query: page, pageSize, format.
{ "versions": [ { "id": 33, "name": "Q4 2025", "is_global": true } ], "pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 } }
is_global означает опубликованную версию проекта.
7.3 Получение показателей
GET /projects/{id}/versions/{versionId}/measures
Параметры query: language, format, page, pageSize.
{ "measures": [ { "row_number": 1, "group": "Revenue", "block": "Sales", "measure_name": "Total revenue", "measure_description": "Gross revenue across all channels", "original_source_type": "Database", "original_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "amount" }, "display_data_type": "Numeric", "measure_type": "base", "formula": null, "status": "Active" } ], "pagination": { "total": 42, "page": 1, "pageSize": 20, "totalPages": 3 } }
Состав возвращаемых ключей соответствует колоночной раскладке РПИ (metricsIdToKeyMap в сервисе). Поля select возвращаются как локализованные подписи; формульные поля содержат ссылки, заменённые с числовых ID на читаемые имена; поля select_db (источник) — структурированный объект с разрешёнными именем подключения, базой данных, схемой, таблицей и колонкой.
При запросе format=xlsx тело ответа имеет вид { "downloadUrl": "<подписанная ссылка S3, действует 15 минут>" }.
7.4 Получение измерений
GET /projects/{id}/versions/{versionId}/dimensions
Возвращает строки измерений в том же конверте, с раскладкой колонок измерений (measurementIdToKeyMap). Параметры query: language, format, page, pageSize.
{ "dimensions": [ { "row_number": 1, "group": "Customer", "block": "Profile", "dimension_name": "Customer name", "dimension_type": "primary", "dimension_group": "Customers", "display_data_type": "Text", "source_data_type": "VARCHAR(255)", "status": "Active" } ], "pagination": { "total": 18, "page": 1, "pageSize": 20, "totalPages": 1 } }
7.5 Получение фактов
GET /projects/{id}/versions/{versionId}/facts
Возвращает строки фактов (factsIdToKeyMap). Параметры query: language, format, page, pageSize.
{ "facts": [ { "row_number": 1, "group": "Sales", "block": "Orders", "fact_name": "Order line", "fact_description": "An individual line item on a sales order", "original_source_type": "Database", "original_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_order_line", "column": null }, "fact_type": "primary" } ], "pagination": { "total": 6, "page": 1, "pageSize": 20, "totalPages": 1 } }
7.6 Получение полного РПИ
GET /projects/{id}/versions/{versionId}/rmd
Возвращает показатели, измерения и факты в одном payload, страничит каждую секцию независимо.
{ "measures": [ /* см. 7.3 */ ], "dimensions":[ /* см. 7.4 */ ], "facts": [ /* см. 7.5 */ ], "pagination": { "measures": { "total": 42, "page": 1, "pageSize": 20, "totalPages": 3 }, "dimensions": { "total": 18, "page": 1, "pageSize": 20, "totalPages": 1 }, "facts": { "total": 6, "page": 1, "pageSize": 20, "totalPages": 1 } } }
При format=xlsx показатели и измерения объединяются в одну книгу с колонкой type, различающей строки.
8 Публичный DF API (df-api/v1)
Базовый путь: /df-api/v1. Аутентификация: X-Api-Key. Все эндпоинты — read-only.
DF API сгруппирован в шесть логических областей: витрины, подключения, группы измерений, таблицы фактов, связи и сводный экспорт РПИ. Эндпоинты витрин и подключений возвращают только JSON; эндпоинты групп измерений, таблиц фактов, связей и сводного экспорта дополнительно поддерживают ?format=xlsx (см. раздел 3.5).
8.1 Список витрин
GET /projects/{project_id}/versions/{version_id}/data-marts
Параметры query:
| Параметр | Тип | Описание |
| page, pageSize, language | — | См. раздел 3 |
| type | string | Фильтр по типу витрины: with_grouping, without_grouping, with_grouping_and_pivoting |
| merge_type | string | Фильтр по типу слияния: union, join |
| search | string | Поиск без учёта регистра по подстроке в имени и описании |
Ответ (200 OK):
{ "data_marts": [ { "id": "57", "name": "Monthly Sales Mart", "description": "Aggregated by month and region", "owner": "Pavel Shalavin", "created_at": "2026-04-15T08:30:00Z", "type": "with_grouping", "merge_type": "union", "source_fact_table_count": 2, "has_physical_view": true } ], "pagination": { "page": 1, "pageSize": 20, "total": 45, "total_pages": 3 } }
8.2 Получение деталей витрины
GET /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}
Возвращает полную конфигурацию витрины.
{ "id": "57", "name": "Monthly Sales Mart", "description": "Aggregated by month and region", "owner": "Pavel Shalavin", "created_at": "2026-04-15T08:30:00Z", "type": "with_grouping", "merge_type": "union", "source_fact_tables": [ { "id": "11", "name": "fact_sales", "description": "Primary sales facts" } ], "selected_measures": [ { "instance_id": "204", "measure_id": "501", "measure_name": "Total revenue", "description": "Gross revenue", "formula": "SUM([Amount])", "data_type": "Numeric", "display_name": "Revenue", "aggregation_configuration": { "type": "default", "group_by_fields": [] }, "source_fact_table_id": "11" } ], "selected_facts": [ { "fact_id": "611", "fact_name": "Order line", "data_type": "Numeric", "display_name": "Order line", "include_in_result": true, "filter_condition": null, "source_fact_table_id": "11" } ], "selected_dimensions": [ { "dimension_id": "722", "dimension_name": "Region", "description": "Geographic region", "data_type": "Text", "display_name": "Region", "include_in_result": true, "filter_condition": null, "source_fact_table_id": "11", "source_dimension_group_id": "9", "source_dimension_group_name": "Geography" } ], "physical_view": { "exists": false, "type": null, "database": null, "schema": null, "name": null, "created_at": null } }
Для транспонированных витрин (type = "with_grouping_and_pivoting") ответ дополнительно содержит selected_measure_attributes, в котором неявный ключ транспонирования Measure name всегда добавлен в начало:
"selected_measure_attributes": [ { "attribute_id": "3", "attribute_name": "Measure name" }, { "attribute_id": "27", "attribute_name": "Measure description" } ]
Семантика полей:
| Поле | Примечания |
| merge_type | null для витрин, не объединяющих несколько таблиц фактов |
| instance_id | Уникальный идентификатор экземпляра показателя |
| aggregation_configuration.type | default, none, custom или global |
| aggregation_configuration.group_by_fields | Идентификаторы column-value полей группировки показателя |
| include_in_result | false — элемент используется только для фильтрации, не попадает в проекцию |
| filter_condition | Выражение фильтра с заменой column-value-ссылок на имена; null, если фильтра нет |
| source_dimension_group_id / source_dimension_group_name | Разрешаются по модели данных, даже если группа измерений не указана явно в атрибутах витрины |
8.3 Получение метаданных физического представления
GET /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}/view
Возвращает метаданные физического объекта, материализованного DataForge для витрины. Подключение к целевой СУБД не выполняется.
При наличии физического представления:
{ "exists": true, "type": "materialized_view", "database": "analytics_db", "schema": "marts", "name": "monthly_sales", "created_at": "2026-04-21T09:00:00Z", "connection": { "id": "3", "name": "Production PostgreSQL", "db_type": "postgres" } }
При отсутствии:
{ "exists": false, "type": null, "database": null, "schema": null, "name": null, "created_at": null, "connection": null }
type принимает одно из значений regular_view, materialized_view, table.
8.4 Генерация SQL-скрипта
POST /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}/generate-sql
Генерирует выражение SQL SELECT. SQL только формируется и возвращается — он не выполняется.
Опциональное тело запроса:
{ "limit": 100, "offset": 0 }
| Параметр | Тип | Описание |
| limit | integer (>0) | Добавляет ограничение строк в синтаксисе диалекта (LIMIT n OFFSET m для PostgreSQL/ClickHouse, OFFSET m ROWS FETCH NEXT n ROWS ONLY для MS SQL). offset без limit игнорируется |
| offset | integer (≥0) | Смещение строк; имеет смысл вместе с limit |
Для MS SQL, если в SQL отсутствует ORDER BY, сервис добавляет ORDER BY (SELECT NULL) для синтаксической валидности OFFSET … FETCH NEXT.
Ответ (200 OK):
{ "sql_script": "SELECT ...\nFROM ...\nGROUP BY ...\nLIMIT 100 OFFSET 0", "target_db_type": "postgres", "validation_errors": [] }
Если генерация SQL завершается ошибкой (повреждённые формулы или отсутствие привязок), ответ остаётся HTTP 200, а сбой описывается в validation_errors:
{ "sql_script": "", "target_db_type": null, "validation_errors": [ { "code": "SQL_GENERATION_FAILED", "message": "Measure 'Revenue' references an unresolved field" } ] }
Такая форма позволяет клиенту отличить «витрина не может сейчас сформировать SQL» от «витрины не существует» (которая возвращает 404).
8.5 Список подключений
GET /projects/{project_id}/versions/{version_id}/connections
Параметры query:
| Параметр | Тип | Описание |
| page, pageSize, language | — | См. раздел 3 |
| db_type | string | Фильтр по типу СУБД: postgresql, clickhouse, sqlserver |
| status | string | Фильтр по статусу: active, inactive, never_verified, failed |
Ответ (200 OK):
{ "connections": [ { "id": "1", "name": "Production PostgreSQL", "db_type": "postgresql", "host": "db.production.company.com", "port": 5432, "database": "analytics_db", "schema": "public", "username": "analytics_user", "status": "active", "last_updated_at": "2026-04-15T08:30:00Z" }, { "id": "2", "name": "Legacy SQL Server", "db_type": "sqlserver", "host": "sqlserver.legacy.company.com", "port": 1433, "database": "legacy_warehouse", "schema": null, "username": "etl_service", "status": "active", "last_updated_at": "2026-04-14T10:00:00Z" } ], "pagination": { "page": 1, "pageSize": 20, "total": 8, "total_pages": 1 } }
Замечания:
-
schema возвращается только для PostgreSQL. Для MS SQL Server схема включается в имя таблицы в других местах системы; для ClickHouse вместо схемы используется имя базы данных.
-
status рассчитывается из внутреннего состояния. Приоритет имеет never_verified — без отметки об успешном обновлении система не может утверждать состояние. failed соответствует внутреннему UPDATE_ERROR. inactive соответствует DISABLED. В остальных случаях статус — active.
8.6 Получение деталей подключения
GET /projects/{project_id}/versions/{version_id}/connections/{connection_id}
Параметры query:
| Параметр | Тип | По умолчанию | Описание |
| language | string | en | Локализация |
| include_db_schema | boolean | false | При true облегчённый массив db_tables заменяется полным объектом db_schema |
Ответ (200 OK) — include_db_schema=false:
{ "id": "1", "name": "Production PostgreSQL", "db_type": "postgresql", "host": "db.production.company.com", "port": 5432, "database": "analytics_db", "schema": "public", "username": "analytics_user", "status": "active", "last_updated_at": "2026-04-15T08:30:00Z", "db_tables": [ { "name": "fact_sales" }, { "name": "dim_customer" } ] }
Ответ (200 OK) — include_db_schema=true:
{ "id": "1", "name": "Production PostgreSQL", "db_type": "postgresql", "host": "db.production.company.com", "port": 5432, "database": "analytics_db", "schema": "public", "username": "analytics_user", "status": "active", "last_updated_at": "2026-04-15T08:30:00Z", "db_schema": { "tables": [ { "table_name": "fact_sales", "schema": "public", "columns": [ { "column_name": "sale_id", "data_type": "int" }, { "column_name": "sale_date", "data_type": "datetime" }, { "column_name": "amount", "data_type": "decimal(18,2)" } ] } ] } }
8.7 Получение схемы подключения
GET /projects/{project_id}/versions/{version_id}/connections/{connection_id}/schema
Возвращает только закешированную схему (таблицы и колонки) подключения.
{ "id": "1", "name": "Production PostgreSQL", "db_type": "postgresql", "last_updated_at": "2026-04-15T08:30:00Z", "schema": { "tables": [ { "table_name": "fact_sales", "schema": "public", "columns": [ { "column_name": "sale_id", "data_type": "int" }, { "column_name": "sale_date", "data_type": "datetime" } ] } ] } }
Возвращаемая схема — закешированный снимок, снятый при настройке подключения или ручном обновлении. Он не отражает изменения целевой СУБД в реальном времени. Поле last_updated_at фиксирует момент снятия снимка.
8.8 Список групп измерений
GET /projects/{project_id}/versions/{version_id}/dimension-groups
Возвращает группы измерений (справочники) текущей версии проекта с их первичным ключом, составом измерений и идентификаторами таблиц фактов, ссылающихся на группу.
Параметры query: page, pageSize, language, format.
Ответ (200 OK):
{ "dimension_groups": [ { "id": "9", "name": "Geography", "description": "Geographic regions and countries", "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }, "dimensions": [ { "id": "722", "name": "Region", "level": 1, "data_type": "Text", "physical_column": "region_name" }, { "id": "723", "name": "Country", "level": 2, "data_type": "Text", "physical_column": "country_code" } ], "related_fact_tables": ["11", "14"], "created_at": "2026-04-15T08:30:00Z" } ], "pagination": { "page": 1, "pageSize": 20, "total": 12, "total_pages": 1 } }
| Поле | Примечания |
| primary_key | null, если у группы не разрешён физический ключ. db и schema могут быть null для типов источников, не разделяющих их (например, ClickHouse) |
| dimensions | Члены группы, упорядочены по уровню иерархии. data_type — локализованный отображаемый тип; physical_column — имя колонки в исходной таблице |
| related_fact_tables | Строковые ID таблиц фактов, ссылающихся на группу; полная конфигурация доступна через 8.10 / 8.11 |
При format=xlsx ответ — { "downloadUrl": "<подписанная ссылка S3>" }, книга содержит один лист Dimension Groups со строками списка.
8.9 Получение деталей группы измерений
GET /projects/{project_id}/versions/{version_id}/dimension-groups/{dimension_group_id}
Возвращает полную конфигурацию одной группы измерений, включая связанные таблицы фактов с колонками внешних ключей.
Параметры query: language, format.
Ответ (200 OK):
{ "id": "9", "name": "Geography", "description": "Geographic regions and countries", "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }, "related_fact_tables": [ { "fact_table_id": "11", "fact_table_name": "fact_sales", "foreign_key_column": "region_id" }, { "fact_table_id": "14", "fact_table_name": "fact_inventory", "foreign_key_column": "region_id" } ], "created_at": "2026-04-15T08:30:00Z", "updated_at": "2026-04-21T11:15:00Z" }
Если группа не существует в указанной версии, API возвращает 404 Not Found с кодом DF_API.DIMENSION_GROUP_NOT_FOUND.
8.10 Список таблиц фактов
GET /projects/{project_id}/versions/{version_id}/fact-tables
Возвращает таблицы фактов текущей версии проекта со счётчиками элементов.
Параметры query: page, pageSize, language, format.
Ответ (200 OK):
{ "fact_tables": [ { "id": "11", "name": "fact_sales", "description": "Primary sales facts", "owner": "Pavel Shalavin", "created_at": "2026-04-15T08:30:00Z", "measures_count": 5, "dimensions_count": 3, "facts_count": 2, "verification_filters_count": 2, "related_dimension_groups_count": 1 } ], "pagination": { "page": 1, "pageSize": 20, "total": 6, "total_pages": 1 } }
dimensions_count учитывает только измерения, добавленные непосредственно в таблицу фактов (т. е. не входящие ни в одну группу измерений). Измерения, унаследованные через группы, доступны через related_dimension_groups_count и через эндпоинт деталей.
При format=xlsx книга содержит один лист Fact Tables.
8.11 Получение деталей таблицы фактов
GET /projects/{project_id}/versions/{version_id}/fact-tables/{fact_table_id}
Возвращает полную конфигурацию таблицы фактов — её показатели, измерения, факты, группы измерений (с первичными и внешними ключами) и фильтры верификации.
Параметры query:
| Параметр | Тип | По умолчанию | Описание |
| language | string | en | Локализация |
| include_dependencies | boolean | false | При true каждая мера несёт рекурсивное дерево dependencies, разрешающее ссылки формулы до целевых мер |
| format | string | json | json или xlsx |
Ответ (200 OK):
{ "id": "11", "name": "fact_sales", "description": "Primary sales facts", "owner": "Pavel Shalavin", "created_at": "2026-04-15T08:30:00Z", "updated_at": "2026-04-21T09:00:00Z", "measures": [ { "id": "501", "name": "Total revenue", "description": "Gross revenue", "formula": "SUM([Amount])", "data_type": "Numeric", "measure_type": "base", "dependencies": null } ], "dimensions": [ { "id": "801", "name": "Sale date", "description": "Calendar date of the sale", "data_type": "Date", "physical_column": "sale_date", "is_from_dimension_group": false, "dimension_group_id": null } ], "facts": [ { "id": "611", "name": "Order line", "description": null, "data_type": "Numeric", "fact_type": "primary", "formula": null, "physical_column": "amount" } ], "dimension_groups": [ { "id": "9", "name": "Geography", "description": "Geographic regions and countries", "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }, "foreign_key": { "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" } } ], "verification_filters": [ { "id": "301", "name": "Valid sales only", "description": "Excludes test and cancelled orders", "conditions": "[Status] != 'cancelled'", "is_valid": true, "invalid_reason": null } ] }
| Поле | Примечания |
| measure_type | base (источник напрямую) или calculated (производный по формуле) |
| fact_type | primary, derived или constant |
| is_from_dimension_group | true — измерение унаследовано из связанной группы; false — задано прямо в таблице фактов |
| dependencies | Присутствует и заполняется только при include_dependencies=true. Узел содержит id, name, type, formula и рекурсивный массив dependencies |
| verification_filters[].is_valid | false, если выражение фильтра не разрешается (отсутствующая ссылка, ошибка синтаксиса). invalid_reason несёт локализованное объяснение |
При format=xlsx книга содержит пять листов: Measures, Dimensions, Facts, Dimension Groups, Verification Filters.
Если таблица фактов не существует в указанной версии, API возвращает 404 Not Found с кодом DF_API.FACT_TABLE_NOT_FOUND.
8.12 Список связей
GET /projects/{project_id}/versions/{version_id}/relationships
Возвращает связи внешних ключей между таблицами фактов и группами измерений в текущей версии проекта.
Параметры query:
| Параметр | Тип | Описание |
| page, pageSize, language | — | См. раздел 3 |
| fact_table_id | integer | Фильтр: связи с указанной таблицей фактов в качестве источника |
| dimension_group_id | integer | Фильтр: связи с указанной группой измерений в качестве цели |
| format | string | json или xlsx |
Ответ (200 OK):
{ "relationships": [ { "id": "1101", "source_fact_table": { "id": "11", "name": "fact_sales" }, "target_dimension_group": { "id": "9", "name": "Geography" }, "foreign_key": { "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" }, "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }, "relationship_type": "many_to_one", "created_at": "2026-04-15T08:30:00Z" } ], "pagination": { "page": 1, "pageSize": 20, "total": 4, "total_pages": 1 } }
relationship_type описывает кратность от источника (таблицы фактов) к цели (группе измерений). foreign_key и primary_key могут быть null, если соответствующий маппинг неполный; в этом случае связь видна, но не пригодна для построения SQL-join'а.
8.13 Получение деталей связи
GET /projects/{project_id}/versions/{version_id}/relationships/{relationship_id}
Возвращает одну связь с описательными полями обеих сторон.
Параметры query: language, format.
Ответ (200 OK):
{ "id": "1101", "source_fact_table": { "id": "11", "name": "fact_sales", "description": "Primary sales facts" }, "target_dimension_group": { "id": "9", "name": "Geography", "description": "Geographic regions and countries", "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" } }, "foreign_key": { "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" }, "primary_key": { "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }, "relationship_type": "many_to_one", "created_at": "2026-04-15T08:30:00Z", "updated_at": "2026-04-21T11:15:00Z" }
Если связь не существует в указанной версии, API возвращает 404 Not Found с кодом DF_API.RELATIONSHIP_NOT_FOUND.
8.14 Сводный экспорт РПИ
GET /projects/{project_id}/versions/{version_id}/rmd
Возвращает полный снимок метаданных версии проекта — содержимое РПИ (показатели, измерения, факты) и модели данных (группы измерений, таблицы фактов, связи) — в одном payload плюс отметку времени exported_at. Предназначен для внешних каталогов и полной синхронизации состояния.
Параметры query: language, format.
Ответ (200 OK):
{ "project": { "id": "12", "name": "Sales Analytics", "description": "Production sales warehouse" }, "version": { "id": "33", "name": "Q4 2025", "is_global": true }, "measures": [ /* такая же форма строк, как в 7.3 */ ], "dimensions": [ /* такая же форма строк, как в 7.4 */ ], "facts": [ /* такая же форма строк, как в 7.5 */ ], "dimension_groups": [ /* такая же форма строк, как в 8.8 */ ], "fact_tables": [ /* такая же форма строк, как в 8.10 */ ], "relationships": [ /* такая же форма строк, как в 8.12 */ ], "exported_at": "2026-05-05T08:30:00Z" }
При format=xlsx книга содержит шесть листов — Measures, Dimensions, Facts, Dimension Groups, Fact Tables, Relationships — а тело ответа имеет вид { "downloadUrl": "<подписанная ссылка S3>" }.
Этот эндпоинт — публичный аналог унаследованного rmd-api/v1 /rmd (7.6), расширенный сущностями модели данных. Любой из эндпоинтов можно использовать для содержимого РПИ; модель данных раскрывает только эндпоинт DF API.
9 Журналирование аудита
События аутентификации в публичном API записываются с типом, действием, актором, IP-источником, целевым объектом и свободным полем what:
| Событие | Тип | Действие | Триггер |
| Успешная аутентификация в API | API_ACCESS | API_ACCESS_SUCCESSFUL | Запрос публичного API прошёл все проверки ApiKeyGuard |
| Неуспешная аутентификация в API | API_ACCESS | API_ACCESS_FAILED | Учётка заблокирована, IP заблокирован или лицензия невалидна |
Поля журнала аудита:
| Поле | Значение |
| type | Категория события (API_ACCESS) |
| action | Конкретное действие в категории |
| who | Email актора (владелец API-ключа) |
| from_where | IP клиента (учитывается X-Forwarded-For) |
| where | Имя компании |
| what | Группа эндпоинтов (RmdApi v1 / DfApi v1) |
| before, after | Diff для событий изменения (не используется в read-only API) |
Срок хранения журнала задаётся настройками компании; записи доступны на экране Audit Log пользователям с ролью Company Admin или Super Admin. События аудита внутреннего API (вход в UI, управление лицензиями и т. п.) описаны в главе 8.
10 Справочник кодов ошибок
В таблице сведены коды ошибок публичного API.
| Код | HTTP | Поверхность | Описание |
| API_KEY.KEY_MISSING | 401 | оба | Заголовок X-Api-Key не передан |
| API_KEY.INVALID_KEY | 401 | оба | API-ключ не соответствует ни одному сохранённому |
| API_KEY.INVALID_ENCRYPTED_API_KEY | 400 | оба | Зашифрованный payload ключа не удалось декодировать |
| API_KEY.AUTH_FAILED | 401 | оба | Общий сбой аутентификации |
| API_KEY.ACCOUNT_LOCKED | 403 | оба | Пользователь не в статусе ENABLED |
| API_KEY.IP_BLOCKED | 403 | оба | IP клиента в чёрном списке или вне белого списка |
| API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE | 403 | оба | У компании нет действующей лицензии |
| PROJECT.NOT_FOUND | 404 | rmd-api | Проект не существует или недоступен |
| VERSION.NOT_FOUND | 404 | rmd-api | Версия не существует или недоступна |
| DF_API.INVALID_PARAMETER | 400 | df-api | Параметр пути или query не прошёл валидацию |
| DF_API.PAGE_SIZE_EXCEEDED | 400 | df-api | pageSize превышает 100 |
| DF_API.INVALID_TYPE | 400 | df-api | Некорректное значение фильтра type для витрин |
| DF_API.INVALID_MERGE_TYPE | 400 | df-api | Некорректное значение фильтра merge_type для витрин |
| DF_API.INVALID_DB_TYPE | 400 | df-api | Некорректное значение фильтра db_type для подключений |
| DF_API.INVALID_STATUS | 400 | df-api | Некорректное значение фильтра status для подключений |
| DF_API.INVALID_FORMAT | 400 | df-api | format отличается от json и xlsx |
| DF_API.PROJECT_NOT_FOUND | 404 | df-api | Проект не существует или недоступен |
| DF_API.VERSION_NOT_FOUND | 404 | df-api | Версия не существует или недоступна |
| DF_API.DATA_MART_NOT_FOUND | 404 | df-api | Витрина не существует в указанной версии |
| DF_API.CONNECTION_NOT_FOUND | 404 | df-api | Подключение не существует в указанной версии |
| DF_API.DIMENSION_GROUP_NOT_FOUND | 404 | df-api | Группа измерений не существует в указанной версии |
| DF_API.FACT_TABLE_NOT_FOUND | 404 | df-api | Таблица фактов не существует в указанной версии |
| DF_API.RELATIONSHIP_NOT_FOUND | 404 | df-api | Связь не существует в указанной версии |
| SQL_GENERATION_FAILED | 200 (в validation_errors) | df-api | Витрина не может сейчас сформировать SQL |
| RATE_LIMIT_EXCEEDED | 429 | оба | Превышена квота запросов на ключ (планируется) |
| INTERNAL_ERROR | 500 | оба | Непредвиденная ошибка сервера |