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
- Login to your Coginiti Team/Enterprise application
- In the upper-right corner of any page, click your profile icon, then click API Keys
- In the left sidebar click "Add" button to create a new entry for your token
- Under Token name, enter a name for the token
- Optionally, under Description, add a note to describe the purpose of the token
- Under Expiration, select an expiration for the token
- (For admin accounts only) Select an owner of the token if needed
- Click Save button
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
- Login to your Coginiti Team/Enterprise application
- Click your profile icon, then click API Keys
- Select the token you want to delete
- 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
| Code | Description |
|---|---|
200 | OK - Everything worked as expected |
400 | Bad Request - The request was unacceptable since it didn't pass validation |
401 | Unauthorized - No valid API key was provided |
403 | Forbidden - The API key doesn't have permission to perform the request |
404 | Not Found - The requested resource doesn't exist |
409 | Conflict - The request conflicts with another request |
412 | Precondition Failed - The ETag provided doesn't match the current version |
422 | Unprocessable Entity - The request was well-formed but contains semantic errors |
428 | Precondition Required - A required header (e.g., If-Match) is missing |
500, 502, 503, 504 | Server 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"
}
| Field | Description |
|---|---|
message | A brief human-readable message |
detail | A 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:
| Field | Description |
|---|---|
name | Name of the catalog asset |
path | Path of the asset within Coginiti Catalog |
type | Type of the catalog asset |
created_at | Date and time the asset was created (UTC) |
updated_at | Date and time the asset was updated (UTC) |
Asset Types:
| Type | Description |
|---|---|
REGULAR_FOLDER | Folder |
USER_HOME | User home folder |
SCHEDULED_EVENTS | Scheduled event |
PROJECT | CoginitiScript project |
SQL_SCRIPT | SQL Script |
COGINITI_SCRIPT | CoginitiScript |
UPLOAD_TEMPLATE | Bulk load template |
DATA_INSERT_TEMPLATE | Data insert template |
PROJECT_MANIFEST | Project 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:
| Condition | Status | Detail |
|---|---|---|
| Path not found | 404 | Path not found: @Personal/Reports |
| Path points to asset instead of folder | 400 | Not a folder: @Personal/Reports/asset1 |
| No permission for path | 403 | You 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:
| Field | Description |
|---|---|
name | Name of the catalog asset |
description | Asset description |
path | Absolute path of the asset within Catalog |
type | Asset type |
data | Object containing asset-type specific properties |
created_at | Date and time the asset was created (UTC) |
updated_at | Date and time the asset was updated (UTC) |
For SQL_SCRIPT and COGINITI_SCRIPT types, the data object contains:
| Field | Description |
|---|---|
content | Content of the SQL/CoginitiScript file |
Response Headers:
| Header | Description |
|---|---|
ETag | Entity 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:
- When you retrieve a resource with
GET /catalog/by-path/{path}, the response includes anETagheader - To update or delete the resource, include the ETag value in the
If-Matchheader - If the resource was modified since you retrieved it, the server returns
412 Precondition Failedwith the current ETag - 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:
| Field | Required | Description |
|---|---|---|
parent_path | Yes | Path to the parent folder. Must start with @Personal, @Shared, or @Project Hub |
name | Yes | Name of the folder (max 255 characters, cannot contain /) |
folder_type | No | Type 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:
| Header | Description |
|---|---|
ETag | Entity tag for the created folder |
Location | URL path to retrieve the created folder |
Folder Types:
| Type | Description |
|---|---|
REGULAR | Standard folder for organizing assets |
PROJECT | CoginitiScript project folder |
Errors:
| Condition | Status | Detail |
|---|---|---|
| Missing parent_path | 400 | Parent path is required |
| Missing name | 400 | Name is required |
Name contains / | 400 | Name cannot contain '/' character |
| Name exceeds 255 characters | 400 | Name exceeds maximum length of 255 characters |
| Invalid folder_type | 400 | Invalid value for folder_type |
| Parent path not found | 404 | Path not found: @Personal/nonexistent |
| Name already exists | 400 | Name already exists. |
| Parent is an asset | 422 | Parent path must be a folder |
| No permission | 404 | Path 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:
| Field | Required | Description |
|---|---|---|
parent_path | Yes | Path to the parent folder |
name | Yes | Name of the asset (max 255 characters, cannot contain /) |
content | Yes | Content 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 asCOGINITI_SCRIPT - All other content is created as
SQL_SCRIPT
Errors:
| Condition | Status | Detail |
|---|---|---|
| Missing parent_path | 400 | Parent path is required |
| Missing name | 400 | Name is required |
| Missing content | 400 | Content is required |
Name contains / | 400 | Name cannot contain '/' character |
| Name exceeds 255 characters | 400 | Name exceeds maximum length of 255 characters |
| Content exceeds 15 MB | 400 | Content exceeds the 15 MB size limit |
| Parent path not found | 404 | Path not found: @Personal/nonexistent |
| Name already exists | 400 | Name already exists. |
| No permission | 404 | Path not found: [path] |
Update Folder
Rename an existing folder.
Endpoint: PUT /catalog/folders/{path}
Required Headers:
| Header | Description |
|---|---|
If-Match | ETag value from a previous GET request |
Request Body:
{
"name": "New Folder Name"
}
Request Fields:
| Field | Required | Description |
|---|---|---|
name | No | New 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"
}
When a folder is renamed, all child assets and subfolders automatically have their paths updated to reflect the new parent path.
Errors:
| Condition | Status | Detail |
|---|---|---|
| Missing If-Match header | 428 | If-Match header is required |
| ETag doesn't match | 412 | Entity has been modified. Refresh and retry with current ETag: "43" |
| Path not found | 404 | Path not found: @Personal/nonexistent |
| Path is an asset | 422 | Path must be a folder |
Name contains / | 400 | Name cannot contain '/' character |
| Name exceeds 255 characters | 400 | Name exceeds maximum length of 255 characters |
| Name already exists | 400 | Name already exists. |
| No permission | 404 | Path not found: [path] |
Update Asset
Update an asset's name and/or content.
Endpoint: PUT /catalog/assets/{path}
Required Headers:
| Header | Description |
|---|---|
If-Match | ETag value from a previous GET request |
Request Body:
{
"name": "updated_report",
"content": "SELECT * FROM fact_sales WHERE year = 2025"
}
Request Fields:
| Field | Required | Description |
|---|---|---|
name | No | New name for the asset |
content | No | New 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:
| Condition | Status | Detail |
|---|---|---|
| Missing If-Match header | 428 | If-Match header is required |
| ETag doesn't match | 412 | Entity has been modified. Refresh and retry with current ETag: "43" |
| Path not found | 404 | Path not found: @Personal/nonexistent |
| Path is a folder | 422 | Path must be an asset |
| Content exceeds 15 MB | 400 | Content exceeds the 15 MB size limit |
Name contains / | 400 | Name cannot contain '/' character |
| Name exceeds 255 characters | 400 | Name exceeds maximum length of 255 characters |
| Name already exists | 400 | Name already exists. |
| No permission | 404 | Path not found: [path] |
Delete Folder
Delete an empty folder from the catalog.
Endpoint: DELETE /catalog/folders/{path}
Required Headers:
| Header | Description |
|---|---|
If-Match | ETag 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:
| Condition | Status | Detail |
|---|---|---|
| Missing If-Match header | 428 | If-Match header is required |
| ETag doesn't match | 412 | Entity has been modified. Refresh and retry with current ETag: "43" |
| Path not found | 404 | Path not found: @Personal/nonexistent |
| Path is an asset | 422 | Path must be a folder |
| Folder not empty | 400 | Folder is not empty |
| No permission | 404 | Path not found: [path] |
Delete Asset
Delete an asset from the catalog.
Endpoint: DELETE /catalog/assets/{path}
Required Headers:
| Header | Description |
|---|---|
If-Match | ETag 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:
| Condition | Status | Detail |
|---|---|---|
| Missing If-Match header | 428 | If-Match header is required |
| ETag doesn't match | 412 | Entity has been modified. Refresh and retry with current ETag: "43" |
| Path not found | 404 | Path not found: @Personal/nonexistent |
| Path is a folder | 422 | Path must be an asset |
| No permission | 404 | Path 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:
| Field | Description |
|---|---|
path | Absolute path to the catalog asset within the catalog |
params | Object with parameter names and values for script substitution |
connection.name | Name 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:
| Field | Description |
|---|---|
package | Absolute path to the CoginitiScript package |
block_name | Name of the block to execute |
args | Object with argument names and values for the block |
params | Object with parameter names and values for script substitution |
connection.name | Name 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.
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:
| Field | Required | Description |
|---|---|---|
sql | Yes | SQL query to execute |
params | No | Object with parameter names and values for query substitution. Parameter names use $ or $$ prefix |
connection | Yes | Connection specification |
connection.name | Yes | Name of the connection |
connection.overrides | No | Object 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:
| Condition | Status | Detail |
|---|---|---|
| Missing sql | 400 | SQL query is required |
| Missing connection | 400 | Connection is required |
| Missing connection name | 400 | Connection name is required |
| Connection not found | 404 | Connection not found: [name] |
Response Formats
The Accept request header specifies the response format:
application/json(default)text/csvapplication/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:
| Parameter | Type | Description |
|---|---|---|
count | integer | Maximum number of users to return (e.g., 50) |
startIndex | integer | 1-based index where to start results (e.g., 11) |
filter | string | SCIM 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:
| Parameter | Type | Description |
|---|---|---|
count | integer | Maximum number of groups to return |
startIndex | integer | 1-based index where to start results |
filter | string | SCIM 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:
| Parameter | Location | Required | Description |
|---|---|---|---|
connection_name | path | Yes | Name 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:
| Field | Type | Description |
|---|---|---|
version | string | Schema version of the semantic model |
layers | array | List of semantic layers |
Layer Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the layer |
label | string | Display name (optional) |
environment | object | Target platform configuration |
environment.platform | string | Data platform identifier (e.g., snowflake, databricks) |
domains | array | List of domain definitions |
Domain Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the domain |
label | string | Display name (optional) |
description | string | Domain description (optional) |
entities | array | List of entity definitions |
Entity Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the entity |
label | string | Display name (optional) |
description | string | Entity description (optional) |
table_name | string | Underlying database table (optional, mutually exclusive with query) |
query | string | Underlying SQL query (optional, mutually exclusive with table_name) |
dimensions | array | List of dimension definitions |
measures | array | List of measure definitions |
relationships | array | List of relationship definitions |
Dimension Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the dimension |
label | string | Display name (optional) |
data_type | string | Semantic data type: TEXT, NUMBER, INTEGER, FLOAT, DECIMAL, DATE, DATETIME, BOOLEAN |
hidden | boolean | Whether the dimension is hidden from queries |
expr | string | SQL expression for this dimension |
Measure Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the measure |
label | string | Display name (optional) |
data_type | string | Semantic data type: NUMBER, INTEGER, FLOAT, DECIMAL |
hidden | boolean | Whether the measure is hidden from queries |
expr | string | SQL expression for this measure (the column or expression to aggregate) |
aggregation_type | string | Aggregation function: SUM, COUNT, COUNT_DISTINCT, AVG, MIN, MAX, MEDIAN, CUSTOM |
Relationship Object:
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier for the relationship |
join_type | string | Cardinality type: ONE_TO_ONE, ONE_TO_MANY, MANY_TO_ONE |
to_entity | string | Target entity name |
expr | string | SQL join condition expression |
Errors:
| Condition | Status | Detail |
|---|---|---|
| Connection not found | 400 | Connection not found |
| No semantic model configured | 400 | No semantic model found for connection |
| No permission for connection | 403 | You 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:
| Field | Required | Description |
|---|---|---|
connection_name | No | Name of the database connection |
query | Yes | Semantic query specification |
query.dimensions | Conditional | Array of dimension field references. At least one of dimensions or measures is required |
query.measures | Conditional | Array of measure field references. At least one of dimensions or measures is required |
query.filters | No | Array of filter conditions |
query.order_by | No | Array of ordering specifications |
query.limit | No | Pagination specification |
Field Reference Object:
| Field | Required | Description |
|---|---|---|
entity | Yes | Entity name containing the field |
field | Yes | Dimension or measure name |
alias | No | Column alias in the generated SQL |
Order By Object:
| Field | Required | Description |
|---|---|---|
field | Yes | Field reference object |
desc | No | Sort descending (default: false) |
Limit Object:
| Field | Required | Description |
|---|---|---|
fetch | Yes | Number of rows to return (must be greater than 0) |
skip | No | Number 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.
| Type | Description | Additional Fields |
|---|---|---|
CONDITION | A single field condition | field (field reference), operator (operator object) |
AND | Logical AND of multiple filters | filters (array of filter objects) |
OR | Logical OR of multiple filters | filters (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):
| Type | Description | Example Values |
|---|---|---|
EQUALS | Field equals one of the values | ["active", "pending"] |
NOT_EQUALS | Field does not equal any of the values | ["cancelled"] |
CONTAINS | Field contains one of the substrings | ["corp"] |
NOT_CONTAINS | Field does not contain the substring | ["test"] |
STARTS_WITH | Field starts with one of the prefixes | ["US-"] |
NOT_STARTS_WITH | Field does not start with the prefix | ["TEMP-"] |
ENDS_WITH | Field ends with one of the suffixes | [".com"] |
NOT_ENDS_WITH | Field does not end with the suffix | [".test"] |
Comparison operators (use value string):
| Type | Description | Example Value |
|---|---|---|
GREATER_THAN | Field is greater than the value | "1000" |
GREATER_THAN_OR_EQUAL | Field is greater than or equal to the value | "0" |
LESS_THAN | Field is less than the value | "500" |
LESS_THAN_OR_EQUAL | Field is less than or equal to the value | "100" |
Date range operators (use values array with two date strings):
| Type | Description | Example Values |
|---|---|---|
IN_DATE_RANGE | Field is within the date range (inclusive) | ["2024-01-01", "2024-12-31"] |
NOT_IN_DATE_RANGE | Field is outside the date range | ["2024-01-01", "2024-03-31"] |
Date comparison operators (use value string):
| Type | Description | Example Value |
|---|---|---|
BEFORE_DATE | Field is before the date | "2024-01-01" |
AFTER_DATE | Field is after the date | "2024-12-31" |
BEFORE_OR_ON_DATE | Field is before or on the date | "2024-06-30" |
AFTER_OR_ON_DATE | Field is on or after the date | "2024-01-01" |
Null check operators (no value field):
| Type | Description |
|---|---|
IS_NULL | Field is null |
IS_NOT_NULL | Field 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:
| Field | Type | Description |
|---|---|---|
sql | string | Generated SQL query for the target platform |
Errors:
| Condition | Status | Detail |
|---|---|---|
| Missing dimensions and measures | 400 | At least one dimension or measure is required |
| Invalid entity name | 400 | Entity not found: [entity_name] |
| Invalid field name | 400 | Field not found: [field_name] in entity [entity_name] |
| Invalid operator type | 400 | Invalid operator type: [type] |
| Invalid limit values | 400 | Fetch must be greater than 0 |
| No semantic model configured | 400 | No semantic model found for connection |
| No permission for connection | 403 | You don't have permission to access this connection |
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:
- Documentation: docs.coginiti.co
- Support: support.coginiti.co
- Community: Ask questions on Stack Overflow