Skip to content

Complete API Description

1 Basic Information

The system provides a public API for integration with external systems, scripts, and other tools. The public API allows automating the retrieval of key platform metadata.

The public API is divided into two basic surfaces:

Surface Base Path Modules Audience
Public RMD API /rmd-api/v1 rmd-api External integrations, AI agents
Public DF API /df-api/v1 df-api (data marts, connections, dimension groups, fact tables, relationships, consolidated RMD export) External integrations, AI agents

Basic principles applied to all public API calls:

Principle Description
Versioning in URL Public API is versioned via URL path (/v1/). Within a single major version, the contract is extended additively — new optional fields may appear, existing fields are not removed or renamed
Data versioning Each endpoint working with RMD, fact tables, data marts, or connections is tied to a project version
Read-only Public API does not modify data, does not execute queries in connected DBMS, and does not expose DBMS credentials
License control The entire public API is available only under an active license of the calling company
Audit Each authentication attempt in the public API — successful or unsuccessful — is recorded in the audit log

The base URL depends on the deployment environment:

Environment Base URL
On-Premise https://${YOUR_SERVER_HOST}/api
Cloud https://api.[cloud system URL]

Note: ${YOUR_SERVER_HOST} is your server address specified in the SERVER_HOST environment variable.

All API requests require passing an API key header:

Header Type Required Description
X-Api-Key string Yes Your API key obtained during creation

2 Authentication and Authorization

2.1 API Key

The public API uses long-lived API keys generated through user management. Each user has at most one active key at any time; generating a new key replaces the previous one. The plaintext key is shown to the operator once and is not stored — only the bcrypt hash is stored in the api_key table.

ApiKeyGuard (applied to all rmd-api/v1 and df-api/v1 endpoints) performs the following checks in the specified order on each request:

  1. API key presence. If the X-Api-Key header is missing → 401 API_KEY.KEY_MISSING.
  2. API key validity. The passed key is bcrypt-compared with each stored hash; if no match → 401 API_KEY.INVALID_KEY.
  3. User status. If the found user is not in ENABLED status → 403 API_KEY.ACCOUNT_LOCKED.
  4. IP filtering. The client IP (from X-Forwarded-For, otherwise from the connection) is checked against the company's whitelist and blacklist. If blocked → 403 API_KEY.IP_BLOCKED.
  5. Company presence. If the user has no company → 403 API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE.
  6. Active license. LicenseService.checkLimitedAndThrow(companyId) is called. On failure → 403 API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE. See section 5.
  7. Audit. A record of success or failure is written to the audit log (type API_ACCESS, action API_ACCESS_SUCCESSFUL / API_ACCESS_FAILED).

2.2 Roles

The public API is read-only for all roles — RolesGuard does not apply to it because every operation is a read operation. The user's role remains important for ApiKeyGuard authentication (the user must be in ENABLED status).

2.3 IP Filtering

IP filtering is implemented in CompanyService.checkIp(user, ip) and is called on each public API request as part of ApiKeyGuard.

A company can enable an IP whitelist, an IP blacklist, or both. Each list supports either exact IPv4 addresses or CIDR ranges. When a whitelist is enabled, requests from IPs not in the list are rejected; when a blacklist is enabled, requests from IPs in the list are rejected. Failed IP checks are written to the audit log as API_ACCESS / API_ACCESS_FAILED.

2.4 Throttling

The request limit per API key for the public API is under development. After implementation, successful responses will include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers, and exceeding the limit will return 429 Too Many Requests with code RATE_LIMIT_EXCEEDED.

2.5 License Control

ApiKeyGuard calls LicenseService.checkLimitedAndThrow after authentication and IP filtering. The entire public API surface is rejected when there is no active license. The returned error is the same regardless of the specific reason: 403 Forbidden with code API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE. See section 5 for conditions and recovery procedure.

2.6 Transport Security

All public API endpoints must be served over HTTPS with TLS 1.2 or higher. The on-premises deployment template uses nginx with TLS termination in front of the backend; the backend itself listens on plain HTTP inside a trusted network.

API keys are stored as bcrypt hashes and are never returned in responses. See section 6 for the complete list of fields not returned in responses.

3 Common Request and Response Conventions

3.1 Request Format

Property Value
Content-Type application/json for JSON bodies
Charset UTF-8
Authentication header X-Api-Key:
Language Optional Accept-Language; language query parameter takes precedence

3.2 Pagination

List endpoints use a unified pagination form. Query parameters:

Parameter Type Default Description
page integer 1 Page number (1-indexed)
pageSize integer 20 Maximum 100 on public API
Filter parameters various Endpoint-specific filters (described inline)

