Skip to main content

Coginiti API Reference

The Coginiti HTTP API enables users to interact with Coginiti through third-party applications. The API provides endpoints for browsing the catalog, executing queries and scripts, interacting with the semantic layer, and managing users and groups through SCIM integration.

Overview

Base URL

All API URLs referenced in this documentation have the following base URL unless otherwise specified:

{hostname}/api/v1

Replace {hostname} with your Coginiti instance hostname.

Supported Formats

  • Request Format: JSON
  • Response Format: JSON, CSV, Arrow Stream (execution endpoints)
  • Date Format: ISO 8601 (e.g., 2023-02-14T19:28:40.840Z)

Authentication

The Coginiti HTTP API requires authentication with a personal access token created in the Coginiti UI.

Personal Access Tokens

API requests that use a token must use the Authorization header. The header's value must specify the Bearer type followed by a space and the access token:

Authorization: Bearer {Token}

Creating a Personal Access Token

  1. Login to your Coginiti Team/Enterprise application
  2. In the upper-right corner of any page, click your profile icon, then click API Keys
  3. In the left sidebar click "Add" button to create a new entry for your token
  4. Under Token name, enter a name for the token
  5. Optionally, under Description, add a note to describe the purpose of the token
  6. Under Expiration, select an expiration for the token
  7. (For admin accounts only) Select an owner of the token if needed
  8. Click Save button
Security

Personal access tokens are like passwords and share the same inherent security risks. Keep your tokens secure, do not store them as plain text, and do not expose them to other persons. The token is only displayed once when created.

Deleting a Personal Access Token

  1. Login to your Coginiti Team/Enterprise application
  2. Click your profile icon, then click API Keys
  3. Select the token you want to delete
  4. Click Delete button

Once deleted, any scripts or applications using this token will no longer be able to access the Coginiti API.

Error Handling

Coginiti API uses conventional HTTP response codes to indicate the success or failure of an API request.

HTTP Status Codes

CodeDescription
200OK - Everything worked as expected
400Bad Request - The request was unacceptable since it didn't pass validation
401Unauthorized - No valid API key was provided
403Forbidden - The API key doesn't have permission to perform the request
404Not Found - The requested resource doesn't exist
409Conflict - The request conflicts with another request
412Precondition Failed - The ETag provided doesn't match the current version
422Unprocessable Entity - The request was well-formed but contains semantic errors
428Precondition Required - A required header (e.g., If-Match) is missing
500, 502, 503, 504Server Errors - Something went wrong on Coginiti's side

Error Response Format

Error messages are sent back in the response body using the following format:

{
"message": "The request was unacceptable, since it didn't pass validation",
"detail": "Name field should not be blank"
}
FieldDescription
messageA brief human-readable message
detailA lengthier explanation of the error (optional)

Catalog Browse API

The Catalog Browse API allows users to browse and retrieve catalog assets from their Coginiti workspace.

List Catalog Assets

List catalog assets by the given path. The response contains direct children of the given folder.

Endpoint: GET /catalog/list/{path}

Parameters:

  • path: Path of the asset with forward slash separators. Must be URL-encoded. Should start from namespace: @Personal, @Shared, @Project Hub

Example Request:

curl -X GET 'https://{hostname}/api/v1/catalog/list/@Personal/Reports/Sample%20Project' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_'

Response:

{
"data": [
{
"name": "Folder 1",
"path": "@Personal/Reports/Sample Project/Folder 1",
"type": "REGULAR_FOLDER",
"created_at": "2023-02-14T19:28:40.840Z",
"updated_at": "2023-02-14T19:28:40.840Z"
},
{
"name": "Asset 1",
"path": "@Personal/Reports/Sample Project/Asset 1",
"type": "SQL_SCRIPT",
"created_at": "2023-02-14T19:28:40.840Z",
"updated_at": "2023-02-14T19:28:40.840Z"
}
]
}

Response Fields:

FieldDescription
nameName of the catalog asset
pathPath of the asset within Coginiti Catalog
typeType of the catalog asset
created_atDate and time the asset was created (UTC)
updated_atDate and time the asset was updated (UTC)

Asset Types:

