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{:target="_blank"}.
!!! note "Permissions" All endpoints require the Member global role. Most reads need the Viewer team permission on the anomaly's datastore; writes need Author (or higher), with description edits additionally requiring Editor. Ticket-link writes need the Manager global role plus Author on the datastore. Per-endpoint requirements are called out below.
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{:target="_blank"}. |
status |
string |
One of Active, Acknowledged, Resolved, Duplicate, Invalid, Discarded. See Status{:target="_blank"}. |
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{:target="_blank"}. |
global_tags |
Tag[] |
Tags applied to the anomaly. |
assignees |
UserStub[] |
Users assigned to the anomaly. See Assignees{:target="_blank"}. |
failed_checks |
FailedCheck[] |
Checks that produced the anomaly. |
datastore, container, partition_scan |
refs | Source location of the anomaly. |
??? example "Example response (abbreviated)"
```json
{
"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": "..." }]
}
```
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 "Example request"
```bash
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.
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
```bash
curl -X GET "https://your-instance.qualytics.io/api/anomalies/12345" \
-H "Authorization: Bearer YOUR_TOKEN"
```
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
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 Editor.
Body fields:
| Field | Type | Description |
|---|---|---|
status |
"Active" | "Acknowledged" |
Change the open-state of the anomaly. 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 Viewer on the datastore. |
??? example "Example request"
```bash
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]
}'
```
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 Editor 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
```bash
curl -X PATCH "https://your-instance.qualytics.io/api/anomalies" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "id": 12345, "status": "Acknowledged" },
{ "id": 12346, "assignee_ids": [42] },
{ "id": 12347, "tags": [] }
]'
```
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 Viewer 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{:target="_blank"}.
??? example "Set or replace the assignee list"
```bash
curl -X PUT "https://your-instance.qualytics.io/api/anomalies/12345" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "assignee_ids": [42, 57] }'
```
??? example "Clear all assignees"
```bash
curl -X PUT "https://your-instance.qualytics.io/api/anomalies/12345" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "assignee_ids": [] }'
```
??? example "Bulk-replace assignees across many anomalies"
```bash
curl -X PATCH "https://your-instance.qualytics.io/api/anomalies" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "id": 12345, "assignee_ids": [42, 57] },
{ "id": 12346, "assignee_ids": [42] },
{ "id": 12347, "assignee_ids": [] }
]'
```
See Deep Dive · Anomaly Assignees{:target="_blank"} for inheritance rules, notification behavior, and history tracking.
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.
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; false hard-deletes it. Hard-delete only works on already-archived anomalies. |
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 "Example request: archive with a resolution comment"
```bash
curl -X DELETE "https://your-instance.qualytics.io/api/anomalies/12345?archive=true&status=Resolved" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"comment": "Resolved after backfill, see @[Alice Lee](42)",
"mentioned_user_ids": [42]
}'
```
??? example "Example request: hard-delete an archived anomaly"
```bash
curl -X DELETE "https://your-instance.qualytics.io/api/anomalies/12345?archive=false" \
-H "Authorization: Bearer YOUR_TOKEN"
```
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
```bash
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 }
]'
```
Returns the paginated change log for an anomaly: status changes, description edits, tag updates, assignee changes, and comments. Each entry carries the user who made the change and a timestamp.
Endpoint: GET /api/anomalies/{id}/history
??? example "Example response (abbreviated)"
```json
[
{
"transaction_id": 9876,
"user": { "id": 1, "name": "Alice Lee" },
"timestamp": "2026-05-15T17:22:08Z",
"changeset": {
"status": ["Active", "Acknowledged"],
"assignees": [
[{ "id": 42, "name": "Alice Lee" }],
[{ "id": 42, "name": "Alice Lee" }, { "id": 57, "name": "Bob Patel" }]
]
}
}
]
```
Each changeset field is a [before, after] tuple, so you can reconstruct the audit trail offline.
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. |
include_deleted |
bool (default false) |
When true, deleted comments are included in the response. |
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.
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 Editor permission on the container, and the platform writes an audit-log entry naming the masked fields you accessed. |
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{:target="_blank"} section.
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.
Endpoint: GET /api/anomalies/{anomaly_id}/ticket-links
Endpoint: POST /api/anomalies/{anomaly_id}/ticket-links
Permission: Manager global 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
```bash
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"
}'
```
Endpoint: DELETE /api/anomalies/{anomaly_id}/ticket-links/{link_id}
Permission: Manager global role + Author team permission on the anomaly's datastore. Removes the association only; the external ticket is left untouched.