Datastore API
You can manage datastores programmatically using the Qualytics API. Source and enrichment datastores use the same endpoints. This page covers creating, reading, updating, and deleting datastores, as well as multi-schema bulk creation.
Complete API Reference
For the full interactive API documentation with all request/response schemas, visit the API docs.
All endpoints use the base URL of your Qualytics deployment (e.g., https://your-instance.qualytics.io/api).
Create a Datastore
Creates a single datastore with either a new or existing connection. By default, it creates a source datastore. Set enrichment_only: true to create an enrichment datastore on a supported connector.
Endpoint: POST /api/datastores
Permission: Manager
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/datastores" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Sales",
"connection": {
"name": "postgres_production",
"type": "postgresql",
"host": "db.acme-corp.com",
"port": 5432,
"username": "qualytics_reader",
"password": "s3cur3_p4ssw0rd",
"parameters": {}
},
"database": "production",
"schema": "sales",
"teams": ["Data Platform", "Analytics"],
"tags": ["production", "postgresql"],
"trigger_sync": true
}'
Response (200 OK):
{
"id": 42,
"name": "Production Sales",
"type": "postgresql",
"store_type": "jdbc",
"database": "production",
"schema": "sales",
"connected": true,
"favorite": false,
"enrichment_only": false,
"created": "2026-03-31T14:30:00.000000Z",
"teams": [
{ "name": "Data Platform" },
{ "name": "Analytics" }
],
"global_tags": [
{ "name": "production" },
{ "name": "postgresql" }
]
}
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/datastores" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Finance",
"connection_id": 7,
"database": "production",
"schema": "finance",
"teams": ["Finance Team"],
"trigger_sync": true
}'
Response (200 OK):
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/datastores" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Processed Events S3",
"connection_id": 12,
"root_path": "/processed/events/2026/",
"teams": ["Data Engineering"],
"trigger_sync": true
}'
Response (200 OK):
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/datastores" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Raw Events S3",
"connection": {
"name": "s3_data_lake",
"type": "s3",
"uri": "s3://acme-data-lake",
"access_key": "AKIAIOSFODNN7EXAMPLE",
"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLE"
},
"root_path": "/raw/events/2026/",
"teams": ["Data Engineering"],
"trigger_sync": true
}'
Response (200 OK):
Create Request Fields
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | Yes | Display name for the datastore (max 255 characters). | |
connection |
object | Conditional | Connection details for a new connection. Required if connection_id is not provided. |
|
connection_id |
integer | Conditional | ID of an existing connection. Required if connection is not provided. |
|
database |
string | Conditional | Database name (JDBC only). The key must be present for most JDBC connectors; omitting it, or sending null, is rejected. PostgreSQL accepts an empty value and then uses the postgres database. |
|
schema |
string | Conditional | Schema name (JDBC and Native only). The key must be present for connectors that use a schema. An empty value falls back to the connector's default, such as public on PostgreSQL or dbo on SQL Server. |
|
root_path |
string | Conditional | Base directory path (DFS only, required for DFS). | |
folder_globbing_enabled |
boolean | No | false |
Enable folder-level globbing for DFS datastores (DFS only). |
description |
string | No | null |
Description for the datastore (max 8000 characters). |
teams |
list[string] |
No | null |
Team names to assign. |
tags |
list[string] |
No | null |
Tag names to assign. |
group_id |
integer | No | null |
Existing datastore group ID to assign. |
group |
object | No | null |
Inline group to create and assign (mutually exclusive with group_id). |
trigger_sync |
boolean | No | null |
Whether to trigger a sync operation after creation. |
enrichment_only |
boolean | No | false |
Whether to create an enrichment datastore, which another datastore can link as its destination. |
enrichment_prefix |
string | No | Auto-generated | Prefix for enrichment outputs (max 60 characters). |
enrichment_source_record_limit |
integer | No | Platform Default (shipped value 10) |
Max source records per anomaly (1–1,000,000,000). Omit or send null to inherit. |
enrichment_remediation_strategy |
string | No | "none" |
Whether Scans write a snapshot of anomalous source records: "none", "append", or "overwrite". A datastore is created without a destination link, so leave it at "none" and change it after linking. |
high_count_rollup_threshold |
integer | No | Platform Default (shipped value 10) |
Max anomalies per check before rolling up (1–1,000). Omit or send null to inherit. |
enrichment_auto_sync |
boolean | No | true |
Whether Qualytics syncs and profiles the enrichment destination after this datastore writes to it. |
To link the new datastore to a destination, use Link an Enrichment Destination after creation. enrichment_datastore_id is available in the bulk creation request, not in POST /api/datastores.
Get a Datastore
Retrieves a single datastore by its ID.
Endpoint: GET /api/datastores/{id}
Permission: Member
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/datastores/42" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
{
"id": 42,
"name": "Production Sales",
"type": "postgresql",
"store_type": "jdbc",
"database": "production",
"schema": "sales",
"connected": true,
"favorite": false,
"enrichment_only": false,
"description": null,
"created": "2026-03-31T14:30:00.000000Z",
"updated": "2026-03-31T14:30:00.000000Z",
"connection": {
"id": 7,
"name": "postgres_production",
"type": "postgresql"
},
"teams": [
{ "name": "Data Platform" },
{ "name": "Analytics" }
],
"global_tags": [
{ "name": "production" },
{ "name": "postgresql" }
],
"metrics": {
"containers": 12,
"records": 1850000,
"fields_profiled": 87,
"active_checks": 145,
"active_anomalies": 3
}
}
List Datastores
Retrieves a paginated list of datastores with optional filtering and sorting.
Endpoint: GET /api/datastores
Permission: Member
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/datastores?tag=production&sort_name=asc&limit=10" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
Query Parameters
| Parameter | Type | Description |
|---|---|---|
id |
list[int] |
Filter by datastore ID(s). |
name |
string | Filter by exact name. |
search |
string | Search by partial name (case-insensitive) or exact ID. |
datastore_type |
list[string] |
Filter by connection type (e.g., postgresql, snowflake, s3). |
tag |
list[string] |
Filter by tag names. |
group |
list[int] |
Filter by datastore group ID(s). |
enrichment_only |
boolean | true returns enrichment datastores; false returns source datastores. Omitting this parameter returns source datastores. |
sort_name |
string | Sort by name (asc or desc). |
sort_created |
string | Sort by created date (asc or desc). |
sort_favorite |
string | Sort by favorite flag (asc or desc). Default: desc. |
sort_containers |
string | Sort by container count (asc or desc). |
sort_active_anomalies |
string | Sort by active anomalies count (asc or desc). |
The datastore tree uses GET /api/datastores/listing?role=all to show source and enrichment datastores. On that endpoint, role=source and role=enrichment correspond to the other tree filters. The role parameter is not supported by GET /api/datastores.
Update a Datastore
Updates an existing datastore's properties, tags, or teams.
Endpoint: PUT /api/datastores/{id}
Permission: Editor
The minimum is the Member user role with the Editor team permission on the datastore. Changing its team assignments additionally requires Manager.
Include name, connection_id, enrichment_only, enrichment_prefix, and the connector's location fields. Preserve the current enrichment_source_record_limit, high_count_rollup_threshold, and enrichment_remediation_strategy explicitly; omitting these resets them to their update defaults (10, 10, and none). Read the datastore before constructing an update.
Example request and response
Request:
curl -X PUT "https://your-instance.qualytics.io/api/datastores/42" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Sales (Updated)",
"description": "Core sales data from the production PostgreSQL database",
"connection_id": 7,
"database": "production",
"schema": "sales",
"enrichment_only": false,
"enrichment_prefix": "_production_sales",
"enrichment_source_record_limit": 10,
"high_count_rollup_threshold": 10,
"enrichment_remediation_strategy": "none",
"tags": ["production", "postgresql", "sales"],
"teams": ["Data Platform", "Analytics", "Sales Ops"]
}'
Response:
{
"id": 42,
"name": "Production Sales (Updated)",
"description": "Core sales data from the production PostgreSQL database",
"type": "postgresql",
"store_type": "jdbc",
"database": "production",
"schema": "sales",
"global_tags": [
{ "name": "production" },
{ "name": "postgresql" },
{ "name": "sales" }
],
"teams": [
{ "name": "Data Platform" },
{ "name": "Analytics" },
{ "name": "Sales Ops" }
]
}
Note
Updating tags or teams replaces the entire list. To add a tag without removing existing ones, include all current tags plus the new one.
Delete a Datastore
Permanently deletes a datastore and all its associated containers, checks, and anomalies.
Endpoint: DELETE /api/datastores/{id}
Permission: Admin
Example request
curl -X DELETE "https://your-instance.qualytics.io/api/datastores/42" \
-H "Authorization: Bearer YOUR_TOKEN"
Response: 204 No Content
Warning
Deletion permanently removes the datastore's containers, checks, anomalies, and operation history from Qualytics. It does not delete your source data or physical data already written to an enrichment destination. Deleting a destination also removes incoming links and resets linked datastores' remediation strategies to none. See Delete Datastore for blocking dependencies.
Toggle Favorite
Marks or unmarks a datastore as a favorite.
Endpoint: PATCH /api/datastores/{id}/favorite
Permission: Member
Example request and response
Request:
curl -X PATCH "https://your-instance.qualytics.io/api/datastores/42/favorite" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "favorite": true }'
Response: Returns the updated datastore object with "favorite": true.
Assign Group
Assigns a datastore to a datastore group.
Endpoint: PATCH /api/datastores/{id}/assign-group
Permission: Editor
Example request and response
Request:
curl -X PATCH "https://your-instance.qualytics.io/api/datastores/42/assign-group" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "group_id": 5 }'
Response: Returns the updated datastore object with the group assigned.
To unassign, pass null:
curl -X PATCH "https://your-instance.qualytics.io/api/datastores/42/assign-group" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "group_id": null }'
Link an Enrichment Destination
Links a datastore to an enrichment destination, the datastore its scan results, remediation snapshots, materialized copies, and exports are written into. Calling it with a different destination replaces the current one; no unlink is needed.
Endpoint: PATCH /api/datastores/{datastore_id}/enrichment/{enrichment_id}
Permission: Member role with the Editor team permission on the datastore
Example request
Link datastore ID 110 to the destination with ID 42:
curl -X PATCH "https://your-instance.qualytics.io/api/datastores/110/enrichment/42" \
-H "Authorization: Bearer YOUR_TOKEN"
Response: Returns the updated datastore object with the destination linked.
For the enrichment settings and the destination-side endpoints, see the Enrichment API.
Unlink an Enrichment Destination
Removes the link from a datastore.
Endpoint: DELETE /api/datastores/{datastore_id}/enrichment
Permission: Admin
Example request
curl -X DELETE "https://your-instance.qualytics.io/api/datastores/110/enrichment" \
-H "Authorization: Bearer YOUR_TOKEN"
Response: 204 No Content
Warning
Unlinking requires the Admin role, which is a higher permission than linking, and is refused while the datastore has Export or Materialize operations in flows or on a schedule.
Test Connection
Validates connectivity for a datastore without persisting any data.
Endpoint: POST /api/datastores/connection
Permission: Manager
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/datastores/connection" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Test PostgreSQL",
"connection": {
"name": "pg_test",
"type": "postgresql",
"host": "db.acme-corp.com",
"port": 5432,
"username": "qualytics_reader",
"password": "s3cur3_p4ssw0rd"
},
"database": "production",
"schema": "public"
}'
Response (204 No Content): Connection verified successfully.
Error Response (400 Bad Request):
Multi-Schema Creation
Use these endpoints to programmatically discover catalogs and schemas, validate connectivity, and bulk-create multiple datastores from a single connection.
Discover Catalogs
Retrieves the list of available catalogs (databases/projects) from a connection.
Endpoint: POST /api/connections/catalogs
Permission: Manager
Send the full connection details in the request body.
Response: list[string] (a list of catalog names).
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/connections/catalogs" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "snowflake_production",
"type": "snowflake",
"host": "acme.snowflakecomputing.com",
"username": "qualytics_user",
"password": "your_password",
"parameters": {
"role": "qualytics_read_role",
"warehouse": "qualytics_wh"
}
}'
Response:
Endpoint: GET /api/connections/{connection_id}/catalogs
Permission: Manager
Response: list[DiscoveredCatalog] (catalogs annotated with existing datastores).
Note
Only JDBC and Native connectors support catalog discovery. Calling this endpoint for a DFS connection returns 409 Conflict.
Discover Schemas
Retrieves the list of available schemas within a catalog.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
catalog |
string | No | Catalog/database name to filter schemas by |
Endpoint: POST /api/connections/schemas
Permission: Manager
Send the full connection details in the request body.
Response: list[string] (a list of schema names).
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/connections/schemas?catalog=production_db" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "snowflake_production",
"type": "snowflake",
"host": "acme.snowflakecomputing.com",
"username": "qualytics_user",
"password": "your_password",
"parameters": {
"role": "qualytics_read_role",
"warehouse": "qualytics_wh"
}
}'
Response:
Endpoint: GET /api/connections/{connection_id}/schemas
Permission: Manager
Response: list[DiscoveredSchema] (schemas annotated with existing datastores).
Note
Only JDBC and Native connectors support schema discovery. Calling this endpoint for a DFS connection returns 409 Conflict.
Validate Schemas
Validates connectivity for one or more schemas before creating datastores.
Endpoint: POST /api/connections/datastores/validate
Permission: Manager
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/connections/datastores/validate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"connection": {
"name": "snowflake_production",
"type": "snowflake",
"host": "acme.snowflakecomputing.com",
"username": "qualytics_user",
"password": "your_password",
"parameters": {
"role": "qualytics_read_role",
"warehouse": "qualytics_wh"
}
},
"database": "production_db",
"schemas": ["public", "sales", "finance"]
}'
Response:
Endpoint: POST /api/connections/{connection_id}/datastores/validate
Permission: Manager
Bulk Create Datastores
Creates multiple source datastores from selected schemas in a single operation.
Endpoint: POST /api/connections/datastores/bulk
Permission: Manager
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/connections/datastores/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"connection": {
"name": "snowflake_production",
"type": "snowflake",
"host": "acme.snowflakecomputing.com",
"username": "qualytics_user",
"password": "your_password",
"parameters": {
"role": "qualytics_read_role",
"warehouse": "qualytics_wh"
}
},
"database": "production_db",
"schemas": ["public", "sales", "finance"],
"name_template": "prod_{{schema}}",
"description": "Production Snowflake datastores",
"teams": ["Data Platform"],
"trigger_sync": true,
"enrichment_datastore_id": 42,
"enrichment_source_record_limit": 100,
"enrichment_remediation_strategy": "append",
"high_count_rollup_threshold": 10,
"group_id": 5,
"tags": ["production", "snowflake"]
}'
Response:
Endpoint: POST /api/connections/{connection_id}/datastores/bulk
Permission: Manager
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/connections/7/datastores/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"database": "production_db",
"schemas": ["public", "sales"],
"name_template": "prod_{{schema}}",
"teams": ["Data Platform", "Analytics"],
"trigger_sync": true
}'
Response:
Example response with errors
When some schemas fail, the response includes both successes and errors:
Note
The bulk creation is non-atomic. Schemas that succeed are created even if other schemas fail, and each failure is listed in errors. To retry after a request that created a new connection, send the failed schemas to POST /api/connections/{connection_id}/datastores/bulk with the connection_id from the response. When no datastore was created, connection_id is null and the new connection was not saved either, so send the whole request again.
Each entry in errors names the schema (schema_name) or root path (path) and the message (error). When Qualytics can classify the failure, the entry also carries a fault_context with a title and a hint. A datastore whose link to the enrichment destination is refused, for example because its prefix is already used there (Prefix already used on this destination), is removed rather than left unlinked, and reported in errors.
Bulk Create Request Fields
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
object | Yes (new only) | Connection details to create before bulk creation. | |
schemas |
list[string] |
Conditional | List of schema names to create JDBC/Native datastores for. Required if root_paths is not provided. |
|
root_paths |
list[string] |
Conditional | List of root paths to create DFS datastores for. Required if schemas is not provided. Mutually exclusive with schemas. |
|
database |
string | No | null |
Database/catalog name (for connectors with catalog hierarchy). |
name_template |
string | No | {connection_name}_{{schema}} |
Naming pattern with {{schema}} placeholder. |
description |
string | No | null |
Description applied to all created datastores. |
enrichment_only |
boolean | No | false |
Whether to create an enrichment datastore, which another datastore can link as its destination. |
enrichment_datastore_id |
integer | No | null |
Existing enrichment destination ID to link. |
enrichment_prefix |
string | No | null |
Prefix shared by the created datastores. With more than one schema or root path, each gets its schema or root path appended in lowercase, with slashes and other characters turned into underscores: _prod becomes _prod_sales for the schema sales, and _prod_landing_orders for the root path /landing/orders. The shared part is shortened so the result fits in 60 characters, and a derived prefix that repeats one already used in the request, including one given in enrichment_prefixes, is numbered _2, _3. Omit it to generate each from the datastore name. |
enrichment_prefixes |
object | No | null |
A prefix per schema or root path, keyed as listed in schemas or root_paths (for example, {"sales": "_sales_eu"}). Overrides enrichment_prefix for those. Values are used as sent and never numbered: when the datastores are linked to an enrichment destination, two identical values make the second link refused and that datastore listed in errors. A key that is not in schemas or root_paths is rejected with a 422. |
enrichment_source_record_limit |
integer | No | Platform Default (shipped value 10) |
Max source records per anomaly (1–1,000,000,000). Omit or send null to inherit. |
enrichment_remediation_strategy |
string | No | "none" |
Replication strategy: "none", "append", or "overwrite". |
high_count_rollup_threshold |
integer | No | Platform Default (shipped value 10) |
Max anomalies per check before rolling up (1–1,000). Omit or send null to inherit. |
enrichment_auto_sync |
boolean | No | true |
Whether Qualytics syncs and profiles the enrichment destination after this datastore writes to it. |
group_id |
integer | No | null |
Existing datastore group ID to assign. |
group |
object | No | null |
Inline group to create and assign (mutually exclusive with group_id). |
tags |
list[string] |
No | null |
Tags to apply to all created datastores. |
teams |
list[string] |
No | null |
Teams to assign to all created datastores. |
trigger_sync |
boolean | No | null |
Whether to trigger a sync operation after each creation. |
Warning
group_id and group are mutually exclusive. Provide only one.
Note
schemas and root_paths are mutually exclusive. Provide one or the other. Use schemas for JDBC/Native connectors and root_paths for DFS connectors.
Practical Guides
End-to-End: Onboard All Schemas from a Snowflake Database
Complete workflow: Snowflake with new connection
1. Discover what databases are available:
curl -X POST "https://your-instance.qualytics.io/api/connections/catalogs" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "snowflake_prod",
"type": "snowflake",
"host": "acme.snowflakecomputing.com",
"username": "qualytics_user",
"password": "your_password",
"parameters": {
"role": "qualytics_read_role",
"warehouse": "qualytics_wh"
}
}'
Response: ["production_db", "staging_db", "analytics_db"]
2. Discover schemas in your target database:
curl -X POST "https://your-instance.qualytics.io/api/connections/schemas?catalog=production_db" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... same connection payload ... }'
Response: ["public", "sales", "finance", "hr", "marketing"]
3. Validate the schemas you want to onboard:
curl -X POST "https://your-instance.qualytics.io/api/connections/datastores/validate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"connection": { ... same connection payload ... },
"database": "production_db",
"schemas": ["public", "sales", "finance", "hr", "marketing"]
}'
4. Bulk create all datastores:
curl -X POST "https://your-instance.qualytics.io/api/connections/datastores/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"connection": { ... same connection payload ... },
"database": "production_db",
"schemas": ["public", "sales", "finance", "hr", "marketing"],
"name_template": "prod_snowflake_{{schema}}",
"teams": ["Data Platform"],
"trigger_sync": true,
"enrichment_datastore_id": 42,
"enrichment_source_record_limit": 100,
"enrichment_remediation_strategy": "append",
"tags": ["production", "snowflake"]
}'
Response: { "created": [101, 102, 103, 104, 105], "errors": [], "connection_id": 12 }
End-to-End: Onboard PostgreSQL Schemas Using an Existing Connection
Complete workflow: PostgreSQL with existing connection
1. Discover schemas:
curl -X GET "https://your-instance.qualytics.io/api/connections/7/schemas?catalog=analytics_db" \
-H "Authorization: Bearer YOUR_TOKEN"
2. Bulk create:
curl -X POST "https://your-instance.qualytics.io/api/connections/7/datastores/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"database": "analytics_db",
"schemas": ["raw", "staging", "curated"],
"name_template": "analytics_{{schema}}",
"teams": ["Analytics"],
"trigger_sync": true
}'
3. Link enrichment after creation:
Automation Script
Python automation script
import requests
BASE_URL = "https://your-instance.qualytics.io/api"
TOKEN = "YOUR_TOKEN"
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
CONNECTION_ID = 7
DATABASE = "production_db"
NAME_TEMPLATE = "prod_{{schema}}"
TEAMS = ["Data Platform"]
ENRICHMENT_ID = 42
# Step 1: Discover available schemas
resp = requests.get(
f"{BASE_URL}/connections/{CONNECTION_ID}/schemas",
headers=HEADERS,
params={"catalog": DATABASE},
)
resp.raise_for_status()
discovered = resp.json()
# Filter out schemas that already have datastores
new_schemas = [
s["name"] for s in discovered
if len(s["existing_datastores"]) == 0
]
print(f"Found {len(new_schemas)} new schemas: {new_schemas}")
if not new_schemas:
print("No new schemas to onboard.")
exit()
# Step 2: Validate connectivity
resp = requests.post(
f"{BASE_URL}/connections/{CONNECTION_ID}/datastores/validate",
headers=HEADERS,
json={
"database": DATABASE,
"schemas": new_schemas,
},
)
resp.raise_for_status()
validation = resp.json()
failed = [r for r in validation["results"] if r["status"] == "failure"]
if failed:
print(f"Warning: {len(failed)} schemas failed validation:")
for f in failed:
print(f" - {f['schema_name']}: {f.get('message', 'Unknown error')}")
valid_schemas = [
r["schema_name"] for r in validation["results"]
if r["status"] == "success"
]
print(f"Proceeding with {len(valid_schemas)} valid schemas.")
# Step 3: Bulk create datastores
resp = requests.post(
f"{BASE_URL}/connections/{CONNECTION_ID}/datastores/bulk",
headers=HEADERS,
json={
"database": DATABASE,
"schemas": valid_schemas,
"name_template": NAME_TEMPLATE,
"teams": TEAMS,
"trigger_sync": True,
"enrichment_datastore_id": ENRICHMENT_ID,
},
)
resp.raise_for_status()
result = resp.json()
print(f"Created {len(result['created'])} datastores: {result['created']}")
if result["errors"]:
print(f"Errors: {result['errors']}")
Common Errors
When a request fails, the API returns an HTTP status code and a JSON body with a detail field describing the problem (or, for validation errors, an array of field-level issues). The most common errors across these endpoints are listed below.
| Status | Cause | Example detail |
|---|---|---|
400 Bad Request |
The datastore type is not available on this deployment. | "Datastore type 's3' is not available" |
401 Unauthorized |
The bearer token is missing or invalid. | (response body comes from the auth layer) |
403 Forbidden |
The user does not have the role required by the endpoint (see Permission Summary). | |
404 Not Found |
The connection_id or resource ID does not exist. |
"Connection id: 99 not found" |
409 Conflict |
A datastore with the same name already exists. |
"Datastore 'Orders Lake' already exists with id: 42" |
409 Conflict |
The connector type does not support enrichment when enrichment_only: true is set. |
"Datastore type 'x' doesn't support enrichment" |
422 Unprocessable Entity |
Both connection and connection_id were provided, or neither. |
"Either connection or connection_id must be provided" |
422 Unprocessable Entity |
A field the connector requires is missing from the payload, such as database or schema on a JDBC datastore. |
"Invalid schema provided for datastore of type 'postgresql', required fields: [...]" |
422 Unprocessable Entity |
The request body failed validation (wrong field type, missing required field, etc.). | Array of {loc, msg, type} entries |
Example error body
Example validation error body (422)
{
"detail": [
{
"loc": ["body", "name"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Tip
For the full per-endpoint response schema (including success and error shapes), use the interactive API docs.
Permission Summary
| Operation | Minimum Permission |
|---|---|
| Create a datastore | Manager |
| Get / List datastores | Member |
| Update a datastore | Editor |
| Delete a datastore | Admin |
| Toggle favorite | Member |
| Assign group | Editor |
| Link enrichment | Member |
| Unlink enrichment | Admin |
| Test connection | Manager |
| Discover catalogs / schemas | Manager |
| Validate schemas | Manager |
| Bulk create datastores | Manager |
In this table, Member, Manager, and Admin are platform roles; Editor is a team permission granted on the datastore through its teams.