TypeDescription
REGULAR_FOLDERFolder
USER_HOMEUser home folder
SCHEDULED_EVENTSScheduled event
PROJECTCoginitiScript project
SQL_SCRIPTSQL Script
COGINITI_SCRIPTCoginitiScript
UPLOAD_TEMPLATEBulk load template
DATA_INSERT_TEMPLATEData insert template
PROJECT_MANIFESTProject manifest file

Project Hub Path Convention: For @Project Hub assets, the path should begin with @Project Hub/{project name}/ rather than the full absolute path. For example: @Project Hub/sales_2025/folder/of/the/project

Errors:

ConditionStatusDetail
Path not found404Path not found: @Personal/Reports
Path points to asset instead of folder400Not a folder: @Personal/Reports/asset1
No permission for path403You don't have permission to perform the request

Get Catalog Asset by Path

Retrieve a single asset by path.

Endpoint: GET /catalog/by-path/{path}

Example Request:

curl -X GET 'https://{hostname}/api/v1/catalog/by-path/@Personal/Reports/Sample%20Project/my_asset' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_'

Response:

{
"name": "my_asset",
"description": "Asset description",
"path": "@Personal/Reports/Sample Project/my_asset",
"type": "SQL_SCRIPT",
"data": {
"content": "SELECT * FROM fact_sales"
},
"created_at": "2023-02-14T19:28:40.840Z",
"updated_at": "2023-02-14T19:28:40.840Z"
}

Response Fields:

FieldDescription
nameName of the catalog asset
descriptionAsset description
pathAbsolute path of the asset within Catalog
typeAsset type
dataObject containing asset-type specific properties
created_atDate and time the asset was created (UTC)
updated_atDate and time the asset was updated (UTC)

For SQL_SCRIPT and COGINITI_SCRIPT types, the data object contains:

FieldDescription
contentContent of the SQL/CoginitiScript file

Response Headers:

HeaderDescription
ETagEntity tag representing the current version of the resource

The ETag header is used for optimistic concurrency control in management operations.

Catalog Management API

The Catalog Management API allows users to create, update, and delete folders and assets in the Coginiti catalog.

Concurrency Control

The management endpoints use optimistic concurrency control with ETags to prevent conflicts when multiple clients modify the same resource.

How it works:

  1. When you retrieve a resource with GET /catalog/by-path/{path}, the response includes an ETag header
  2. To update or delete the resource, include the ETag value in the If-Match header
  3. If the resource was modified since you retrieved it, the server returns 412 Precondition Failed with the current ETag
  4. Retry by fetching the latest version and resubmitting with the new ETag

ETag Format:

ETags are quoted numeric values representing the resource revision:

ETag: "42"

Create Folder

Create a new folder in the catalog.

Endpoint: POST /catalog/folders

Request Body:

{
"parent_path": "@Personal/Reports",
"name": "Q1 Analysis",
"folder_type": "REGULAR"
}

Request Fields:

FieldRequiredDescription
parent_pathYesPath to the parent folder. Must start with @Personal, @Shared, or @Project Hub
nameYesName of the folder (max 255 characters, cannot contain /)
folder_typeNoType of folder: REGULAR (default) or PROJECT

Example Request:

curl -X POST 'https://{hostname}/api/v1/catalog/folders' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--data-raw '{
"parent_path": "@Personal",
"name": "My Reports",
"folder_type": "REGULAR"
}'

Response (201 Created):

{
"name": "My Reports",
"path": "@Personal/My Reports",
"type": "REGULAR_FOLDER",
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z"
}

Response Headers:

HeaderDescription
ETagEntity tag for the created folder
LocationURL path to retrieve the created folder

Folder Types:

TypeDescription
REGULARStandard folder for organizing assets
PROJECTCoginitiScript project folder

Errors:

ConditionStatusDetail
Missing parent_path400Parent path is required
Missing name400Name is required
Name contains /400Name cannot contain '/' character
Name exceeds 255 characters400Name exceeds maximum length of 255 characters
Invalid folder_type400Invalid value for folder_type
Parent path not found404Path not found: @Personal/nonexistent
Name already exists400Name already exists.
Parent is an asset422Parent path must be a folder
No permission404Path not found: [path]

Create Asset

Create a new SQL script or CoginitiScript in the catalog.

Endpoint: POST /catalog/assets

Request Body:

{
"parent_path": "@Personal/Reports",
"name": "sales_report",
"content": "SELECT * FROM fact_sales WHERE year = 2024"
}

