Public API v2
Base path: /df-api/v2. Auth: X-Api-Key. Endpoints are gated by company license (section 5 of the API page). v2 is a full read/write CRUD API: alongside the read endpoints it exposes create/update/delete endpoints over projects, versions, RMD content (measures, dimensions, facts), dimension groups, fact tables (including membership and per-fact-table verification filters), version-scoped verification filters, and relationships. Data marts and connections are available in v2 read-only (sections 5–6); in addition v2 manages version export/import (section 4 of the Public API v2 write operations page) and Git connections (section 5 of the Public API v2 write operations page).
Compared with v1, v2 uses a unified pagination format (totalPages in every list instead of the totalPages/total_pages mix), an extended error format (section 4), idempotent writes (section 1 of the Public API v2 write operations page), and SQL generation for measures (section 3); throttling is the same across all groups (section 2.4 of the API page). Read endpoints are described in section 2, write endpoints in section 2 of the Public API v2 write operations page, and project access management in section 9.9.7.
Throughout this section {v} abbreviates /df-api/v2/projects/{project_id}/versions/{version_id}; project_id and version_id are required integer path parameters and are not repeated in the parameter tables. Every v2 error is delivered in the envelope of section 4; the common errors of section 4 of the API page apply to every endpoint.
1 Differences from v1
| Aspect | v1 | v2 |
|---|---|---|
| Measures, dimensions, facts paths | {v}/measures, {v}/dimensions, {v}/facts |
the same |
| Pagination key | totalPages on RMD/projects/versions, total_pages on the other lists |
totalPages everywhere |
Entity id (measures/dimensions/facts) |
absent | present (first field) |
| Throttling | 100 requests / 60 s per key | the same |
| SQL for measures | no | ?include_sql=true |
| Measure data type (fact table detail) | data_type |
display_data_type |
| Dimension data type (fact table detail) | data_type |
display_data_type + dimension_type + formula |
| Fact data type (fact table detail) | data_type |
field absent |
| Dimension data type inside a dimension group | data_type |
display_data_type; the v2 group details additionally return the dimensions array (with description), which the v1 details lack |
created_at / updated_at of dimension groups, relationships and fact table details |
present (null for groups and relationships) |
absent |
relationship_type |
localized label (Many-to-one) |
slug many_to_one |
Data mart description when unset |
key absent | null |
pageSize above 100 |
400 for data marts, connections, dimension groups, fact tables, relationships; uncapped for projects, versions and RMD content |
400 for data marts, connections, Git connections; silently clamped to 100 for the other lists |
| Data marts and connections | read-only | read-only (ported to v2) |
Data mart physical_view |
exists, type, database, schema, name, created_at |
+ status, is_stale, last_refresh_at (section 5.2) |
| Version export/import, Git connections | none | sections 4–5 of the Public API v2 write operations page |
format for data marts and connections |
json | xlsx |
json only |
generate-sql status |
201 | 200 |
Source objects (connected_source, primary_key, foreign_key) |
db, schema, table, column |
+ connection (section 3.6 of the API page) |
Dialect rules for the source object schema (section 3.6 of the API page) |
RMD endpoints only | every endpoint |
| Error shape | 4 fields | + code and details (section 4) |
Idempotency (Idempotency-Key) |
none | on write POSTs; generate-sql, export/file, import/validate, import/preview and the Git connection test opted out |
| Partial success of a bulk operation | — | 207 Multi-Status (section 1 of the Public API v2 write operations page) |
2 Read endpoints
Read (GET) routes for v2. The data mart and connection reads are described with their groups in sections 5 and 9.9.9.
| Method | Path | Section |
|---|---|---|
GET |
/projects |
2.1 |
GET |
/projects/{id}/versions |
2.2 |
GET |
{v}/measures |
2.3 |
GET |
{v}/dimensions |
2.4 |
GET |
{v}/facts |
2.5 |
GET |
{v}/rmd |
2.6 |
GET |
{v}/dimension-groups |
2.7 |
GET |
{v}/dimension-groups/{dimension_group_id} |
2.8 |
GET |
{v}/fact-tables |
2.9 |
GET |
{v}/fact-tables/{fact_table_id} |
2.10 |
GET |
{v}/relationships |
2.11 |
GET |
{v}/relationships/{relationship_id} |
2.12 |
GET |
{v}/data-marts, …/{data_mart_id}, …/{data_mart_id}/view; POST …/generate-sql |
5.1–5.4 |
GET |
{v}/connections, …/{connection_id}, …/{connection_id}/schema |
6.1–6.3 |
GET |
/projects/{projectId}/access |
3.1 of the Public API v2 write operations page |
GET |
/git-connections, …/{connection_id} |
5.1–5.3 of the Public API v2 write operations page |
The read payloads mirror their v1 counterparts (sections 1 of the Public API v1 page and 2.8–2.14 of the Public API v1 page), differing in the pagination convention of section 1 and in two additions.
The first: every measure, dimension, and fact object — both in the per-section endpoints ({v}/measures, {v}/dimensions, {v}/facts) and in the combined RMD export ({v}/rmd) — begins with an id field. This is the entity's stable identifier within the project version (the same value the write endpoints of section 2 of the Public API v2 write operations page accept as {id} in their paths and echo in their responses), letting a client feed a read result straight into a subsequent PUT / PATCH / DELETE or assignment call without an extra lookup. id is a string and is present regardless of ?include_sql or ?language; row_number remains present for display/ordering. The addition is backward-compatible — clients that ignore unknown fields are unaffected.
The second: every source object carries a connection field naming the connection it belongs to — see the shape and the dialect rules in section 9.3.6. This affects connected_source of dimensions and facts, primary_key of dimension groups, primary_key and foreign_key in fact-table details and relationships, and all of the above inside the combined export. connection is null when the connection has since been deleted — in that case db is null as well.
Common to every list endpoint below: page (default 1) and pageSize (default 20, silently clamped to 100); format=xlsx answers { "downloadUrl": "<signed URL valid for 15 minutes>" }; an invalid format answers 400 DF_API.INVALID_FORMAT.
2.1 List projects
Request
GET /df-api/v2/projects
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
format |
query | json | xlsx |
no | Default json; xlsx produces a one-sheet workbook Projects |
language |
query | ru | en |
no | Accepted for contract parity; no field is localized |
Details
Only projects the key owner can access are returned (section 2.2 of the API page); inaccessible projects are excluded from the page and from total.
Response
200 OK
{
"projects": [
{ "id": 12, "name": "Sales Analytics", "description": "Production sales warehouse" }
],
"pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
projects[].id |
integer | Project identifier |
projects[].name |
string | Project name |
projects[].description |
string | null | Project description |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.2 List versions
Request
GET /df-api/v2/projects/{id}/versions
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | yes | Project identifier |
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
format |
query | json | xlsx |
no | Default json; xlsx produces a one-sheet workbook Versions |
language |
query | ru | en |
no | Accepted for contract parity; no field is localized |
Response
200 OK
{
"versions": [ { "id": 33, "name": "Q4 2025", "is_global": true } ],
"pagination": { "total": 1, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
versions[].id |
integer | Version identifier |
versions[].name |
string | Version name |
versions[].is_global |
boolean | true for the project's published (global) version |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.3 Get measures
Request
GET {v}/measures
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
language |
query | ru | en |
no | Language of the select-type labels |
format |
query | json | xlsx |
no | Default json |
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
include_sql |
query | boolean | no | true or 1: augment every measure with a sql_code object (section 3) |
Details
Every row starts with id — the stable identifier of the measure within the version — and carries all columns of the measures table of the RMD, empty ones as null. Select-type fields are localized labels; formula references are rendered as [Name].
Response
200 OK
{
"measures": [
{
"id": "1000",
"row_number": 1,
"group": "Revenue",
"block": "Sales",
"measure_name": "Total revenue",
"measure_description": "Gross revenue across all channels",
"original_source_type": "Database",
"original_source": "ERP",
"original_object": "sales.amount",
"display_data_type": "Number",
"measure_type": "Base",
"restrictions": null,
"formula": null,
"report_for_verification": null,
"comment": null,
"status": "Active",
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null,
"variation": null
}
],
"pagination": { "total": 42, "page": 1, "pageSize": 20, "totalPages": 3 }
}
| Field | Type | Description |
|---|---|---|
measures[].id |
string | Stable identifier of the measure within the version; the {measure_id} of the write endpoints |
measures[].sql_code |
object | Present only with include_sql=true and only when SQL could be generated (section 3) |
measures[].row_number |
integer | Position of the row in the RMD table |
measures[].group, block |
string | null | Grouping columns of the RMD |
measures[].measure_name |
string | Measure name |
measures[].measure_description |
string | null | Description |
measures[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
measures[].display_data_type |
string | null | Localized data type label (Number, Text, Date, …) |
measures[].measure_type |
string | Localized label: Base or Calculated |
measures[].restrictions |
string | null | Restrictions column |
measures[].formula |
string | null | Formula of a calculated measure with references rendered as [Name] |
measures[].report_for_verification, comment, responsible_for_data, variation |
string | null | Free-text columns |
measures[].status, relevance, required, visibility |
string | null | Localized select labels |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.4 Get dimensions
Request
GET {v}/dimensions
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
language |
query | ru | en |
no | Language of the select-type labels |
format |
query | json | xlsx |
no | Default json |
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
Details
Every row starts with id — the stable identifier of the dimension within the version — and carries all columns of the dimensions table of the RMD, empty ones as null; select-type fields are localized labels. source_data_type is resolved from connected_source through the cached schema of the referenced connection (null when it cannot be resolved). connected_source carries connection (section 3.6 of the API page): { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_customer", "column": "customer_name" }. The dialect rules apply: schema is "" for MS SQL Server and ClickHouse.
Response
200 OK
{
"dimensions": [
{
"id": "2000",
"row_number": 1,
"group": "Customer",
"block": "Profile",
"dimension_name": "Customer name",
"dimension_description": "Full customer name",
"original_source_type": null,
"original_source": null,
"original_object": null,
"dimension_group": "Customers",
"display_data_type": "Text",
"source_data_type": "VARCHAR(255)",
"dimension_type": "Primary",
"formula": null,
"connected_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_customer", "column": "customer_name" },
"comment": null,
"value_options": null,
"status": "Active",
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null
}
],
"pagination": { "total": 18, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
dimensions[].id |
string | Stable identifier; the {dimension_id} of the write endpoints |
dimensions[].row_number |
integer | Position of the row in the RMD table |
dimensions[].group, block |
string | null | Grouping columns of the RMD |
dimensions[].dimension_name, dimension_description |
string / string | null | Name and description |
dimensions[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
dimensions[].dimension_group |
string | null | Name of the dimension group the dimension belongs to |
dimensions[].display_data_type |
string | null | Localized data type label |
dimensions[].source_data_type |
string | null | Physical column type resolved from the connection's cached schema |
dimensions[].dimension_type |
string | Localized label: Primary or Derived |
dimensions[].formula |
string | null | Formula of a derived dimension |
dimensions[].connected_source |
object | null | Source object { db, schema, table, column } (section 3.6 of the API page) |
dimensions[].connected_source.connection |
string | null | Connection name; null (together with db) when the connection has been deleted |
dimensions[].comment, responsible_for_data |
string | null | Free-text columns |
dimensions[].value_options |
string | null | Allowed values column |
dimensions[].status, relevance, required, visibility |
string | null | Localized select labels |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.5 Get facts
Request
GET {v}/facts
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
language |
query | ru | en |
no | Language of the select-type labels |
format |
query | json | xlsx |
no | Default json |
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
Details
Every row starts with id — the stable identifier of the fact within the version — and carries all columns of the facts table of the RMD, empty ones as null; select-type fields are localized labels. connected_source carries connection (section 3.6 of the API page), and source_data_type is resolved from it through the cached schema of the referenced connection (null when it cannot be resolved).
Response
200 OK
{
"facts": [
{
"id": "3000",
"row_number": 1,
"group": "Sales",
"block": "Orders",
"fact_name": "Order line",
"fact_description": "An individual line item on a sales order",
"original_source_type": null,
"original_source": null,
"original_object": null,
"source_data_type": "DECIMAL(18,2)",
"fact_type": "Primary",
"formula": null,
"connected_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_order_line", "column": "amount" },
"report_for_verification": null,
"comment": null,
"status": null,
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null
}
],
"pagination": { "total": 6, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
facts[].id |
string | Stable identifier; the {fact_id} of the write endpoints |
facts[].row_number |
integer | Position of the row in the RMD table |
facts[].group, block |
string | null | Grouping columns of the RMD |
facts[].fact_name, fact_description |
string / string | null | Name and description |
facts[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
facts[].source_data_type |
string | null | Physical column type resolved from the connection's cached schema |
facts[].fact_type |
string | Localized label: Primary or Derived |
facts[].formula |
string | null | Formula of a derived fact |
facts[].connected_source |
object | null | Source object { db, schema, table, column } (section 3.6 of the API page) |
facts[].connected_source.connection |
string | null | Connection name; null (together with db) when the connection has been deleted |
facts[].report_for_verification, comment, responsible_for_data |
string | null | Free-text columns |
facts[].status, relevance, required, visibility |
string | null | Localized select labels |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.6 Combined RMD export
Request
GET {v}/rmd
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
language |
query | ru | en |
no | Language of the select-type labels |
format |
query | json | xlsx |
no | Default json; xlsx produces six sheets: Measures, Dimensions, Facts, Dimension Groups, Fact Tables, Relationships |
include_sql |
query | boolean | no | Augment every measure with sql_code (section 3) |
Details
The combined export: project, version, all RMD rows and the data model in one non-paginated payload. Every RMD row starts with id; every source object carries connection; dimension_groups[], fact_tables[] and relationships[] carry the same objects as the corresponding v2 list endpoints (with display_data_type, slug relationship_type, no created_at).
Response
200 OK
{
"project": { "id": "12", "name": "Sales Analytics", "description": "Production sales warehouse" },
"version": { "id": "33", "name": "Q4 2025", "is_global": true },
"measures": [
{
"id": "1000",
"row_number": 1,
"group": "Revenue",
"block": "Sales",
"measure_name": "Total revenue",
"measure_description": "Gross revenue across all channels",
"original_source_type": "Database",
"original_source": "ERP",
"original_object": "sales.amount",
"display_data_type": "Number",
"measure_type": "Base",
"restrictions": null,
"formula": null,
"report_for_verification": null,
"comment": null,
"status": "Active",
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null,
"variation": null
}
],
"dimensions": [
{
"id": "2000",
"row_number": 1,
"group": "Customer",
"block": "Profile",
"dimension_name": "Customer name",
"dimension_description": "Full customer name",
"original_source_type": null,
"original_source": null,
"original_object": null,
"dimension_group": "Customers",
"display_data_type": "Text",
"source_data_type": "VARCHAR(255)",
"dimension_type": "Primary",
"formula": null,
"connected_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_customer", "column": "customer_name" },
"comment": null,
"value_options": null,
"status": "Active",
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null
}
],
"facts": [
{
"id": "3000",
"row_number": 1,
"group": "Sales",
"block": "Orders",
"fact_name": "Order line",
"fact_description": "An individual line item on a sales order",
"original_source_type": null,
"original_source": null,
"original_object": null,
"source_data_type": "DECIMAL(18,2)",
"fact_type": "Primary",
"formula": null,
"connected_source": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_order_line", "column": "amount" },
"report_for_verification": null,
"comment": null,
"status": null,
"relevance": null,
"required": null,
"visibility": null,
"responsible_for_data": null
}
],
"dimension_groups": [
{
"id": "9",
"name": "Geography",
"description": "Geographic regions and countries",
"primary_key": {
"connection": "Production PostgreSQL",
"db": "analytics_db",
"schema": "public",
"table": "dim_geography",
"column": "region_id"
},
"dimensions": [
{ "id": "722", "name": "Region", "level": 1, "display_data_type": "Text", "physical_column": "region_name" },
{ "id": "723", "name": "Country", "level": 2, "display_data_type": "Text", "physical_column": "country_code" }
],
"related_fact_tables": ["11", "14"]
}
],
"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
}
],
"relationships": [
{
"id": "1101",
"source_fact_table": { "id": "11", "name": "fact_sales" },
"target_dimension_group": { "id": "9", "name": "Geography" },
"foreign_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" },
"primary_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" },
"relationship_type": "many_to_one"
}
],
"exported_at": "2026-05-05T08:30:00Z"
}
| Field | Type | Description |
|---|---|---|
project, version |
object | Identity of the exported version (id as strings; version.name may be null) |
measures[].id |
string | Stable identifier of the measure within the version; the {measure_id} of the write endpoints |
measures[].sql_code |
object | Present only with include_sql=true and only when SQL could be generated (section 3) |
measures[].row_number |
integer | Position of the row in the RMD table |
measures[].group, block |
string | null | Grouping columns of the RMD |
measures[].measure_name |
string | Measure name |
measures[].measure_description |
string | null | Description |
measures[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
measures[].display_data_type |
string | null | Localized data type label (Number, Text, Date, …) |
measures[].measure_type |
string | Localized label: Base or Calculated |
measures[].restrictions |
string | null | Restrictions column |
measures[].formula |
string | null | Formula of a calculated measure with references rendered as [Name] |
measures[].report_for_verification, comment, responsible_for_data, variation |
string | null | Free-text columns |
measures[].status, relevance, required, visibility |
string | null | Localized select labels |
dimensions[].id |
string | Stable identifier; the {dimension_id} of the write endpoints |
dimensions[].row_number |
integer | Position of the row in the RMD table |
dimensions[].group, block |
string | null | Grouping columns of the RMD |
dimensions[].dimension_name, dimension_description |
string / string | null | Name and description |
dimensions[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
dimensions[].dimension_group |
string | null | Name of the dimension group the dimension belongs to |
dimensions[].display_data_type |
string | null | Localized data type label |
dimensions[].source_data_type |
string | null | Physical column type resolved from the connection's cached schema |
dimensions[].dimension_type |
string | Localized label: Primary or Derived |
dimensions[].formula |
string | null | Formula of a derived dimension |
dimensions[].connected_source |
object | null | Source object { db, schema, table, column } (section 3.6 of the API page) |
dimensions[].connected_source.connection |
string | null | Connection name; null (together with db) when the connection has been deleted |
dimensions[].comment, responsible_for_data |
string | null | Free-text columns |
dimensions[].value_options |
string | null | Allowed values column |
dimensions[].status, relevance, required, visibility |
string | null | Localized select labels |
facts[].id |
string | Stable identifier; the {fact_id} of the write endpoints |
facts[].row_number |
integer | Position of the row in the RMD table |
facts[].group, block |
string | null | Grouping columns of the RMD |
facts[].fact_name, fact_description |
string / string | null | Name and description |
facts[].original_source_type, original_source, original_object |
string | null | Provenance columns; free text |
facts[].source_data_type |
string | null | Physical column type resolved from the connection's cached schema |
facts[].fact_type |
string | Localized label: Primary or Derived |
facts[].formula |
string | null | Formula of a derived fact |
facts[].connected_source |
object | null | Source object { db, schema, table, column } (section 3.6 of the API page) |
facts[].connected_source.connection |
string | null | Connection name; null (together with db) when the connection has been deleted |
facts[].report_for_verification, comment, responsible_for_data |
string | null | Free-text columns |
facts[].status, relevance, required, visibility |
string | null | Localized select labels |
dimension_groups[].id, name, description |
string / string / string | null | Identity |
dimension_groups[].primary_key |
object | null | Source object with connection (section 3.6 of the API page); null when the group has no key yet |
dimension_groups[].dimensions[] |
array | id, name, level (hierarchy level), display_data_type (localized), physical_column |
dimension_groups[].related_fact_tables[] |
string | IDs of the fact tables the group is assigned to |
fact_tables[].id, name, description, owner, created_at |
— | Identity; owner is the creator's name |
fact_tables[].measures_count, dimensions_count, facts_count |
integer | Elements assigned directly to the fact table |
fact_tables[].verification_filters_count |
integer | Per-fact-table verification filters |
fact_tables[].related_dimension_groups_count |
integer | Dimension groups assigned to the fact table |
relationships[].relationship_type |
string | Slug many_to_one (the only cardinality the model supports) |
relationships[].foreign_key, primary_key |
object | null | Source objects with connection; null when the mapping is incomplete |
relationships[].id |
string | Relationship identifier |
relationships[].source_fact_table |
object | id, name of the fact table holding the foreign key |
relationships[].target_dimension_group |
object | id, name of the dimension group holding the primary key |
exported_at |
string | When the snapshot was taken, ISO 8601 |
Errors. Common errors only (section 4 of the API page).
2.7 List dimension groups
Request
GET {v}/dimension-groups
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
language |
query | ru | en |
no | Language of the display_data_type labels |
format |
query | json | xlsx |
no | Default json; xlsx produces a one-sheet workbook Dimension Groups |
Details
Returns the dimension groups (справочники) defined in the project version, with their primary key, member dimensions and the IDs of fact tables that reference them. primary_key carries connection, schema is "" for MS SQL Server and ClickHouse and db is null only when the connection has since been deleted; member dimensions expose display_data_type instead of data_type; there is no created_at.
Response
200 OK
{
"dimension_groups": [
{
"id": "9",
"name": "Geography",
"description": "Geographic regions and countries",
"primary_key": {
"connection": "Production PostgreSQL",
"db": "analytics_db",
"schema": "public",
"table": "dim_geography",
"column": "region_id"
},
"dimensions": [
{ "id": "722", "name": "Region", "level": 1, "display_data_type": "Text", "physical_column": "region_name" },
{ "id": "723", "name": "Country", "level": 2, "display_data_type": "Text", "physical_column": "country_code" }
],
"related_fact_tables": ["11", "14"]
}
],
"pagination": { "total": 12, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
dimension_groups[].id, name, description |
string / string / string | null | Identity |
dimension_groups[].primary_key |
object | null | Source object with connection (section 3.6 of the API page); null when the group has no key yet |
dimension_groups[].dimensions[] |
array | id, name, level (hierarchy level), display_data_type (localized), physical_column |
dimension_groups[].related_fact_tables[] |
string | IDs of the fact tables the group is assigned to |
pagination |
object | total, page, pageSize, totalPages |
Errors. Common errors only (section 4 of the API page).
2.8 Get dimension group details
Request
GET {v}/dimension-groups/{dimension_group_id}
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
dimension_group_id |
path | integer | yes | Dimension group identifier |
language |
query | ru | en |
no | Localization |
format |
query | json | xlsx |
no | Default json |
Details
Returns the full configuration of a single dimension group: the dimensions array (each member with description) alongside the related fact tables with their foreign key columns. There is no created_at / updated_at.
Response
200 OK
{
"id": "9",
"name": "Geography",
"description": "Geographic regions and countries",
"primary_key": {
"connection": "Production PostgreSQL",
"db": "analytics_db",
"schema": "public",
"table": "dim_geography",
"column": "region_id"
},
"dimensions": [
{ "id": "722", "name": "Region", "description": "Sales region", "level": 1, "display_data_type": "Text", "physical_column": "region_name" }
],
"related_fact_tables": [
{ "fact_table_id": "11", "fact_table_name": "fact_sales", "foreign_key_column": "region_id" }
]
}
| Field | Type | Description |
|---|---|---|
dimensions[] |
array | Members with id, name, description, level, display_data_type, physical_column |
related_fact_tables[] |
array | fact_table_id, fact_table_name, foreign_key_column |
id, name, description |
string / string / string | null | Identity |
primary_key |
object | null | Source object with connection (section 3.6 of the API page); null when the group has no key yet |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.DIMENSION_GROUP_NOT_FOUND |
404 | The dimension group does not exist in the version |
| Common errors | — | Section 4 of the API page |
2.9 List fact tables
Request
GET {v}/fact-tables
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
language |
query | ru | en |
no | Accepted; the list has no localized field |
format |
query | json | xlsx |
no | Default json; xlsx produces a one-sheet workbook Fact Tables |
Details
dimensions_count counts only dimensions added directly to the fact table (not part of any dimension group). Dimensions inherited via dimension groups are accessible through related_dimension_groups_count and through the detail endpoint.
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, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
fact_tables[].id, name, description, owner, created_at |
— | Identity; owner is the creator's name |
fact_tables[].measures_count, dimensions_count, facts_count |
integer | Elements assigned directly to the fact table |
fact_tables[].verification_filters_count |
integer | Per-fact-table verification filters |
fact_tables[].related_dimension_groups_count |
integer | Dimension groups assigned to the fact table |
pagination |
object | page, pageSize, total, totalPages |
Errors. Common errors only (section 4 of the API page).
2.10 Get fact table details
Request
GET {v}/fact-tables/{fact_table_id}
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
fact_table_id |
path | integer | yes | Fact table identifier |
language |
query | ru | en |
no | Language of the display_data_type labels and invalid_reason |
include_dependencies |
query | boolean | no | Default false; true adds the recursive dependencies tree to every measure |
format |
query | json | xlsx |
no | Default json; xlsx produces five sheets: Measures, Dimensions, Facts, Dimension Groups, Verification Filters |
Details
Returns the full configuration of a fact table — its measures, dimensions, facts, dimension groups (with primary and foreign keys) and verification filters. measure_type and fact_type are raw slugs (base / calculated, primary / derived / constant); measures carry display_data_type (not data_type); dimensions carry display_data_type, dimension_type and formula; facts carry no data type field; dimension_groups[].primary_key / foreign_key carry connection; there is no updated_at.
Response
200 OK
{
"id": "11",
"name": "fact_sales",
"description": "Primary sales facts",
"owner": "Pavel Shalavin",
"created_at": "2026-04-15T08:30:00Z",
"measures": [
{ "id": "501", "name": "Total revenue", "description": "Gross revenue", "formula": "SUM([Amount])", "display_data_type": "Number", "measure_type": "base", "dependencies": null }
],
"dimensions": [
{ "id": "801", "name": "Sale date", "description": "Calendar date of the sale", "display_data_type": "Date", "dimension_type": "primary", "formula": null, "physical_column": "sale_date", "is_from_dimension_group": false, "dimension_group_id": null }
],
"facts": [
{ "id": "611", "name": "Order line", "description": null, "fact_type": "primary", "formula": null, "physical_column": "amount" }
],
"dimension_groups": [
{
"id": "9", "name": "Geography", "description": "Geographic regions and countries",
"primary_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" },
"foreign_key": { "connection": "Production PostgreSQL", "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 | Type | Description |
|---|---|---|
measures[].display_data_type |
string | null | Localized data type label |
measures[].measure_type |
string | null | base or calculated |
dimensions[].dimension_type |
string | null | primary or derived |
dimensions[].formula |
string | null | Formula of a derived dimension |
facts[].fact_type |
string | null | primary, derived, or constant |
measures[].dependencies |
array | null | Present and populated only when include_dependencies=true; each node has id, name, type, formula and a recursive dependencies array |
dimensions[].is_from_dimension_group |
boolean | true for dimensions inherited from a linked dimension group; false for dimensions defined directly on the fact table |
dimensions[].dimension_group_id |
string | null | The group an inherited dimension comes from |
…physical_column |
string | null | Column bound to the element in the source table |
dimension_groups[].primary_key, foreign_key |
object | null | Source objects of the join (section 3.6 of the API page) |
verification_filters[].is_valid |
boolean | false when the filter expression cannot be resolved (missing reference, syntax error); invalid_reason carries the localized explanation |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.FACT_TABLE_NOT_FOUND |
404 | The fact table does not exist in the version |
| Common errors | — | Section 4 of the API page |
2.11 List relationships
Request
GET {v}/relationships
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, clamped to 100 |
language |
query | ru | en |
no | Accepted; relationship_type is a slug in v2 and is not localized |
fact_table_id |
query | integer | no | Only relationships whose source is the given fact table |
dimension_group_id |
query | integer | no | Only relationships whose target is the given dimension group |
format |
query | json | xlsx |
no | Default json |
Response
200 OK
{
"relationships": [
{
"id": "1101",
"source_fact_table": { "id": "11", "name": "fact_sales" },
"target_dimension_group": { "id": "9", "name": "Geography" },
"foreign_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" },
"primary_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" },
"relationship_type": "many_to_one"
}
],
"pagination": { "total": 4, "page": 1, "pageSize": 20, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
relationships[].relationship_type |
string | Slug many_to_one (the only cardinality the model supports) |
relationships[].foreign_key, primary_key |
object | null | Source objects with connection; null when the mapping is incomplete |
relationships[].id |
string | Relationship identifier |
relationships[].source_fact_table |
object | id, name of the fact table holding the foreign key |
relationships[].target_dimension_group |
object | id, name of the dimension group holding the primary key |
pagination |
object | page, pageSize, total, totalPages |
Errors. Common errors only (section 4 of the API page).
2.12 Get relationship details
Request
GET {v}/relationships/{relationship_id}
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
relationship_id |
path | integer | yes | Relationship identifier |
language |
query | ru | en |
no | Accepted; nothing is localized |
format |
query | json | xlsx |
no | Default json |
Details
Returns a single relationship with descriptive fields on both sides; the target dimension group additionally carries its primary_key. keys carry connection, relationship_type is the slug many_to_one, no created_at / updated_at.
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": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" }
},
"foreign_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "fact_sales", "column": "region_id" },
"primary_key": { "connection": "Production PostgreSQL", "db": "analytics_db", "schema": "public", "table": "dim_geography", "column": "region_id" },
"relationship_type": "many_to_one"
}
| Field | Type | Description |
|---|---|---|
id |
string | Relationship identifier |
source_fact_table, target_dimension_group |
object | Both sides with description; the group side repeats its primary_key |
foreign_key, primary_key |
object | null | Source objects with connection; null when the mapping is incomplete |
relationship_type |
string | Slug many_to_one (the only cardinality the model supports) |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.RELATIONSHIP_NOT_FOUND |
404 | The relationship does not exist in the version |
| Common errors | — | Section 4 of the API page |
3 SQL generation for measures (include_sql)
The GET {v}/measures (2.3) and GET {v}/rmd (2.6) endpoints accept an optional ?include_sql=true parameter. When supplied, each item in the measures array is augmented with a sql_code field:
{
"id": "1000",
"row_number": 1,
"measure_name": "Total revenue",
"sql_code": {
"generated_at": "2026-05-29T08:00:00.000Z",
"sql_scripts": [
{
"fact_table_id": "11",
"fact_table_name": "fact_sales",
"sql": "SELECT SUM(amount) FROM fact_sales WHERE ..."
}
]
}
}
| Field | Type | Description |
|---|---|---|
sql_code.generated_at |
string | When the SQL was generated, ISO 8601 |
sql_code.sql_scripts[] |
array | One entry per fact table the measure is bound to; for a measure bound to no fact table a single entry with fact_table_id and fact_table_name equal to null |
sql_code.sql_scripts[].sql |
string | The generated SQL. It is generated but not executed |
If SQL cannot be generated for a measure, the sql_code field is omitted from that item; the request itself still succeeds.
4 Error format in v2
v2 extends the base envelope of section 4 of the API page with two machine-readable fields. The response is identical across every v2 endpoint:
{
"message": "Invalid merge_type filter value",
"originalMessage": "DF_API.INVALID_MERGE_TYPE",
"statusCode": 400,
"error": "BadRequestException",
"code": "invalid_merge_type",
"details": [{ "field": "merge_type", "code": "invalid_value" }]
}
| Field | Description |
|---|---|
code |
Machine-readable error code. It is the section 8 of the API page key without the DF_API. prefix, lowercased: DF_API.DATA_MART_NOT_FOUND → data_mart_not_found. One code always maps to exactly one HTTP status |
details |
Which fields caused the error. Empty array when the error is not tied to a field (e.g. invalid_api_key) |
details[].field |
Field or parameter name. For nested and bulk items it is a dotted path: measures.1.measure_type |
details[].code |
Per-field reason from a closed list: missing_field, unknown_field, invalid_value. For not-found errors it repeats the top-level code |
Client-relevant guarantees:
originalMessagealways carries aDF_API.*key. Keys raised by internal application services are mapped ontoDF_API.*before leaving the API — foreign namespaces never reach the public contract.- Keys outside the catalog degrade to a generic code derived from the HTTP status (any unknown 409, for instance, reads as
constraint_violation). - Unexpected internal failures answer
500/internal_errorwith no internal text. - The HTTP status comes from the code catalog, not from the class of the thrown exception.
5 Data marts in v2 (df-api/v2)
Four data mart endpoints, all read-only: like v1, v2 exposes no create, update, or delete operation for data marts, and POST generate-sql persists nothing and merely returns generated query text.
Properties shared by every endpoint of the group:
- authentication is the
X-Api-Keyheader (section 2.1 of the API page) and project access follows section 2.2 of the API page: a project the key owner cannot reach answers404, not403; - JSON output only.
formatmay be omitted or set tojson; any other value,xlsxincluded, is rejected asDF_API.INVALID_FORMAT; language(ru/en) affects descriptive labels only. Slug values (merge_type,db_type) are never translated, because they double as filter inputs;- the security constraints of section 6 of the API page apply in full.
5.1 List data marts
Request
GET {v}/data-marts
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, maximum 100 |
language |
query | ru | en |
no | Default en; affects type only |
format |
query | json |
no | Only json is accepted |
type |
query | string | no | Filter by mart type: with_grouping, without_grouping, with_grouping_and_pivoting |
merge_type |
query | string | no | Filter by merge type: union, join |
search |
query | string | no | Case-insensitive substring search across name and description |
Details
A page beyond the last one is not an error: data_marts comes back empty with total still correct.
Response
200 OK
{
"data_marts": [
{
"id": "9666",
"name": "DF Fashion retail sales only",
"description": "Sales mart without plans",
"owner": "Alyona Zubakova",
"created_at": "2026-05-21T13:23:10.621Z",
"type": "With grouping",
"merge_type": null,
"source_fact_table_count": 1,
"has_physical_view": false
}
],
"pagination": { "page": 1, "pageSize": 20, "total": 2, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
data_marts[].id |
string | Data mart identifier. A string, not a number |
data_marts[].name |
string | Display name |
data_marts[].description |
string | null | The key is always present; null when no description is set (an empty string in storage is returned as null too); v1 omits the key in that case |
data_marts[].owner |
string | Owner's name ("First Last"), or the e-mail when no name is set |
data_marts[].created_at |
string | Creation timestamp, ISO 8601 |
data_marts[].type |
string | Localized type label: "With grouping", "Without grouping", "With grouping and pivoting" |
data_marts[].merge_type |
string | null | Raw slug union / join, never localized. null for marts that do not combine multiple fact tables |
data_marts[].source_fact_table_count |
integer | Number of distinct fact tables used as sources |
data_marts[].has_physical_view |
boolean | Whether a physical view has been materialized for the mart |
pagination |
object | page, pageSize, total, totalPages |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.INVALID_TYPE |
400 | type is outside the allowed set |
DF_API.INVALID_MERGE_TYPE |
400 | merge_type is outside the allowed set |
DF_API.PAGE_SIZE_EXCEEDED |
400 | pageSize above 100 |
| Common errors | — | Section 4 of the API page |
5.2 Get data mart details
Request
GET {v}/data-marts/{data_mart_id}
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
data_mart_id |
path | integer | yes | Data mart identifier |
language |
query | ru | en |
no | Language of type and data_type labels |
format |
query | json |
no | Only json is accepted |
Details
The mart's full configuration: source fact tables, selected measures, facts and dimensions, and physical-view metadata. For pivoted marts (type corresponding to the slug with_grouping_and_pivoting) the response additionally carries selected_measure_attributes, placed before physical_view:
"selected_measure_attributes": [
{ "attribute_id": "18", "attribute_name": "Measure name" },
{ "attribute_id": "24", "attribute_name": "Measure description" }
]
Response
200 OK
{
"id": "9667",
"name": "DF Fashion retail sales and plans",
"description": "Combined mart with sales and plans",
"owner": "Alyona Zubakova",
"created_at": "2026-05-21T13:23:10.621Z",
"type": "With grouping",
"merge_type": "join",
"source_fact_tables": [
{ "id": "6784", "name": "Sales and refunds", "description": "Fact table with sales and refunds" }
],
"selected_measures": [
{
"instance_id": "155341",
"measure_id": "33540",
"measure_name": "Items Gross, count",
"description": "Count of distinct items sold",
"formula": "COUNTDISTINCT({[operation_id]=1} [Receipt position])",
"data_type": "Number",
"display_name": "Items Gross, count",
"aggregation_configuration": { "type": "default", "group_by_fields": [] },
"source_fact_table_id": "6784"
}
],
"selected_facts": [
{
"fact_id": "611",
"fact_name": "Order line",
"description": null,
"data_type": "Number",
"display_name": "Order line",
"include_in_result": true,
"filter_condition": null,
"source_fact_table_id": "6784"
}
],
"selected_dimensions": [
{
"dimension_id": "33929",
"dimension_name": "Year",
"description": "Year of the period within the filter range",
"data_type": "Number",
"display_name": "Year",
"include_in_result": true,
"filter_condition": "[Year] >= 2020",
"source_fact_table_id": "6785",
"source_dimension_group_id": "1204",
"source_dimension_group_name": "Calendar"
}
],
"physical_view": {
"exists": false,
"type": null,
"database": null,
"schema": null,
"name": null,
"created_at": null,
"status": null,
"is_stale": null,
"last_refresh_at": null
}
}
| Field | Type | Description |
|---|---|---|
description (mart, source_fact_tables[], selected_measures[], selected_facts[], selected_dimensions[]) |
string | null | The key is always present; null when no description is set |
source_fact_tables[] |
array | Fact tables the mart draws from |
selected_measures[].instance_id |
string | Identifier of the measure instance inside the mart. The same measure may appear several times with different aggregation settings |
selected_measures[].measure_id |
string | Identifier of the measure itself in the RMD |
selected_measures[].formula |
string | Formula of a calculated measure; empty for base measures |
selected_measures[].aggregation_configuration.type |
string | default, none, custom, or global |
selected_measures[].aggregation_configuration.group_by_fields |
array | Identifiers of the fields the measure is grouped by |
…include_in_result |
boolean | false means the element is used for filtering only and is not projected into the result |
…filter_condition |
string | null | Filter expression with element references replaced by their names; null when there is no filter |
selected_dimensions[].source_dimension_group_id / source_dimension_group_name |
string | null | The dimension group resolved from the data model — even when it is not listed explicitly among the mart's attributes |
physical_view |
object | Metadata of the materialized object — exists, type (localized label of the object kind), database (raw slug), schema, name, created_at, status, is_stale, last_refresh_at — but without the connection field, which only the dedicated view endpoint returns |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.DATA_MART_NOT_FOUND |
404 | The mart does not exist in the given version. details addresses the data_mart_id field |
| Common errors | — | Section 4 of the API page |
5.3 Get physical view metadata
Request
GET {v}/data-marts/{data_mart_id}/view
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
data_mart_id |
path | integer | yes | Data mart identifier |
language |
query | ru | en |
no | Language of the type label |
format |
query | json |
no | Only json is accepted |
Details
Metadata of the object DataForge has materialized for the mart. No connection to the target database is made — only what the platform already knows is returned. When no view exists, exists is false and every other field is null:
{
"exists": false, "type": null, "database": null, "schema": null, "name": null, "created_at": null,
"status": null, "is_stale": null, "last_refresh_at": null, "connection": null
}
Response
200 OK
{
"exists": true,
"type": "Materialized view",
"database": "clickhouse",
"schema": "dataforge_test",
"name": "materialized_view_6839_datamart_DF Fashion retail sales only",
"created_at": "2026-03-17T12:44:45.000Z",
"status": "active",
"is_stale": false,
"last_refresh_at": "2026-08-30T03:00:12.000Z",
"connection": { "id": "3160", "name": "Click", "db_type": "clickhouse" }
}
| Field | Type | Description |
|---|---|---|
exists |
boolean | Whether the object is materialized. When false, every other field is null |
type |
string | null | Localized label for the object kind. Raw slugs: regular_view, materialized_view, table |
database |
string | null | Raw database-type slug: postgresql, clickhouse, sqlserver. Never localized |
schema |
string | null | Schema the object was created in. PostgreSQL only; null for MS SQL Server and ClickHouse, whose connection-level "schema" is its database |
name |
string | null | Object name in the database |
created_at |
string | null | When the object was created, ISO 8601 |
status |
string | null | Raw lifecycle status slug of the object: deploying, active, updating, deleting, error. Never localized |
is_stale |
boolean | null | true when the mart's configuration changed after deployment and the object has diverged from it (it needs to be recreated); false when the object matches the current configuration; null when there is no object |
last_refresh_at |
string | null | When the object's data was last refreshed successfully, ISO 8601; null if no refresh has run yet |
connection |
object | null | The connection the object lives in. null when there is no view or the connection could not be resolved. Carries no credentials — only id, name, and db_type |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.DATA_MART_NOT_FOUND |
404 | The mart does not exist in the given version. details addresses the data_mart_id field |
| Common errors | — | Section 4 of the API page |
5.4 Generate the SQL script
Request
POST {v}/data-marts/{data_mart_id}/generate-sql
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
data_mart_id |
path | integer | yes | Data mart identifier |
limit |
query | integer > 0 | no | Appends a row limit in the target dialect's syntax: LIMIT n OFFSET m for PostgreSQL and ClickHouse, OFFSET m ROWS FETCH NEXT n ROWS ONLY for MS SQL |
offset |
query | integer ≥ 0 | no | Row offset; effective only together with limit. Sent without limit, it is ignored |
language |
query | ru | en |
no | Language of validation_errors[].message |
POST {v}/data-marts/{data_mart_id}/generate-sql?limit=100&offset=0
Details
The request takes no body. Returns the mart's SQL query text. The query is not executed, no data is read from the target database, and nothing is persisted.
Unlike v1, which accepts
{ "limit": …, "offset": … }in the request body, v2 takes these values from the query string. A JSON body carrying a numericlimitis rejected by the edge web application firewall before it reaches the API.
The endpoint ignores Idempotency-Key — it creates nothing, so a repeated call always regenerates the SQL rather than replaying a cached response. It answers 200, not 201, and not a 4xx when generation fails: validation_errors is what distinguishes "the mart cannot currently produce SQL" from "the mart does not exist" (404). For MS SQL, when the query has no ORDER BY, ORDER BY (SELECT NULL) is appended — otherwise OFFSET … FETCH NEXT is not syntactically valid.
Response
200 OK on success:
{
"sql_script": "SELECT ... FROM ... GROUP BY ... LIMIT 100 OFFSET 0",
"target_db_type": "clickhouse",
"validation_errors": []
}
200 OK when SQL generation fails:
{
"sql_script": "",
"target_db_type": null,
"validation_errors": [
{ "code": "SQL_GENERATION_FAILED", "message": "Foreign key is not configured for dimension group \"Geography\"" }
]
}
| Field | Type | Description |
|---|---|---|
sql_script |
string | The generated SELECT. Empty string on failure |
target_db_type |
string | null | Target database type: postgres, clickhouse, sqlserver. null on failure |
validation_errors |
array | Empty array on success |
validation_errors[].code |
string | Always SQL_GENERATION_FAILED |
validation_errors[].message |
string | Human-readable cause, localized per language / Accept-Language. For unexpected internal failures a generic text is returned, with no internal detail |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.VALIDATION_FAILED |
400 | A query parameter failed schema validation: limit not a positive integer (0, -3, 2.7, abc), offset negative. details addresses the offending parameter |
DF_API.DATA_MART_NOT_FOUND |
404 | The mart does not exist in the given version |
| Common errors | — | Section 4 of the API page |
6 Connections in v2 (df-api/v2)
Three endpoints over connections to external databases. All are read-only — creating, updating, and deleting connections is not available through the public API in either v1 or v2; that happens only in the platform UI.
Two properties matter for integrations:
- credentials are never returned. Passwords, certificates, private keys, and connection strings are absent from every response (the full list is in section 6 of the API page). Only the network coordinates and the database user name needed for SQL building are exposed;
- the schema is a cached snapshot taken at connection setup or at the last refresh — not a live read of the database at request time. When the snapshot was taken is reported in
last_updated_at.
Connections whose database the public API does not expose (MySQL, for instance) are omitted entirely — they appear neither in the list nor in total, and addressing such a connection by id answers 404.
6.1 List connections
Request
GET {v}/connections
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page |
query | integer | no | Page number, 1-indexed. Default 1 |
pageSize |
query | integer | no | Items per page. Default 20, maximum 100 |
language |
query | ru | en |
no | Accepted for contract parity; it does not affect the response — every connection field is a raw slug |
format |
query | json |
no | Only json is accepted |
db_type |
query | string | no | Filter by database type: postgresql, clickhouse, sqlserver |
status |
query | string | no | Filter by status: active, inactive, never_verified, failed |
Details
Internal state to public status mapping:
| Internal state | Public status |
|---|---|
| No schema refresh has ever succeeded | never_verified |
UPDATE_ERROR |
failed |
DISABLED |
inactive |
| Anything else | active |
never_verified takes priority: without a successful refresh the platform cannot claim anything about the connection's state, so that status is returned regardless of the internal value.
Response
200 OK
{
"connections": [
{
"id": "3821",
"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-05-21T13:23:07.188Z"
},
{
"id": "3822",
"name": "Legacy SQL Server",
"db_type": "sqlserver",
"host": "sqlserver.legacy.company.com",
"port": 1433,
"database": "legacy_warehouse",
"schema": null,
"username": "etl_service",
"status": "failed",
"last_updated_at": "2026-05-14T10:00:00.000Z"
}
],
"pagination": { "page": 1, "pageSize": 20, "total": 2, "totalPages": 1 }
}
| Field | Type | Description |
|---|---|---|
connections[].id |
string | Connection identifier |
connections[].name |
string | Display name |
connections[].db_type |
string | Raw slug: postgresql, clickhouse, sqlserver. Never localized |
connections[].host |
string | Database server hostname or IP address |
connections[].port |
integer | Port |
connections[].database |
string | Database name |
connections[].schema |
string | null | Schema. Returned for PostgreSQL only; for MS SQL the schema is folded into the table name and for ClickHouse the database name plays that role — null in both cases |
connections[].username |
string | Database user name. Not a secret, and required for SQL building (section 6 of the API page) |
connections[].status |
string | active, inactive, never_verified, or failed |
connections[].last_updated_at |
string | null | When the schema was last refreshed successfully, ISO 8601 |
pagination |
object | page, pageSize, total, totalPages |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.INVALID_DB_TYPE |
400 | db_type is outside the allowed set |
DF_API.INVALID_STATUS |
400 | status is outside the allowed set |
DF_API.PAGE_SIZE_EXCEEDED |
400 | pageSize above 100 |
| Common errors | — | Section 4 of the API page |
6.2 Get connection details
Request
GET {v}/connections/{connection_id}
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
connection_id |
path | integer | yes | Connection identifier |
include_db_schema |
query | boolean | no | Default false. true or 1 replaces the lightweight db_tables array with the full db_schema object; anything else reads as false |
language |
query | ru | en |
no | Accepted for contract parity; it does not affect the response |
format |
query | json |
no | Only json is accepted |
Details
When the schema cache is empty or unreadable, an empty table list is returned rather than an error: db_tables: [] or db_schema.tables: [].
Response
200 OK without include_db_schema:
{
"id": "3821",
"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-05-21T13:23:07.188Z",
"db_tables": [{ "name": "fact_sales" }, { "name": "dim_customer" }]
}
200 OK with include_db_schema=true:
{
"id": "3821",
"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-05-21T13:23:07.188Z",
"db_schema": {
"connection": "Production PostgreSQL",
"tables": [
{
"table_name": "fact_sales",
"schema": "public",
"columns": [
{ "column_name": "sale_id", "data_type": "int" },
{ "column_name": "amount", "data_type": "decimal(18,2)" }
]
}
]
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Connection identifier |
name |
string | Display name |
db_type |
string | Raw slug: postgresql, clickhouse, sqlserver. Never localized |
host |
string | Database server hostname or IP address |
port |
integer | Port |
database |
string | Database name |
schema |
string | null | Schema. Returned for PostgreSQL only; for MS SQL the schema is folded into the table name and for ClickHouse the database name plays that role — null in both cases |
username |
string | Database user name. Not a secret, and required for SQL building (section 6 of the API page) |
status |
string | active, inactive, never_verified, or failed |
last_updated_at |
string | null | When the schema was last refreshed successfully, ISO 8601 |
db_tables |
array | Lightweight table list: names only. Present when include_db_schema is not set |
db_schema |
object | Full structure. Present instead of db_tables when include_db_schema=true |
db_schema.connection |
string | null | Name of the connection the schema belongs to; repeats the top-level name so the object stays self-identifying when handled apart from this envelope |
db_schema.tables[].table_name |
string | Table name |
db_schema.tables[].schema |
string | null | Table's schema. null for ClickHouse, which has no schemas — its cached snapshot carries the database name in that position, and it is deliberately not surfaced here |
db_schema.tables[].columns[].column_name |
string | Column name |
db_schema.tables[].columns[].data_type |
string | Column type as a string, exactly as read from the database |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.CONNECTION_NOT_FOUND |
404 | The connection does not exist in the given version, or its database is not exposed by the public API. details addresses the connection_id field |
| Common errors | — | Section 4 of the API page |
6.3 Get connection schema
Request
GET {v}/connections/{connection_id}/schema
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
connection_id |
path | integer | yes | Connection identifier |
format |
query | json |
no | Only json is accepted |
Details
Returns only the connection's identity and its cached schema, without the network coordinates. The schema is a cached snapshot taken at connection setup or at the last refresh, not a live read; last_updated_at reports when it was taken. Note the container key is schema, not db_schema as in the connection details endpoint; the contents are the same.
Response
200 OK
{
"id": "3821",
"name": "Production PostgreSQL",
"db_type": "postgresql",
"last_updated_at": "2026-05-21T13:23:07.188Z",
"schema": {
"connection": "Production PostgreSQL",
"tables": [
{
"table_name": "fact_sales",
"schema": "public",
"columns": [
{ "column_name": "sale_id", "data_type": "int" },
{ "column_name": "sale_date", "data_type": "datetime" }
]
}
]
}
}
| Field | Type | Description |
|---|---|---|
id, name, db_type |
string | Connection identity |
last_updated_at |
string | null | When the schema snapshot was taken, ISO 8601 |
schema |
object | The cached structure |
schema.connection |
string | null | Name of the connection the schema belongs to — repeats the top-level name. The same object with the same field is called db_schema in the connection details endpoint; v1 has no connection field |
schema.tables[] |
array | Tables with their column lists; the field set and the per-table schema semantics table_name, the table's schema (null for ClickHouse, which has no schemas), columns[] of column_name and data_type |
Errors
| Code | HTTP | Condition |
|---|---|---|
DF_API.CONNECTION_NOT_FOUND |
404 | The connection does not exist in the given version, or its database is not exposed by the public API. details addresses the connection_id field |
| Common errors | — | Section 4 of the API page |