Anomalies API
This page documents the REST endpoints for working with anomalies in Qualytics. All endpoints share the base URL of your deployment (for example, https://your-instance.qualytics.io/api) and require a Bearer token.
Sections below are grouped by what you are trying to do. The same anomaly object is exchanged across reads and writes: see the Anomaly schema section for the field reference.
Tip
For complete API documentation, including request/response schemas, visit the API docs.
Permissions
All endpoints require the Member role. Most reads need the Viewer team permission on the anomaly's datastore; writes need the Author team permission (or higher), with description edits additionally requiring the Editor team permission. Ticket-link writes need the Manager role plus the Author team permission on the datastore. Per-endpoint requirements are called out below.
Anomaly schema
Every read endpoint that returns an anomaly emits the same shape. The most relevant fields are:
| Field | Type | Notes |
|---|---|---|
id |
int |
Numeric primary identifier. |
uuid |
string (UUID) |
Stable, globally unique identifier. |
type |
"record" \| "shape" |
Anomaly category. See Types. |
status |
string |
One of Active, Acknowledged, Resolved, Duplicate, Invalid, Discarded. See Status. |
weight |
int |
Severity score. |
anomalous_records_count |
int |
Number of records flagged for record anomalies. |
description |
string |
User-editable business description. |
created |
string (ISO 8601) |
When the anomaly was detected. |
fingerprint |
int |
Used to deduplicate recurring anomalies. See Fingerprints. |
global_tags |
Tag[] |
Tags applied to the anomaly. |
assignees |
UserStub[] |
Users assigned to the anomaly. See Assignees. |
failed_checks |
FailedCheck[] |
Checks that produced the anomaly. |
datastore, container, partition_scan |
refs | Source location of the anomaly. |
Example response (abbreviated)
{
"id": 12345,
"uuid": "8e8b9f8b-1234-4abc-9def-012345678901",
"type": "record",
"status": "Active",
"weight": 54,
"anomalous_records_count": 1,
"description": "Customer ID is missing for new orders",
"created": "2026-05-15T17:22:08Z",
"fingerprint": 9182736455,
"global_tags": [{ "id": 7, "name": "High" }],
"assignees": [
{ "id": 42, "name": "Alice Lee", "email": "alice@example.com" }
],
"failed_checks": [{ "id": 778, "rule_type": "notNull", "message": "..." }]
}
List and retrieve
List anomalies
Returns a paginated list of anomalies sorted by creation timestamp and severity (most recent first).
Endpoint: GET /api/anomalies
Permission: Member. Results are scoped to datastores the caller can see.
Common query parameters (all optional, most accept multiple values):
| Parameter | Type | Description |
|---|---|---|
search |
string |
Substring match on the anomaly message or ID. |
status |
AnomalyStatusType[] |
Filter by status (Active, Acknowledged, Resolved, Duplicate, Invalid, Discarded). |
anomaly_type |
"record" \| "shape" |
Filter by anomaly type. |
datastore, container |
int[] |
Scope to specific datastores or containers. |
field, quality_check, rule_type |
varies | Scope to specific fields, checks, or rule types. |
tag |
string[] |
Filter by tag names. |
assignee |
int[] |
Filter by anomaly assignee user IDs. |
archived |
"include" \| "only" |
Include or restrict to archived anomalies. |
timeframe, created_date, start_date, end_date |
varies | Time-based filters. |
related_to_id |
int |
Return anomalies that share a fingerprint with the given anomaly. |
sort_id, sort_created, sort_weight, sort_anomalous_records_count |
"asc" \| "desc" |
Sort options. |
Example request
curl -X GET "https://your-instance.qualytics.io/api/anomalies?status=Active&assignee=42&tag=High&sort_weight=desc" \
-H "Authorization: Bearer YOUR_TOKEN"
Returns active anomalies assigned to user 42 with the tag High, sorted by severity descending.
Get a single anomaly
Endpoint: GET /api/anomalies/{id}
{id} accepts either the numeric ID or the UUID. Pass include_deleted=true to surface anomalies that have been soft-deleted.
Example
Get failed checks for an anomaly
Returns the list of checks that produced the anomaly, with the message text and the rule type for each.
Endpoint: GET /api/anomalies/{id}/failed-checks
Update
Update a single anomaly
Updates the writable fields of an anomaly. Only the fields you include in the body are changed; omitted fields are left untouched.
Endpoint: PUT /api/anomalies/{id}
Permission: Member role + Author team permission on the anomaly's datastore. Description updates additionally require the Editor team permission.
Body fields:
| Field | Type | Description |
|---|---|---|
status |
"Active" \| "Acknowledged" |
Change the open-state of the anomaly. Setting "Acknowledged" on an archived anomaly restores it to the Acknowledged state (direct restore to Active is not allowed). Use the delete endpoint to archive. |
description |
string |
User-editable business description. |
tags |
string[] |
Tag names. Replaces the current set; pass [] to clear. |
assignee_ids |
int[] |
User IDs assigned to the anomaly. Replaces the current set; pass [] to unassign everyone. Each user must have at least the Viewer team permission on the datastore. |
Example request
curl -X PUT "https://your-instance.qualytics.io/api/anomalies/12345" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "Acknowledged",
"description": "Investigating with the data engineering team",
"tags": ["High", "Customer-Impact"],
"assignee_ids": [42, 57]
}'
Bulk update anomalies
Applies the same changes to many anomalies in one call.
Endpoint: PATCH /api/anomalies
Permission: Member role + Author team permission on each anomaly's datastore. Description updates additionally require the Editor team permission on the corresponding datastore.
Body: A list of bulk-update entries, each with the anomaly identifier plus the fields to change. Per-entry fields match the single-update body, with the addition of:
| Field | Type | Description |
|---|---|---|
id |
int or UUID |
The anomaly to update. |
Example
Manage assignees
Assignees are managed via the standard update endpoints. The assignee_ids field is replace-only, so pass the full target set on every write rather than the changes you want to apply.
Endpoints:
PUT /api/anomalies/{id}withassignee_idsin the body for a single anomaly.PATCH /api/anomalieswithassignee_idsper entry for many anomalies.GET /api/anomalies?assignee=<id>to filter the list by assignee.
Behavior:
- Each user passed in
assignee_idsmust have at least the Viewer team permission on the anomaly's datastore. Users without access are rejected. - Pass
assignee_ids: []to unassign everyone. - To add a user to an existing set, read
assigneesfirst and submit the union. The API does not support append. - Auto-assignment from a check's default assignee runs only at anomaly creation and is not reachable through this endpoint. See Deep Dive · Anomaly Assignees · Inheritance.
Set or replace the assignee list
Clear all assignees
Bulk-replace assignees across many anomalies
See Deep Dive · Anomaly Assignees for inheritance rules, notification behavior, and history tracking.
Archive and delete
The single-anomaly endpoint covers both the archive flow (mark with a resolution status and keep for audit) and the hard-delete flow (remove the record entirely). Bulk versions follow the same pattern.
Archive or delete a single anomaly
Endpoint: DELETE /api/anomalies/{id}
Permission: Member role + Author team permission on the anomaly's datastore.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status |
"Resolved" \| "Duplicate" \| "Invalid" \| "Discarded" |
The archived status to apply. Required when archive=true. |
archive |
bool |
true (default) archives the anomaly with the resolution status; false permanently deletes the anomaly. The hard-delete is irreversible. |
Body (optional): Lets you attach a comment and tag teammates when archiving.
| Field | Type | Description |
|---|---|---|
status |
ArchivedAnomalyStatusType |
Same as the query parameter; either may be used. |
comment |
string |
Comment posted to the anomaly's history when archiving. |
mentioned_user_ids |
int[] |
Users to notify via @mention in the comment. |
Example request: archive with a resolution comment
Example request: hard-delete an archived anomaly
Bulk archive or delete
Endpoint: DELETE /api/anomalies
Permission: Member role + Author team permission on each anomaly's datastore.
Body: A list of entries combining the anomaly ID with the same fields used in the single-anomaly delete (status, comment, mentioned_user_ids, archive).
Example
curl -X DELETE "https://your-instance.qualytics.io/api/anomalies" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "id": 12345, "archive": true, "status": "Resolved" },
{ "id": 12346, "archive": true, "status": "Duplicate" },
{ "id": 12347, "archive": false }
]'
History and comments
Get the anomaly history
Returns the paginated change log for an anomaly: status changes, description edits, tag updates, assignee changes, and external ticket links and unlinks. Each entry carries the user who made the change and a timestamp.
Endpoint: GET /api/anomalies/{id}/history
Query parameters:
| Parameter | Type | Description |
|---|---|---|
page |
int (default 1) |
Page number to return, starting at 1. |
size |
int (default 50, max 100) |
Number of entries per page. |
include_deleted |
bool (default false) |
When true, also returns history for soft-deleted anomalies. |
Example response (abbreviated)
{
"items": [
{
"changeset": {
"status": ["Active", "Acknowledged"],
"assignees": [
[{ "id": 42, "name": "Alice Lee" }],
[{ "id": 42, "name": "Alice Lee" }, { "id": 57, "name": "Bob Patel" }]
],
"ticket_link": [
null,
{
"ticket_number": "INC0010001",
"ticket_url": "https://example.service-now.com/incident.do?sys_id=abc123sys",
"integration_type": "servicenow"
}
]
},
"transaction": {
"id": 9876,
"issued_at": "2026-05-15T17:22:08Z",
"user": { "id": 1, "name": "Alice Lee" }
},
"operation": "update"
}
],
"total": 124,
"page": 1,
"size": 50,
"pages": 3
}
Each changeset field inside items[].changeset is a [before, after] tuple, so you can reconstruct the audit trail offline. The transaction object on each item carries the id, the issued_at timestamp, and the user who made the change. The operation field reports whether the entry is an insert, update, or delete. To load successive windows, increment page until it reaches pages.
Get comments
Returns the comments on an anomaly, newest first.
Endpoint: GET /api/anomalies/{id}/comments
Query parameters:
| Parameter | Type | Description |
|---|---|---|
start_date |
datetime (ISO 8601, inclusive) |
Lower bound on created. |
end_date |
datetime (ISO 8601, exclusive) |
Upper bound on created. Lets you fetch back-to-back ranges without overlap. |
Source records
Source records are the raw rows from the source data that contributed to a record anomaly. Two formats are available: JSON for inline display and CSV for export.
Get source records as JSON
Endpoint: GET /api/anomalies/{id}/source-record
Query parameters:
| Parameter | Type | Description |
|---|---|---|
limit |
int (≥1, default 10) |
Maximum number of rows returned. |
include_masked |
bool (default false) |
When true, returns raw values for masked fields. Requires the Editor team permission on the container, and the platform writes an audit-log entry naming the masked fields you accessed. |
Download source records as CSV
Endpoint: GET /api/anomalies/{id}/source-record/download
Streams a CSV file (source_records_<id>.csv). Same query parameters as the JSON endpoint.
Note
Source records are cached for up to 8 hours. If you need fresher data, see the Refresh Source Records section.
Tickets
Manage the link between an anomaly and an external ticket (Jira, ServiceNow). Linking does not call the external system; it just records the association so the UI can render the linked ticket.
List ticket links
Endpoint: GET /api/anomalies/{anomaly_id}/ticket-links
Create a ticket link
Endpoint: POST /api/anomalies/{anomaly_id}/ticket-links
Permission: Manager role + Author team permission on the anomaly's datastore.
Body fields:
| Field | Type | Description |
|---|---|---|
integration_id |
int |
The ticketing integration that owns this ticket. |
ticket_id |
string |
External ticket identifier (e.g., the ServiceNow sys_id). |
ticket_number |
string |
Human-readable ticket number (e.g., INC0010001). |
ticket_url |
string (optional) |
Direct URL to the ticket. |
status |
string |
Current external status of the ticket. |
ticket_metadata |
object (optional) |
Additional metadata to store alongside the link. |
Example
curl -X POST "https://your-instance.qualytics.io/api/anomalies/12345/ticket-links" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"integration_id": 3,
"ticket_id": "abc123sys",
"ticket_number": "INC0010001",
"ticket_url": "https://example.service-now.com/incident.do?sys_id=abc123sys",
"status": "New"
}'
Delete a ticket link
Endpoint: DELETE /api/anomalies/{anomaly_id}/ticket-links/{link_id}
Permission: Manager role + Author team permission on the anomaly's datastore. Removes the association only; the external ticket is left untouched.