Request Fields:

FieldRequiredDescription
parent_pathYesPath to the parent folder
nameYesName of the asset (max 255 characters, cannot contain /)
contentYesContent of the script (max 15 MB)

Example Request:

curl -X POST 'https://{hostname}/api/v1/catalog/assets' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--data-raw '{
"parent_path": "@Personal",
"name": "customer_analysis",
"content": "SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id"
}'

Response (201 Created):

{
"name": "customer_analysis",
"path": "@Personal/customer_analysis",
"type": "SQL_SCRIPT",
"data": {
"content": "SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id"
},
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z"
}

Asset Type Detection:

The asset type is automatically determined based on content:

  • Content containing CoginitiScript blocks (#+src, {{ }}) is created as COGINITI_SCRIPT
  • All other content is created as SQL_SCRIPT

Errors:

ConditionStatusDetail
Missing parent_path400Parent path is required
Missing name400Name is required
Missing content400Content is required
Name contains /400Name cannot contain '/' character
Name exceeds 255 characters400Name exceeds maximum length of 255 characters
Content exceeds 15 MB400Content exceeds the 15 MB size limit
Parent path not found404Path not found: @Personal/nonexistent
Name already exists400Name already exists.
No permission404Path not found: [path]

Update Folder

Rename an existing folder.

Endpoint: PUT /catalog/folders/{path}

Required Headers:

HeaderDescription
If-MatchETag value from a previous GET request

Request Body:

{
"name": "New Folder Name"
}

Request Fields:

FieldRequiredDescription
nameNoNew name for the folder

Example Request:

curl -X PUT 'https://{hostname}/api/v1/catalog/folders/@Personal/Old%20Name' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--header 'If-Match: "42"' \
--data-raw '{
"name": "New Name"
}'

Response (200 OK):

{
"name": "New Name",
"path": "@Personal/New Name",
"type": "REGULAR_FOLDER",
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T11:00:00.000Z"
}
Path Updates

When a folder is renamed, all child assets and subfolders automatically have their paths updated to reflect the new parent path.

Errors:

ConditionStatusDetail
Missing If-Match header428If-Match header is required
ETag doesn't match412Entity has been modified. Refresh and retry with current ETag: "43"
Path not found404Path not found: @Personal/nonexistent
Path is an asset422Path must be a folder
Name contains /400Name cannot contain '/' character
Name exceeds 255 characters400Name exceeds maximum length of 255 characters
Name already exists400Name already exists.
No permission404Path not found: [path]

Update Asset

Update an asset's name and/or content.

Endpoint: PUT /catalog/assets/{path}

Required Headers:

HeaderDescription
If-MatchETag value from a previous GET request

Request Body:

{
"name": "updated_report",
"content": "SELECT * FROM fact_sales WHERE year = 2025"
}

Request Fields:

FieldRequiredDescription
nameNoNew name for the asset
contentNoNew content for the asset

At least one field should be provided.

Example Request:

curl -X PUT 'https://{hostname}/api/v1/catalog/assets/@Personal/my_script' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--header 'If-Match: "42"' \
--data-raw '{
"content": "SELECT * FROM updated_table"
}'

Response (200 OK):

{
"name": "my_script",
"path": "@Personal/my_script",
"type": "SQL_SCRIPT",
"data": {
"content": "SELECT * FROM updated_table"
},
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T11:00:00.000Z"
}

Errors:

ConditionStatusDetail
Missing If-Match header428If-Match header is required
ETag doesn't match412Entity has been modified. Refresh and retry with current ETag: "43"
Path not found404Path not found: @Personal/nonexistent
Path is a folder422Path must be an asset
Content exceeds 15 MB400Content exceeds the 15 MB size limit
Name contains /400Name cannot contain '/' character
Name exceeds 255 characters400Name exceeds maximum length of 255 characters
Name already exists400Name already exists.
No permission404Path not found: [path]

Delete Folder

Delete an empty folder from the catalog.

Endpoint: DELETE /catalog/folders/{path}

Required Headers:

HeaderDescription
If-MatchETag value from a previous GET request

Example Request:

curl -X DELETE 'https://{hostname}/api/v1/catalog/folders/@Personal/empty-folder' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'If-Match: "42"'

Response: 204 No Content

