Scan API¶
This page provides payload examples for running, scheduling, and checking the status of scan operations. Replace the placeholder values with data specific to your setup.
All endpoints use the base URL of your Qualytics deployment (e.g., https://your-instance.qualytics.io/api).
Interactive API reference
For the full, interactive API reference (request schemas, response examples, and an in-browser request runner), visit the API docs.
Running a Scan operation¶
To run a scan operation, use the API payload example below and replace the placeholder values with your specific values.
Endpoint (Post)¶
Endpoint: POST /api/operations/run
Remediation is not set per scan. It follows the datastore's enrichment_remediation_strategy setting.
- container_names:
[]means that it will scan all containers. - max_records_analyzed_per_partition:
nullmeans that it will scan all records of all containers. - auto_resolve_passed_anomalies:
true(default for Full scans) automatically resolves previously open anomalies when the same checks no longer detect the issue. Forced tofalseserver-side whenincrementalistrue.
- container_names:
["table_name_1", "table_name_2"]means that it will scan only the tables table_name_1 and table_name_2. - max_records_analyzed_per_partition:
1000000means that it will scan a maximum of 1 million records per partition.
- check_ids:
[101, 102]means that only those two checks are asserted, and only the containers that own at least one of them are scanned. It combines withcheck_categories,container_names, andcontainer_tags: a check has to pass every selection to be asserted. - Omit
check_ids, or sendnull, to assert every active check. An empty list is rejected with a422, and so is an id that is not an active check in the selected categories on a targeted container or field.
About enrichment_source_record_limit
Caps how many source records are kept per anomaly: the rows written to the _source_records enrichment output and returned by the anomaly source-record endpoints. It defaults to 10 and accepts values from 1 to 1,000,000,000. Set it on the datastore to change the default for every scan, or on a scan run or schedule to override it for that run. A change applies to scans that start after it; earlier anomalies keep the sample they were written with. The limit does not affect anomalous_records_count on the anomaly, which holds the full count whenever the platform can compute it (it is null for shape anomalies whose affected rows cannot be counted). Raising it on wide tables increases enrichment storage and scan time.
About persist_source_records_audit
When true, the scan writes every record it loads, for each container it loads, into a new enrichment output named <enrichment_prefix>_source_records_audit_<container_id>_<operation_id>, with the source container's columns. It defaults to false and requires the datastore to have an enrichment destination. The snapshot is not capped by enrichment_source_record_limit or other anomaly limits: it holds exactly what the scan loaded, so incremental, filters, and max_records_analyzed_per_partition still shape it. Values are stored unmasked, including fields masked on the source container, so restrict access to the enrichment destination before enabling it. The field is also accepted on scheduled scans (POST /api/operations/schedule) and on Flow scan actions. See Persist Source Records for Audit.
Scheduling a Scan operation of all containers¶
To schedule a scan operation, use the API payload example below and replace the placeholder values with your specific values.
Endpoint (Post)¶
Endpoint: POST /api/operations/schedule
This payload is to run a scheduled scan operation every day at 00:00
About check_ids on a schedule
A schedule accepts check_ids the same way a run does, and keeps the picked checks from one run to the next. A pick that is later archived, set to draft, or deleted is dropped from its runs. When no pick is left, the scheduled run fails and asks for the selection to be updated, and updating the schedule is refused until it picks at least one active check again, or leaves check_ids out to assert every active check.
Retrieving Scan operation information¶
Endpoint (Get)¶
Endpoint: GET /api/operations/{id}
The status object includes auto_resolved_anomaly_count, the number of previously open anomalies this scan automatically resolved (always 0 for Incremental scans and for Full scans that ran with auto_resolve_passed_anomalies set to false).
check_ids lists the checks the scan asserted, or null when it asserted every active check. For a scan whose tables were chosen by tag, it also includes the checks the tags selected through tagged fields and tagged checks. requested_check_ids lists only the checks the request picked, or null when it picked none.
{
"id": 12345,
"created": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"type": "scan",
"start_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"end_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"result": "success",
"message": null,
"triggered_by": "user@example.com",
"datastore": {
"id": 101,
"name": "Datastore-Sample",
"store_type": "jdbc",
"type": "db_type",
"enrichment_only": false,
"enrichment_prefix": "data_prefix",
"favorite": false
},
"schedule": null,
"incremental": false,
"auto_resolve_passed_anomalies": true,
"remediation": "none",
"max_records_analyzed_per_partition": -1,
"greater_than_time": null,
"greater_than_batch": null,
"high_count_rollup_threshold": 10,
"enrichment_source_record_limit": 10,
"check_categories": ["data_integrity"],
"check_ids": [101, 102],
"requested_check_ids": [101, 102],
"status": {
"total_containers": 2,
"containers_analyzed": 2,
"partitions_scanned": 2,
"records_processed": 28,
"anomalies_identified": 2,
"auto_resolved_anomaly_count": 1
},
"containers": [
{
"id": 234,
"name": "Container1",
"container_type": "table",
"table_type": "table"
},
{
"id": 235,
"name": "Container2",
"container_type": "table",
"table_type": "table"
}
],
"container_scans": [
{
"id": 456,
"created": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"container": {
"id": 235,
"name": "Container2",
"container_type": "table",
"table_type": "table"
},
"start_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"end_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"records_processed": 8,
"anomaly_count": 1,
"result": "success",
"message": null
},
{
"id": 457,
"created": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"container": {
"id": 234,
"name": "Container1",
"container_type": "table",
"table_type": "table"
},
"start_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"end_time": "YYYY-MM-DDTHH:MM:SS.ssssssZ",
"records_processed": 20,
"anomaly_count": 1,
"result": "success",
"message": null
}
],
"tags": []
}
Container Scans¶
A scan operation runs one container scan per container, and each container scan runs one partition scan per partition. Container scans are the unit that links a scan to its anomalies, its check results, and its source records. Every timestamp is UTC, ISO 8601 with a trailing Z.
List container scans¶
Endpoint: GET /api/container-scans
Permission: Member role. Results are scoped to the datastores the caller can see.
Query parameters (all optional):
| Parameter | Type | Description |
|---|---|---|
datastore, container, operation |
int[] |
Scope to specific datastores, containers, or scan operations. |
name, search |
string |
Match on the container name (search also matches the ID). |
result |
string[] |
Filter by result (success, failure, aborted, queued, running). |
latest_only |
bool |
Return only the latest container scan of each container. |
created_date, offset |
date, int |
Return container scans created on one day; offset is your timezone offset from UTC in minutes. |
sort_created, sort_active_anomalies |
"asc" \| "desc" |
Sort options. |
page, size |
int |
Pagination. |
Get a container scan¶
Endpoint: GET /api/container-scans/{id}
Permission: Member role plus the Reporter team permission on the datastore.
Example response (abbreviated)
{
"id": 456,
"created": "2026-06-01T02:00:00.117Z",
"operation_id": 12345,
"datastore": { "id": 101, "name": "Sales" },
"container": { "id": 235, "name": "orders", "container_type": "table" },
"start_time": "2026-06-01T02:00:00.117Z",
"end_time": "2026-06-01T02:03:41.902Z",
"records_processed": 121500,
"anomaly_count": 37,
"result": "success",
"message": null,
"partition_scans": [
{
"id": 9017,
"partition": { "id": 300, "name": "orders" },
"records_processed": 121500,
"anomaly_count": 37,
"open_anomaly_count": 37,
"archived_anomaly_count": 0,
"result": "success",
"message": null
}
]
}
start_time is the value to pass as version_at to GET /api/quality-checks/{id}/version to see the check definitions this scan evaluated.
List anomalies of a container scan¶
Endpoint: GET /api/container-scans/{id}/anomalies
Permission: Member role.
Returns the anomalies the container scan produced, paginated, in the same shape as GET /api/anomalies. The list endpoint offers the same scoping with more filters through GET /api/anomalies?container_scan={id}; see the Anomalies API.
Get source records of a container scan¶
Returns the source records of every anomaly the container scan produced that has source records, deduplicated when several anomalies share a record. Two formats are available.
Endpoint (JSON): GET /api/container-scans/{id}/source-records
Endpoint (CSV): GET /api/container-scans/{id}/source-records/download
Permission: Member role plus the Viewer team permission on the datastore; include_masked=true needs the Editor team permission.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
limit |
int (≥1) |
Maximum number of rows returned. Defaults to 10 on the JSON endpoint; omit on the CSV endpoint to download every recorded row. |
include_masked |
bool (default false) |
When true, returns raw values for masked fields. Requires the Editor team permission, and the platform writes an audit-log entry naming the masked fields you accessed. |
Example request
The rows are the same ones served per anomaly by GET /api/anomalies/{id}/source-record, so the number kept per anomaly is capped by enrichment_source_record_limit (see Running a Scan operation).
List Check Results for a Scan¶
Two endpoints return every check that a scan reported, with each check's result and the number of anomalies it produced in that scan. Use them to answer "which checks passed, which failed, and which could not be evaluated in scan X". Both are paginated (page, size).
Endpoint (whole scan): GET /api/operations/{id}/scan-checks
Endpoint (one container scan): GET /api/container-scans/{id}/checks
Query parameters:
| Parameter | Applies to | Type | Description |
|---|---|---|---|
search |
both | string |
Substring match on the check description or ID. |
result |
container scan | "passed" \| "failed" \| "unasserted" |
Return only checks with that scan_result. |
sort_anomalies |
container scan | "desc" \| "asc" (default desc) |
Sort by anomaly count. |
Permission: Member role, plus the Reporter team permission on the datastore for the operation endpoint and the Viewer team permission for the container-scan endpoint. The operation endpoint is only available for scan operations.
Example request
Example response (abbreviated)
{
"items": [
{
"id": 778,
"description": "Orders must reference a customer",
"rule_type": "notNull",
"status": "Active",
"scan_anomaly_count": 37,
"scan_result": "failed",
"container_scan_anomaly_count": 37,
"container": { "id": 235, "name": "orders" },
"container_scan_id": 456,
"fields": [{ "id": 3101, "name": "customer_id" }]
}
],
"total": 1,
"page": 1,
"size": 50,
"pages": 1
}
scan_result is the check's result in this scan:
| Value | Meaning |
|---|---|
failed |
The check produced at least one anomaly. |
unasserted |
The scan could not evaluate the check in any partition (for example, a filter with invalid syntax or a field that no longer exists). It produced no anomalies, but it did not pass. |
passed |
Neither of the above. A check counts as evaluated when any partition of the container scan passed or failed it. |
Read scan_result rather than treating a scan_anomaly_count of 0 as passed: an unasserted check also has a count of 0.
scan_anomaly_count is the number of distinct anomalies this check produced in the scan. container_scan_anomaly_count is the distinct anomaly count for the whole container scan across all checks. Do not sum scan_anomaly_count across checks to get it, because one anomaly can fail several checks.
For per-container totals, the container_scan object returned by GET /api/operations/{id}/containers carries passed_checks_count, failed_checks_count and unasserted_checks_count, which follow the same rules. Unasserted checks are counted in neither of the other two.
To see what a check looked like when the scan ran, pass the scan's start_time to GET /api/quality-checks/{id}/version.