Response envelope (DF API list endpoints):

{ "data_marts": [ /* items */ ], "pagination": { "page": 1, "pageSize": 20, "total": 45, "total_pages": 3 } }

Response envelope (RMD API list endpoints):

{ "measures": [ /* items */ ], "pagination": { "total": 42, "page": 1, "pageSize": 20, "totalPages": 3 } }

3.3 Localization

Public API responses respect the language query parameter (ru or en). Only descriptive text values are localized (status labels, type labels, dropdown values); JSON keys are always in English.

3.4 Timestamps

All timestamps in responses use ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ).

3.5 Output Formats

The Public RMD API and data model / RMD export endpoints of the Public DF API support ?format=json (default) and ?format=xlsx. For xlsx, the response body has the form { "downloadUrl": "" }. Data mart and connection endpoints in the DF API return only JSON. An invalid format value on a DF API endpoint returns 400 Bad Request with code DF_API.INVALID_FORMAT.

4 Error Handling

The public API uses a unified JSON envelope:

{ "error": { "code": "string", "message": "string", "details": {} } }

Standard HTTP status codes:

Code Meaning
200 Success
400 Bad Request — validation, parameter, or business rule violation
401 Unauthorized — missing or invalid API key
403 Forbidden — IP blocked, account locked, license invalid
404 Not Found — resource does not exist or is unavailable to the caller
429 Too Many Requests — rate limit exceeded (planned)
500 Internal Server Error

Full error code catalog — see section 10.

5 License Validation

License validation is the single control point that decides whether public API access is allowed for a company. It is called by ApiKeyGuard after authentication and IP filtering.

LicenseService.checkLimitedAndThrow(companyId) throws 403 Forbidden when any of the following conditions is true for the calling company:

Condition Description
Company has no license record license is null
License type is NO_LICENSE Placeholder assigned by default before activation
License status is INVALID Expired, not validated, or revoked
License status is SUSPENDED License limits exceeded (users, projects, versions)

The public API maps all these conditions to a single error to avoid exposing license state details:

HTTP Code Body
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": {} } }

An unsuccessful access attempt due to an invalid license is recorded in the audit log (type API_ACCESS, action API_ACCESS_FAILED).

To restore API access, the company administrator (or Super Admin in cloud installations) must upload an active license via the license management screen and ensure the company does not exceed the user, project, and version limits of the corresponding plan.

6 Security Restrictions

The following fields are never included in public API responses:

Field Reason
password DBMS connection credentials and user password
ssl_certificate, caCertificateFileName, clientCertificateFileName Certificate material
ssl_key, clientPrivateKeyFileName Private keys
token, apiKey, key JWT, refresh tokens, API keys, session tokens
connection_string May contain embedded credentials
signature (License entity) License HMAC; never returned to client
Any custom credential fields Sensitive values set by the user

Identifiers intentionally exposed as non-secret context:

Field Reason for exposure
username (DBMS connection) DBMS username — needed for SQL construction, not a secret
host, port, database, schema Network coordinates, known to anyone with read access to the data model

Data at rest encryption:

Field Storage Cipher
API key api_key.key bcrypt
Remote DBMS password remote_db.password AES (key managed by application)
Remote DBMS SSL files object storage (MinIO/S3) Server-side encryption
License signature license.signature HMAC; verified, not returned

7 Public RMD API (rmd-api/v1)

Base path: /rmd-api/v1. Authentication: X-Api-Key. All endpoints are read-only and controlled by company license (section 5).

7.1 List Projects

GET /projects

Query parameters: page, pageSize, format.

Response (200 OK):

{ "projects": [ { "id": 12, "name": "Sales Analytics", "description": "Production sales warehouse" } ], "pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 } }

7.2 List Versions

GET /projects/{id}/versions

Query parameters: page, pageSize, format.

{ "versions": [ { "id": 33, "name": "Q4 2025", "is_global": true } ], "pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 } }

is_global indicates a published project version.

7.3 Get Measures

GET /projects/{id}/versions/{versionId}/measures

Query parameters: 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 } }

The structure of returned keys matches the column layout of RMD (metricsIdToKeyMap in the service). Select fields are returned as localized labels; formula fields contain references replaced from numeric IDs with readable names; select_db fields (source) are structured objects with allowed connection name, database, schema, table, and column.

When requesting format=xlsx, the response body has the form { "downloadUrl": "" }.

7.4 Get Dimensions

GET /projects/{id}/versions/{versionId}/dimensions