Errors:

ConditionStatusDetail
Missing If-Match header428If-Match header is required
ETag doesn't match412Entity has been modified. Refresh and retry with current ETag: "43"
Path not found404Path not found: @Personal/nonexistent
Path is an asset422Path must be a folder
Folder not empty400Folder is not empty
No permission404Path not found: [path]

Delete Asset

Delete an asset from the catalog.

Endpoint: DELETE /catalog/assets/{path}

Required Headers:

HeaderDescription
If-MatchETag value from a previous GET request

Example Request:

curl -X DELETE 'https://{hostname}/api/v1/catalog/assets/@Personal/old_script' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'If-Match: "42"'

Response: 204 No Content

Errors:

ConditionStatusDetail
Missing If-Match header428If-Match header is required
ETag doesn't match412Entity has been modified. Refresh and retry with current ETag: "43"
Path not found404Path not found: @Personal/nonexistent
Path is a folder422Path must be an asset
No permission404Path not found: [path]

Execution API

The Execution API allows users to execute SQL queries and catalog assets and retrieve data sets with governance controls. It's compatible with popular data visualization tools like PowerBI, Excel, and Jupyter Notebooks, as well as orchestration tools like Dagster or Kestra.

Execute Script

Execute a script from the catalog and get a single result set. If the script produces multiple result sets, only the first one is returned.

Endpoint: POST /exec/script

Request Body:

{
"path": "@Personal/Reports/performance",
"params": {
"start_date": "2012-01-01",
"end_date": "2012-12-31"
},
"connection": {
"name": "My Connection"
}
}

Request Fields:

FieldDescription
pathAbsolute path to the catalog asset within the catalog
paramsObject with parameter names and values for script substitution
connection.nameName of the connection

Example Request:

curl -X POST '{hostname}/api/v1/exec/script' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"path": "@Personal/Reports/performance",
"params": {
"start_date": "2012-01-01",
"end_date": "2012-12-31"
},
"connection": {
"name": "My Connection"
}
}'

Response:

{
"result": {
"columns": [
{
"name": "col_1",
"type": "INTEGER"
},
{
"name": "col_2",
"type": "STRING"
}
],
"data": [
[1, "a"],
[2, "b"]
]
},
"stats": {
"started_at": "2023-08-10T15:06:57.581939751Z",
"finished_at": "2023-08-10T15:07:57.581939751Z",
"duration_ms": 1000
}
}

Response Headers:

X-Coginiti-Execution-Started-At: 2023-11-01T10:03:17.354799859Z

Execute CoginitiScript Block

Execute a specific CoginitiScript block from a catalog asset.

Endpoint: POST /exec/block

Request Body:

{
"package": "@Personal/My Projects/demo/customers",
"block_name": "CustomersWithEmailDomain",
"args": {
"domain": "coginiti.co"
},
"params": {},
"connection": {
"name": "My Connection"
}
}

Request Fields:

FieldDescription
packageAbsolute path to the CoginitiScript package
block_nameName of the block to execute
argsObject with argument names and values for the block
paramsObject with parameter names and values for script substitution
connection.nameName of the connection

Complex Arguments:

You can pass lists and maps as block arguments using JSON:

# Passing a list
curl -X POST '{hostname}/api/v1/exec/block' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--data-raw '{
"package": "@Personal/My Projects/demo/customers",
"block_name": "CustomersWithEmailDomain",
"args": {
"domains": ["coginiti.co", "gmail.com"]
},
"connection": {
"name": "My Connection"
}
}'

# Passing a map
curl -X POST '{hostname}/api/v1/exec/block' \
--data-raw '{
"args": {
"payment_fees": {
"credit_card": 0.25,
"paypal": 0.3
}
}
}'

Execute SQL

Execute a SQL query directly against a connection and get a single result set. This endpoint lets you run queries without first saving them as catalog assets.

Semantic vs physical connections

When the connection is a semantic layer connection, write the query in semantic SQL against semantic entities. When the connection is a physical database connection, write native SQL for that platform. For details on the semantic SQL dialect, see the Semantic SQL Reference.

Endpoint: POST /exec/sql

Request Body:

{
"sql": "SELECT order_date, SUM(amount) FROM fact_orders GROUP BY order_date",
"params": {
"$start_date": "2024-01-01"
},
"connection": {
"name": "My Connection",
"overrides": {
"database": "analytics_db",
"schema": "public"
}
}
}

