Skip to content

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
curl -X GET "https://your-instance.qualytics.io/api/anomalies/12345" \
  -H "Authorization: Bearer YOUR_TOKEN"

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
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": [] }
  ]'

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} with assignee_ids in the body for a single anomaly.
  • PATCH /api/anomalies with assignee_ids per entry for many anomalies.
  • GET /api/anomalies?assignee=<id> to filter the list by assignee.

Behavior:

  • Each user passed in assignee_ids must 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 assignees first 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
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] }'
Clear all assignees
curl -X PUT "https://your-instance.qualytics.io/api/anomalies/12345" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "assignee_ids": [] }'
Bulk-replace assignees across many anomalies
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 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
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 request: hard-delete an archived anomaly
curl -X DELETE "https://your-instance.qualytics.io/api/anomalies/12345?archive=false" \
  -H "Authorization: Bearer YOUR_TOKEN"

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 comments. Each entry carries the user who made the change and a timestamp.

Endpoint: GET /api/anomalies/{id}/history

Example response (abbreviated)
[
  {
    "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.

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.
include_deleted bool (default false) When true, deleted comments are included in the response.

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.

Endpoint: GET /api/anomalies/{anomaly_id}/ticket-links

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"
  }'

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.