Returns dimension rows in the same envelope, with dimension column layout (measurementIdToKeyMap). Query parameters: 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 Facts

GET /projects/{id}/versions/{versionId}/facts

Returns fact rows (factsIdToKeyMap). Query parameters: 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 Complete RMD

GET /projects/{id}/versions/{versionId}/rmd

Returns measures, dimensions, and facts in one payload, paginating each section independently.

{ "measures": [ /* see 7.3 */ ], "dimensions":[ /* see 7.4 */ ], "facts": [ /* see 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 } } }

With format=xlsx, measures and dimensions are combined into one workbook with a type column distinguishing rows.

8 Public DF API (df-api/v1)

Base path: /df-api/v1. Authentication: X-Api-Key. All endpoints are read-only.

The DF API is grouped into six logical areas: data marts, connections, dimension groups, fact tables, relationships, and consolidated RMD export. Data mart and connection endpoints return only JSON; dimension group, fact table, relationship, and consolidated export endpoints also support ?format=xlsx (see section 3.5).

8.1 List Data Marts

GET /projects/{project_id}/versions/{version_id}/data-marts

Query parameters:

Parameter Type Description
page, pageSize, language See section 3
type string Filter by data mart type: with_grouping, without_grouping, with_grouping_and_pivoting
merge_type string Filter by merge type: union, join
search string Case-insensitive substring search in name and description

Response (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 Data Mart Details

GET /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}

Returns the complete data mart configuration.

{ "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 } }

For transposed data marts (type = "with_grouping_and_pivoting"), the response additionally contains selected_measure_attributes, where the implicit transposition key Measure name is always added first:

"selected_measure_attributes": [ { "attribute_id": "3", "attribute_name": "Measure name" }, { "attribute_id": "27", "attribute_name": "Measure description" } ]

Field semantics:

Field Notes
merge_type null for data marts that do not merge multiple fact tables
instance_id Unique measure instance identifier
aggregation_configuration.type default, none, custom, or global
aggregation_configuration.group_by_fields Identifiers of column-value grouping fields of the measure
include_in_result false — element used only for filtering, not included in projection
filter_condition Filter expression with column-value references replaced by names; null if no filter
source_dimension_group_id / source_dimension_group_name Resolved from data model even if dimension group is not explicitly specified in mart attributes

8.3 Get Physical View Metadata

GET /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}/view

Returns metadata of the physical object materialized by DataForge for the data mart. Does not connect to the target DBMS.

When physical view exists:

{ "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" } }

When absent:

{ "exists": false, "type": null, "database": null, "schema": null, "name": null, "created_at": null, "connection": null }

type takes one of: regular_view, materialized_view, table.

8.4 Generate SQL Script

POST /projects/{project_id}/versions/{version_id}/data-marts/{data_mart_id}/generate-sql

Generates a SQL SELECT statement. SQL is only constructed and returned — it is not executed.

Optional request body:

{ "limit": 100, "offset": 0 }

Parameter Type Description
limit integer (>0) Adds row limit in dialect syntax (LIMIT n OFFSET m for PostgreSQL/ClickHouse, OFFSET m ROWS FETCH NEXT n ROWS ONLY for MS SQL). offset without limit is ignored
offset integer (≥0) Row offset; meaningful together with limit

For MS SQL, if ORDER BY is absent in the SQL, the service adds ORDER BY (SELECT NULL) for syntactic validity of OFFSET … FETCH NEXT.

Response (200 OK):

{ "sql_script": "SELECT ...\nFROM ...\nGROUP BY ...\nLIMIT 100 OFFSET 0", "target_db_type": "postgres", "validation_errors": [] }

If SQL generation fails (corrupted formulas or missing bindings), the response remains HTTP 200, and the failure is described in validation_errors:

{ "sql_script": "", "target_db_type": null, "validation_errors": [ { "code": "SQL_GENERATION_FAILED", "message": "Measure 'Revenue' references an unresolved field" } ] }

This form allows the client to distinguish between "data mart cannot currently generate SQL" and "data mart does not exist" (which returns 404).

8.5 List Connections

GET /projects/{project_id}/versions/{version_id}/connections

Query parameters:

Parameter Type Description
page, pageSize, language See section 3
db_type string Filter by DBMS type: postgresql, clickhouse, sqlserver
status string Filter by status: active, inactive, never_verified, failed

Response (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 } }

Notes:

  • schema is returned only for PostgreSQL. For MS SQL Server, the schema is included in the table name elsewhere in the system; for ClickHouse, the database name is used instead of a schema.
  • status is derived from internal state. never_verified takes precedence — without a successful update marker, the system cannot assert state. failed corresponds to internal UPDATE_ERROR. inactive corresponds to DISABLED. Otherwise, status is active.

8.6 Get Connection Details

GET /projects/{project_id}/versions/{version_id}/connections/{connection_id}

Query parameters:

Parameter Type Default Description
language string en Localization
include_db_schema boolean false When true, lightweight db_tables array is replaced with full db_schema object

Response (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" } ] }

Response (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 Connection Schema

GET /projects/{project_id}/versions/{version_id}/connections/{connection_id}/schema

Returns only the cached schema (tables and columns) of the connection.

{ "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" } ] } ] } }

The returned schema is a cached snapshot taken when the connection was configured or manually updated. It does not reflect real-time changes in the target DBMS. The last_updated_at field records when the snapshot was taken.

8.8 List Dimension Groups

GET /projects/{project_id}/versions/{version_id}/dimension-groups

Returns dimension groups (reference data) of the current project version with their primary key, dimension composition, and identifiers of fact tables referencing the group.

Query parameters: page, pageSize, language, format.

Response (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 } }

Field Notes
primary_key null if the group does not have a resolved physical key. db and schema may be null for source types that don't have them (e.g., ClickHouse)
dimensions Group members, ordered by hierarchy level. data_type is a localized display type; physical_column is the column name in the source table
related_fact_tables String IDs of fact tables referencing the group; full configuration available via 8.10 / 8.11

With format=xlsx, the response is { "downloadUrl": "" }, the workbook contains one sheet Dimension Groups with list rows.

8.9 Get Dimension Group Details

GET /projects/{project_id}/versions/{version_id}/dimension-groups/{dimension_group_id}

Returns the complete configuration of a single dimension group, including related fact tables with foreign key columns.

Query parameters: language, format.

Response (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" }

If the group does not exist in the specified version, the API returns 404 Not Found with code DF_API.DIMENSION_GROUP_NOT_FOUND.

8.10 List Fact Tables

GET /projects/{project_id}/versions/{version_id}/fact-tables

Returns fact tables of the current project version with item counts.

Query parameters: page, pageSize, language, format.

Response (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 counts only dimensions added directly to the fact table (i.e., not belonging to any dimension group). Dimensions inherited through groups are available via related_dimension_groups_count and via the details endpoint.

With format=xlsx, the workbook contains one sheet Fact Tables.

8.11 Get Fact Table Details

GET /projects/{project_id}/versions/{version_id}/fact-tables/{fact_table_id}

Returns the complete fact table configuration — its measures, dimensions, facts, dimension groups (with primary and foreign keys), and verification filters.

Query parameters:

Parameter Type Default Description
language string en Localization
include_dependencies boolean false When true, each measure carries a recursive dependencies tree, resolving formula references to target measures
format string json json or xlsx

Response (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 } ] }

Field Notes
measure_type base (direct source) or calculated (derived by formula)
fact_type primary, derived, or constant
is_from_dimension_group true — dimension inherited from a linked group; false — set directly on the fact table
dependencies Present and populated only when include_dependencies=true. Node contains id, name, type, formula, and recursive dependencies array
verification_filters[].is_valid false if filter expression does not resolve (missing reference, syntax error). invalid_reason carries a localized explanation

With format=xlsx, the workbook contains five sheets: Measures, Dimensions, Facts, Dimension Groups, Verification Filters.

If the fact table does not exist in the specified version, the API returns 404 Not Found with code DF_API.FACT_TABLE_NOT_FOUND.

8.12 List Relationships

GET /projects/{project_id}/versions/{version_id}/relationships

Returns foreign key relationships between fact tables and dimension groups in the current project version.

Query parameters:

Parameter Type Description
page, pageSize, language See section 3
fact_table_id integer Filter: relationships with the specified fact table as source
dimension_group_id integer Filter: relationships with the specified dimension group as target
format string json or xlsx

Response (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 describes the multiplicity from source (fact table) to target (dimension group). foreign_key and primary_key may be null if the corresponding mapping is incomplete; in this case the relationship is visible but not usable for constructing SQL joins.

8.13 Get Relationship Details

GET /projects/{project_id}/versions/{version_id}/relationships/{relationship_id}

Returns a single relationship with descriptive fields for both sides.

Query parameters: language, format.

Response (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" }

If the relationship does not exist in the specified version, the API returns 404 Not Found with code DF_API.RELATIONSHIP_NOT_FOUND.

8.14 Consolidated RMD Export

GET /projects/{project_id}/versions/{version_id}/rmd

Returns a complete snapshot of the project version's metadata — RMD content (measures, dimensions, facts) and data model (dimension groups, fact tables, relationships) — in one payload plus an exported_at timestamp. Intended for external catalogs and full state synchronization.

Query parameters: language, format.

Response (200 OK):

{ "project": { "id": "12", "name": "Sales Analytics", "description": "Production sales warehouse" }, "version": { "id": "33", "name": "Q4 2025", "is_global": true }, "measures": [ /* same row format as in 7.3 */ ], "dimensions": [ /* same row format as in 7.4 */ ], "facts": [ /* same row format as in 7.5 */ ], "dimension_groups": [ /* same row format as in 8.8 */ ], "fact_tables": [ /* same row format as in 8.10 */ ], "relationships": [ /* same row format as in 8.12 */ ], "exported_at": "2026-05-05T08:30:00Z" }