Request Fields:

FieldRequiredDescription
sqlYesSQL query to execute
paramsNoObject with parameter names and values for query substitution. Parameter names use $ or $$ prefix
connectionYesConnection specification
connection.nameYesName of the connection
connection.overridesNoObject with connection parameter overrides (e.g., database, schema)

Example Request:

curl -X POST '{hostname}/api/v1/exec/sql' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"sql": "SELECT order_date, SUM(amount) FROM fact_orders GROUP BY order_date",
"connection": {
"name": "My Connection"
}
}'

Example Request (with params and overrides):

curl -X POST '{hostname}/api/v1/exec/sql' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
"sql": "SELECT order_date, SUM(amount) FROM fact_orders WHERE order_date >= $start_date GROUP BY order_date",
"params": {
"$start_date": "2024-01-01"
},
"connection": {
"name": "My Connection",
"overrides": {
"database": "analytics_db",
"schema": "public"
}
}
}'

Response:

{
"result": {
"columns": [
{
"name": "order_date",
"type": "DATE"
},
{
"name": "SUM(amount)",
"type": "DECIMAL"
}
],
"data": [
["2024-01-15", 15230.50],
["2024-01-16", 18445.00]
]
},
"stats": {
"started_at": "2024-01-20T10:00:00.000000000Z",
"finished_at": "2024-01-20T10:00:01.250000000Z",
"duration_ms": 1250
}
}

Response Headers:

X-Coginiti-Execution-Started-At: 2024-01-20T10:00:00.000000000Z

Errors:

ConditionStatusDetail
Missing sql400SQL query is required
Missing connection400Connection is required
Missing connection name400Connection name is required
Connection not found404Connection not found: [name]

Response Formats

The Accept request header specifies the response format:

  • application/json (default)
  • text/csv
  • application/vnd.apache.arrow.stream

Running CoginitiScript Tests

Execute CoginitiScript tests for automated data quality testing and CI/CD workflows.

Individual Test Block:

curl -X POST '{hostname}/api/v1/exec/block' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--data-raw '{
"package": "@Personal/data_quality_tests",
"block_name": "TestCustomerDataIntegrity",
"connection": {
"name": "My Connection"
}
}'

Test Suite with test.Run():

curl -X POST '{hostname}/api/v1/exec/script' \
--data-raw '{
"path": "@Personal/test_suites/nightly_tests",
"connection": {
"name": "My Connection"
}
}'

Test Response Format:

{
"tests": [
{
"package_name": "@Personal/data_quality_tests",
"test_name": "TestCustomerEmailFormat",
"status": "PASSED",
"message": "Test passed",
"started_at": "2023-11-01T10:03:17.500Z",
"finished_at": "2023-11-01T10:03:18.250Z",
"duration_ms": 750
},
{
"package_name": "@Personal/data_quality_tests",
"test_name": "TestOrderIntegrity",
"status": "FAILED",
"message": "Test assertion failed: The test returned non-empty recordset",
"started_at": "2023-11-01T10:03:18.250Z",
"finished_at": "2023-11-01T10:03:19.100Z",
"duration_ms": 850
}
],
"stats": {
"tests": 2,
"passed": 1,
"failed": 1,
"started_at": "2023-11-01T10:03:17.354Z",
"finished_at": "2023-11-01T10:03:19.100Z",
"duration_ms": 1746
}
}

Test HTTP Status Codes:

Starting from version 25.6, the API returns HTTP 200 OK for test executions even when tests fail. Check the stats.failed field in the response to determine test results.

SCIM API

The SCIM (System for Cross-domain Identity Management) v2.0 API enables integration with identity providers like Azure AD, Okta, and other SCIM-compliant systems.

Base URL

{hostname}/api/v1/scim/v2

Users

List Users

Endpoint: GET /Users

Query Parameters:

ParameterTypeDescription
countintegerMaximum number of users to return (e.g., 50)
startIndexinteger1-based index where to start results (e.g., 11)
filterstringSCIM expression to filter users (limited to userName equality)

Example Request:

curl -X GET 'https://{hostname}/api/v1/scim/v2/Users' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_'

Response:

{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 1,
"Resources": [
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"id": "user-id",
"userName": "john.doe@example.com",
"active": true,
"emails": [
{
"value": "john.doe@example.com",
"primary": true
}
],
"name": {
"givenName": "John",
"familyName": "Doe",
"fullName": "John Doe",
"formatted": "John Doe"
},
"displayName": "John Doe",
"roles": [{"value": "admin"}],
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"organization": "Example Corp",
"superUser": false,
"userStatus": "CONFIRMED",
"external": false
},
"groups": []
}
]
}

Filter Limitations:

The current SCIM implementation supports only:

  • userName eq "john.doe@example.com" (userName equality filters)
  • Other attributes and complex expressions are not yet supported
  • Unsupported filters return all results

Create User

Endpoint: POST /Users

Request Body:

{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"userName": "testuser1",
"active": true,
"emails": [
{
"value": "testuser1@example.com",
"primary": true
}
],
"name": {
"givenName": "Test",
"familyName": "User",
"fullName": "Test User",
"formatted": "Test User"
},
"displayName": "Test User",
"roles": [{"value": "software_engineer"}],
"password": "SecurePass123!",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"organization": "Example Corp",
"superUser": false,
"userStatus": "CONFIRMED",
"external": false
}
}

Retrieve User

Endpoint: GET /Users/{id}

Update User

Endpoint: PUT /Users/{id}

Delete User

Endpoint: DELETE /Users/{id}

Response:

{
"status": "200"
}

Groups

List Groups

Endpoint: GET /Groups

Query Parameters:

ParameterTypeDescription
countintegerMaximum number of groups to return
startIndexinteger1-based index where to start results
filterstringSCIM expression to filter groups

Response:

{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 1,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "group-id",
"displayName": "Development Team",
"description": "Software development team",
"external": false,
"members": [
{
"value": "user-id",
"display": "John Doe",
"$ref": "link-to-user",
"type": "USER"
}
]
}
]
}

Create Group

Endpoint: POST /Groups

Retrieve Group

Endpoint: GET /Groups/{id}

Update Group

Endpoint: PUT /Groups/{id}

Delete Group

Endpoint: DELETE /Groups/{id}

Semantic Layer API

The Semantic Layer API allows users to retrieve metadata about their semantic model and generate SQL from semantic queries. These endpoints use the same authentication mechanism as the rest of the Coginiti API.

Get semantic layer metadata

Retrieve the full semantic layer metadata for a given connection, including all layers, domains, entities, dimensions, measures, and relationships.

Endpoint: GET /v1/semantic-layer/metadata/{connection_name}

Parameters:

ParameterLocationRequiredDescription
connection_namepathYesName of the database connection. Must be URL-encoded if it contains spaces or special characters

Example Request:

curl -X GET 'https://{hostname}/api/v1/semantic-layer/metadata/My%20Connection' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_'

Response:

{
"version": "1.0",
"layers": [
{
"name": "sales_analytics",
"label": "Sales Analytics",
"environment": {
"platform": "snowflake"
},
"domains": [
{
"name": "orders",
"label": "Orders",
"description": "Order transactions and line items",
"entities": [
{
"name": "fact_orders",
"label": "Orders",
"description": "Order fact table",
"table_name": "analytics.fact_orders",
"dimensions": [
{
"name": "order_date",
"label": "Order Date",
"data_type": "DATE",
"hidden": false,
"expr": "order_date"
}
],
"measures": [
{
"name": "total_revenue",
"label": "Total Revenue",
"data_type": "DECIMAL",
"hidden": false,
"expr": "amount",
"aggregation_type": "SUM"
}
],
"relationships": [
{
"name": "customer",
"join_type": "MANY_TO_ONE",
"to_entity": "dim_customers",
"expr": "fact_orders.customer_id = dim_customers.id"
}
]
}
]
}
]
}
]
}

Response Fields:

FieldTypeDescription
versionstringSchema version of the semantic model
layersarrayList of semantic layers

Layer Object:

FieldTypeDescription
namestringUnique identifier for the layer
labelstringDisplay name (optional)
environmentobjectTarget platform configuration
environment.platformstringData platform identifier (e.g., snowflake, databricks)
domainsarrayList of domain definitions

Domain Object:

FieldTypeDescription
namestringUnique identifier for the domain
labelstringDisplay name (optional)
descriptionstringDomain description (optional)
entitiesarrayList of entity definitions