With format=xlsx, the workbook contains six sheets — Measures, Dimensions, Facts, Dimension Groups, Fact Tables, Relationships — and the response body has the form { "downloadUrl": "" }.

This endpoint is the public counterpart of the legacy rmd-api/v1 /rmd (7.6), extended with data model entities. Either endpoint can be used for RMD content; the data model is only exposed by the DF API endpoint.

9 Audit Logging

Authentication events in the public API are recorded with type, action, actor, source IP, target, and a free-form what field:

Event Type Action Trigger
Successful API authentication API_ACCESS API_ACCESS_SUCCESSFUL Public API request passes all ApiKeyGuard checks
Unsuccessful API authentication API_ACCESS API_ACCESS_FAILED Account locked, IP blocked, or license invalid

Audit log fields:

Field Value
type Event category (API_ACCESS)
action Specific action within category
who Actor email (API key owner)
from_where Client IP (respects X-Forwarded-For)
where Company name
what Endpoint group (RmdApi v1 / DfApi v1)
before, after Diff for change events (not used in read-only API)

The log retention period is set by company settings; records are available on the Audit Log screen to users with Company Admin or Super Admin role. Internal API audit events (UI login, license management, etc.) are described in chapter 8.

10 Error Code Reference

The table summarizes public API error codes.

Code HTTP Surface Description
API_KEY.KEY_MISSING 401 both X-Api-Key header not provided
API_KEY.INVALID_KEY 401 both API key does not match any stored key
API_KEY.INVALID_ENCRYPTED_API_KEY 400 both Encrypted key payload could not be decoded
API_KEY.AUTH_FAILED 401 both General authentication failure
API_KEY.ACCOUNT_LOCKED 403 both User is not in ENABLED status
API_KEY.IP_BLOCKED 403 both Client IP in blacklist or outside whitelist
API.NOT_AVAILABLE_WITHOUT_VALID_LICENSE 403 both Company does not have an active license
PROJECT.NOT_FOUND 404 rmd-api Project does not exist or is unavailable
VERSION.NOT_FOUND 404 rmd-api Version does not exist or is unavailable
DF_API.INVALID_PARAMETER 400 df-api Path or query parameter failed validation
DF_API.PAGE_SIZE_EXCEEDED 400 df-api pageSize exceeds 100
DF_API.INVALID_TYPE 400 df-api Invalid type filter value for data marts
DF_API.INVALID_MERGE_TYPE 400 df-api Invalid merge_type filter value for data marts
DF_API.INVALID_DB_TYPE 400 df-api Invalid db_type filter value for connections
DF_API.INVALID_STATUS 400 df-api Invalid status filter value for connections
DF_API.INVALID_FORMAT 400 df-api format differs from json and xlsx
DF_API.PROJECT_NOT_FOUND 404 df-api Project does not exist or is unavailable
DF_API.VERSION_NOT_FOUND 404 df-api Version does not exist or is unavailable
DF_API.DATA_MART_NOT_FOUND 404 df-api Data mart does not exist in the specified version
DF_API.CONNECTION_NOT_FOUND 404 df-api Connection does not exist in the specified version
DF_API.DIMENSION_GROUP_NOT_FOUND 404 df-api Dimension group does not exist in the specified version
DF_API.FACT_TABLE_NOT_FOUND 404 df-api Fact table does not exist in the specified version
DF_API.RELATIONSHIP_NOT_FOUND 404 df-api Relationship does not exist in the specified version
SQL_GENERATION_FAILED 200 (in validation_errors) df-api Data mart cannot currently generate SQL
RATE_LIMIT_EXCEEDED 429 both Request quota per key exceeded (planned)
INTERNAL_ERROR 500 both Unexpected server error