Entity Object:

FieldTypeDescription
namestringUnique identifier for the entity
labelstringDisplay name (optional)
descriptionstringEntity description (optional)
table_namestringUnderlying database table (optional, mutually exclusive with query)
querystringUnderlying SQL query (optional, mutually exclusive with table_name)
dimensionsarrayList of dimension definitions
measuresarrayList of measure definitions
relationshipsarrayList of relationship definitions

Dimension Object:

FieldTypeDescription
namestringUnique identifier for the dimension
labelstringDisplay name (optional)
data_typestringSemantic data type: TEXT, NUMBER, INTEGER, FLOAT, DECIMAL, DATE, DATETIME, BOOLEAN
hiddenbooleanWhether the dimension is hidden from queries
exprstringSQL expression for this dimension

Measure Object:

FieldTypeDescription
namestringUnique identifier for the measure
labelstringDisplay name (optional)
data_typestringSemantic data type: NUMBER, INTEGER, FLOAT, DECIMAL
hiddenbooleanWhether the measure is hidden from queries
exprstringSQL expression for this measure (the column or expression to aggregate)
aggregation_typestringAggregation function: SUM, COUNT, COUNT_DISTINCT, AVG, MIN, MAX, MEDIAN, CUSTOM

Relationship Object:

FieldTypeDescription
namestringUnique identifier for the relationship
join_typestringCardinality type: ONE_TO_ONE, ONE_TO_MANY, MANY_TO_ONE
to_entitystringTarget entity name
exprstringSQL join condition expression

Errors:

ConditionStatusDetail
Connection not found400Connection not found
No semantic model configured400No semantic model found for connection
No permission for connection403You don't have permission to access this connection

Generate SQL from semantic query

Generate a SQL query from a semantic query specification. The semantic layer translates field references, filters, and ordering into platform-specific SQL.

Endpoint: POST /v1/semantic-layer/codegen/sql

Request Body:

{
"connection_name": "My Connection",
"query": {
"dimensions": [
{
"entity": "fact_orders",
"field": "order_date"
}
],
"measures": [
{
"entity": "fact_orders",
"field": "total_revenue",
"alias": "revenue"
}
],
"order_by": [
{
"field": {
"entity": "fact_orders",
"field": "order_date"
},
"desc": true
}
],
"limit": {
"fetch": 100
}
}
}

Request Fields:

FieldRequiredDescription
connection_nameNoName of the database connection
queryYesSemantic query specification
query.dimensionsConditionalArray of dimension field references. At least one of dimensions or measures is required
query.measuresConditionalArray of measure field references. At least one of dimensions or measures is required
query.filtersNoArray of filter conditions
query.order_byNoArray of ordering specifications
query.limitNoPagination specification

Field Reference Object:

FieldRequiredDescription
entityYesEntity name containing the field
fieldYesDimension or measure name
aliasNoColumn alias in the generated SQL

Order By Object:

FieldRequiredDescription
fieldYesField reference object
descNoSort descending (default: false)

Limit Object:

FieldRequiredDescription
fetchYesNumber of rows to return (must be greater than 0)
skipNoNumber of rows to skip (must be 0 or greater)

Filter types

Filters use a discriminated union pattern with a type field. Filters can be composed to build complex conditions using logical operators.

TypeDescriptionAdditional Fields
CONDITIONA single field conditionfield (field reference), operator (operator object)
ANDLogical AND of multiple filtersfilters (array of filter objects)
ORLogical OR of multiple filtersfilters (array of filter objects)

Operator types

Operators specify the comparison applied to a field value. Each operator uses a type field and may include value or values depending on the operator category.

String and list operators (use values array):

TypeDescriptionExample Values
EQUALSField equals one of the values["active", "pending"]
NOT_EQUALSField does not equal any of the values["cancelled"]
CONTAINSField contains one of the substrings["corp"]
NOT_CONTAINSField does not contain the substring["test"]
STARTS_WITHField starts with one of the prefixes["US-"]
NOT_STARTS_WITHField does not start with the prefix["TEMP-"]
ENDS_WITHField ends with one of the suffixes[".com"]
NOT_ENDS_WITHField does not end with the suffix[".test"]

Comparison operators (use value string):

TypeDescriptionExample Value
GREATER_THANField is greater than the value"1000"
GREATER_THAN_OR_EQUALField is greater than or equal to the value"0"
LESS_THANField is less than the value"500"
LESS_THAN_OR_EQUALField is less than or equal to the value"100"

Date range operators (use values array with two date strings):

TypeDescriptionExample Values
IN_DATE_RANGEField is within the date range (inclusive)["2024-01-01", "2024-12-31"]
NOT_IN_DATE_RANGEField is outside the date range["2024-01-01", "2024-03-31"]

Date comparison operators (use value string):

TypeDescriptionExample Value
BEFORE_DATEField is before the date"2024-01-01"
AFTER_DATEField is after the date"2024-12-31"
BEFORE_OR_ON_DATEField is before or on the date"2024-06-30"
AFTER_OR_ON_DATEField is on or after the date"2024-01-01"

Null check operators (no value field):

TypeDescription
IS_NULLField is null
IS_NOT_NULLField is not null

Example Request (simple query):

curl -X POST 'https://{hostname}/api/v1/semantic-layer/codegen/sql' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--data-raw '{
"connection_name": "My Connection",
"query": {
"dimensions": [
{"entity": "fact_orders", "field": "order_date"}
],
"measures": [
{"entity": "fact_orders", "field": "total_revenue"}
]
}
}'

Example Request (with filters):

curl -X POST 'https://{hostname}/api/v1/semantic-layer/codegen/sql' \
--header 'Authorization: Bearer _YOUR_ACCESS_TOKEN_HERE_' \
--header 'Content-Type: application/json' \
--data-raw '{
"connection_name": "My Connection",
"query": {
"dimensions": [
{"entity": "fact_orders", "field": "order_date"}
],
"measures": [
{"entity": "fact_orders", "field": "total_revenue", "alias": "revenue"}
],
"filters": [
{
"type": "AND",
"filters": [
{
"type": "CONDITION",
"field": {"entity": "fact_orders", "field": "order_date"},
"operator": {
"type": "IN_DATE_RANGE",
"values": ["2024-01-01", "2024-12-31"]
}
},
{
"type": "CONDITION",
"field": {"entity": "fact_orders", "field": "total_revenue"},
"operator": {
"type": "GREATER_THAN",
"value": "1000"
}
}
]
}
],
"order_by": [
{
"field": {"entity": "fact_orders", "field": "total_revenue"},
"desc": true
}
],
"limit": {"fetch": 50, "skip": 0}
}
}'

Response:

{
"sql": "SELECT order_date, SUM(amount) AS revenue FROM analytics.fact_orders WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31' AND amount > 1000 GROUP BY order_date ORDER BY SUM(amount) DESC LIMIT 50"
}

Response Fields:

FieldTypeDescription
sqlstringGenerated SQL query for the target platform

Errors:

ConditionStatusDetail
Missing dimensions and measures400At least one dimension or measure is required
Invalid entity name400Entity not found: [entity_name]
Invalid field name400Field not found: [field_name] in entity [entity_name]
Invalid operator type400Invalid operator type: [type]
Invalid limit values400Fetch must be greater than 0
No semantic model configured400No semantic model found for connection
No permission for connection403You don't have permission to access this connection
Related Documentation

For details on defining semantic models, see the Semantic Model Reference. For the SQL dialect supported in semantic layer expressions, see the Semantic SQL Reference.

Integration Examples

CI/CD Pipeline Integration

Example bash script for processing test results in CI:

#!/bin/bash

# Execute tests via API
response=$(curl -s -X POST '{hostname}/api/v1/exec/script' \
--header 'Authorization: Bearer ${ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
"path": "@Personal/test_suites/ci_tests",
"connection": {
"name": "CI Connection"
}
}')

# Extract test statistics
failed_tests=$(echo "$response" | jq '.stats.failed')
total_tests=$(echo "$response" | jq '.stats.tests')

echo "Test Results: $((total_tests - failed_tests))/$total_tests passed"

# Exit with error code if any tests failed
if [ "$failed_tests" -gt 0 ]; then
echo "$failed_tests test(s) failed"
echo "$response" | jq '.tests[] | select(.status == "FAILED") | {test_name, message}'
exit 1
else
echo "All tests passed"
exit 0
fi

Support

For API support and questions: