Anomalies API¶
You can list, read, update, archive, and delete anomalies programmatically, and work with their history, comments, source records, and tickets.
Tip
For complete API documentation, including 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) and require a Personal API Token. Every endpoint requires the Member role. Reads also need the Reporter team permission on the anomaly's datastore, and changes need the Author team permission. Source records need the Viewer team permission, unmasked values the Editor team permission, and ticket links the Manager role on top of Author. Admins are exempt from team permissions. See Anomaly Permissions for the full matrix.
Error Responses¶
| Status Code | Description |
|---|---|
400 Bad Request |
The change is not allowed in the anomaly's current status, such as moving an archived anomaly straight back to Active, or giving an archived anomaly a different archived status. |
401 Unauthorized |
Missing or invalid API token. |
403 Forbidden |
The user does not have the required role, or lacks the required team permission on the anomaly's datastore. |
404 Not Found |
No anomaly, ticket link, or integration exists with the specified ID. |
409 Conflict |
The anomaly has no source records, the ticket is already linked to the anomaly, or the anomaly is archived and its comments cannot change. |
422 Unprocessable Entity |
Invalid request body or parameters, such as an unknown status or an invalid filters value. |
Error response examples
404 Not Found:
400 Bad Request (restoring an archived anomaly to Active):
409 Conflict (reading the source records of an anomaly that has none):
Anomaly Fields¶
Getting a single anomaly returns the full set of fields below. Items in the list response carry the same fields except operation and fingerprint_source, so get the anomaly by ID when you need them.
| Field | Type | Notes |
|---|---|---|
id |
int |
Numeric primary identifier. |
uuid |
string (UUID) |
Stable, globally unique identifier. |
type |
"record" \| "shape" |
Anomaly category. See Record Anomalies and Shape Anomalies. |
status |
string |
One of Active, Acknowledged, Resolved, Duplicate, Invalid, Discarded. See Status. |
impact_level |
"low" \| "medium" \| "high" \| "critical" \| null |
The anomaly's impact: the highest impact among its failed checks. See Impact. |
impact_reason |
string \| null |
Why the anomaly has its impact, in one line (for example, High: across this scan, some of the checked records failed, on a High-importance check.). |
weight |
float |
Deprecated: use impact_level. Still returned for existing integrations, and no longer used for sorting or Flow filters. |
anomalous_records_count |
int \| null |
Number of records associated with the anomaly. Set to 1 for record anomalies. For shape anomalies it holds the number of affected rows when Qualytics can count them, otherwise null. |
description |
string |
User-editable business description. The value is Markdown and has no length cap. |
created |
string (ISO 8601) |
When the anomaly was detected. |
fingerprint |
string |
Used to deduplicate recurring anomalies. Returned as a quoted string so that large values survive JavaScript clients. See Fingerprints. |
fingerprint_source |
AnomalyStub \| null |
The earliest other anomaly that shares this anomaly's fingerprint, with its id, uuid, type, status, and fingerprint. null when the anomaly has no fingerprint or no other anomaly shares it. Single-anomaly response only. |
global_tags |
Tag[] |
Tags applied to the anomaly. Each tag carries type, name, color, description, category, and weight_modifier. Tags have no id, so identify them by name. |
assignees |
UserStub[] |
Users assigned to the anomaly. See Assignees. |
failed_checks |
FailedCheck[] |
Checks that produced the anomaly. Each entry nests the check under quality_check (with its id and rule_type), next to the message, the suggested_field, suggested_value, and suggested_score fields, and parameters. Each entry also carries its levels: failure_level (how much failed, before the check's importance adjusts it), quality_check_importance_level (the check's importance that adjusted it), impact_level (the result), and impact_reason. |
datastore, container, partition |
refs | Source location of the anomaly. partition carries the partition's name and location. |
operation |
ref | The scan that produced the anomaly, with its type, start and end times, result, and who triggered it. Single-anomaly response only. |
Example response (abbreviated)
{
"id": 12345,
"uuid": "8e8b9f8b-1234-4abc-9def-012345678901",
"type": "record",
"status": "Active",
"impact_level": "high",
"impact_reason": "High: across this scan, some of the checked records failed, on a High-importance check.",
"weight": 54.0,
"anomalous_records_count": 1,
"description": "Customer ID is missing for new orders",
"created": "2026-05-15T17:22:08Z",
"fingerprint": "9182736455",
"fingerprint_source": null,
"global_tags": [
{ "type": "global", "name": "High", "color": "#E53935", "description": null, "category": null, "weight_modifier": 10 }
],
"assignees": [
{ "id": 42, "name": "Alice Lee", "email": "alice@example.com" }
],
"failed_checks": [
{
"quality_check": { "id": 778, "rule_type": "notNull" },
"message": "...",
"suggested_field": null,
"suggested_value": null,
"suggested_score": null,
"parameters": null,
"failure_level": "medium",
"quality_check_importance_level": "high",
"impact_level": "high",
"impact_reason": "High: across this scan, some of the checked records failed, on a High-importance check."
}
]
}
List Anomalies¶
Returns a paginated list of anomalies, scoped to the datastores the caller can see. Without a sort parameter, anomalies are returned by ID, oldest first. With sort_impact_level, anomalies at the same Impact are sorted by weight in the same direction, then by ID. Ties in the other sorts are broken by ID. To match the default order of the anomaly lists in the app, pass sort_impact_level=desc (highest Impact first).
Endpoint: GET /anomalies
Permission: Member role. Only the anomalies of datastores the caller has access to are returned.
| Query Parameter | Type | Description |
|---|---|---|
id |
list of integers or UUIDs |
Return the anomalies with these IDs or UUIDs. |
search |
string |
Match the anomaly ID or UUID, or a piece of a failed check's message, ignoring case. |
message |
string |
Only anomalies with a failed check whose message contains this text, ignoring case. |
status |
list of strings |
Only anomalies with these statuses: Active, Acknowledged, Resolved, Duplicate, Invalid, Discarded. |
archived |
include / only |
Include archived anomalies, or return only archived anomalies. |
anomaly_type |
record / shape |
Only anomalies of this type. |
datastore, container |
list of integers |
Only anomalies of these datastores or tables and files. |
field, field_profile |
list of strings / list of integers |
Only anomalies of these field names or field profile IDs. |
quality_check, template, rule_type |
list of integers / list of integers / list of strings |
Only anomalies produced by these checks, by checks built from these templates, or by checks of these rule types. |
operation, container_scan, partition_scan |
list of integers |
Only anomalies found by these Scan operations, container scans, or partition scans. |
resolved_by_operation, resolved_by_container_scan |
list of integers |
Only anomalies auto-resolved by these Scan operations or container scans. |
tag |
list of strings |
Only anomalies with any of these tag names. |
assignee |
list of integers |
Only anomalies assigned to any of these user IDs. |
impact_level |
list of strings |
Only anomalies at these impact levels: low, medium, high, or critical. Pass it once per level, for example impact_level=critical&impact_level=high. Inside filters, it takes in and not_in. Any other value returns a 422. |
related_to_id |
integer |
The ID of the earliest anomaly with a fingerprint. Returns the later anomalies that share that fingerprint. Passing the ID of a later anomaly returns no results. |
segment |
integer |
Only anomalies of this segment. |
source_enriched |
boolean |
Only anomalies with (true) or without (false) source records. |
timeframe, created_date, start_date, end_date |
varies | Only anomalies created in this period. |
filters |
string (JSON) |
Narrows the list by one or more filters, written as a JSON object. See Filtering Lists. |
sort_id, sort_created, sort_impact_level, sort_anomalous_records_count |
asc / desc |
Sort by ID, creation date, Impact, or anomalous record count. sort_weight is a deprecated alias of sort_impact_level. |
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/anomalies?status=Active&assignee=42&tag=High&sort_impact_level=desc" \
-H "Authorization: Bearer YOUR_TOKEN"
Response (abbreviated). Each item carries the anomaly's fields, except operation and fingerprint_source:
Filtering Lists¶
GET /api/anomalies accepts a filters parameter: a JSON object with one entry per filter, saying how that filter's values should match.
curl -G "https://your-instance.qualytics.io/api/anomalies" \
--data-urlencode 'filters={"tag":{"not_in":["PII"]},"assignee":{"empty":true}}' \
-H "Authorization: Bearer YOUR_TOKEN"
This returns the anomalies that aren't tagged PII and have no one assigned.
| Key | Returns |
|---|---|
in |
The items that match at least one of the values. |
not_in |
The items that match none of the values. |
all_of |
The items that match every value, on filters an item can have several of, such as tags. |
empty |
The items with no value (true) or at least one (false), on filters an item can lack. |
- Different filters combine with AND, and so do keys on the same filter.
emptycan't be combined with other keys on the same filter.- A filter sent both as its own parameter and inside
filtersreturns a422. Put all of that filter's conditions infilters. in,not_inandall_ofeach take between 1 and 20 values.- A filter or key the endpoint doesn't accept returns a
422that names it. The API docs list what each endpoint accepts.
Get an Anomaly¶
Returns the details of one anomaly, including the Scan that found it and the earliest anomaly that shares its fingerprint.
Endpoint: GET /anomalies/{id}
Permission: Member role, with the Reporter team permission on the anomaly's datastore
| Parameter | Type | Description |
|---|---|---|
id |
integer or UUID |
The anomaly ID or UUID. |
include_deleted |
boolean |
Also return the anomaly when it is archived. Defaults to false. |
Example request
curl -X GET "https://your-instance.qualytics.io/api/anomalies/12345?include_deleted=true" \
-H "Authorization: Bearer YOUR_TOKEN"
The response carries the fields listed in Anomaly Fields.
Get an Anomaly's Failed Checks¶
Returns the checks that failed in the anomaly, each with its message, as they were when the anomaly was created. Archived anomalies are included.
Endpoint: GET /anomalies/{id}/failed-checks
Permission: Member role, with the Reporter team permission on the anomaly's datastore
| Parameter | Type | Description |
|---|---|---|
id |
integer or UUID |
The anomaly ID or UUID. |
Example request
Update an Anomaly¶
Changes the status, description, tags, or assignees of an anomaly. Only the fields you send are changed.
Endpoint: PUT /anomalies/{id}
Permission: Member role, with the Author team permission on the anomaly's datastore
| Body Field | Type | Description |
|---|---|---|
status |
Active / Acknowledged |
Acknowledge or reactivate an open anomaly. Sending Acknowledged for an archived anomaly restores it. An archived anomaly cannot go straight back to Active. To archive an anomaly, use Archive or Delete an Anomaly. |
description |
string |
The anomaly's description, in Markdown, with no length limit. |
tags |
list of strings |
Tag names. Replaces the current tags, and [] removes them all. Tags on the anomaly do not change its impact_level. Only the tags on its failed checks do, through the checks' Importance. |
assignee_ids |
list of integers |
User IDs. Replaces the current assignees, and [] unassigns everyone. To add a user, read assignees first and send the full list. Each user needs at least the Viewer team permission on the datastore. |
Assigning from a check's default assignee happens only when the anomaly is created, so it cannot be triggered through this endpoint. See Anomaly Assignees.
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]
}'
The response carries the updated anomaly, with the fields listed in Anomaly Fields.
Update Anomalies in Bulk¶
Applies changes to many anomalies in one call. Each entry names an anomaly and the fields to change on it.
Endpoint: PATCH /anomalies
Permission: Member role, with the Author team permission on the datastore of every anomaly
The body is a list of entries. Each entry accepts the body fields of Update an Anomaly, plus:
| Body Field | Type | Description |
|---|---|---|
id |
integer or UUID |
The anomaly to update. |
tags_mode |
replace / add |
How tags is applied. replace (the default) makes tags the anomaly's only tags. add keeps the current tags and adds the given ones, without repeating a tag. |
Example request
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": [] },
{ "id": 12348, "tags": ["Critical"], "tags_mode": "add" }
]'
The response carries the list of updated anomalies.
Archive or Delete an Anomaly¶
Archives an anomaly with a resolution, or deletes it permanently. Returns 204 No Content.
Endpoint: DELETE /anomalies/{id}
Permission: Member role, with the Author team permission on the anomaly's datastore
| Query Parameter | Type | Description |
|---|---|---|
archive |
boolean |
true (the default) archives the anomaly. false deletes it permanently, whatever its status, and cannot be undone. |
status |
Resolved / Duplicate / Invalid / Discarded |
The resolution to archive with. When it is left out, an Active anomaly is archived as Resolved. An archived anomaly cannot be given a different resolution. |
| Body Field | Type | Description |
|---|---|---|
status |
string |
The resolution, as an alternative to the query parameter. |
comment |
string |
A comment added to the anomaly's Timeline when it is archived. |
mentioned_user_ids |
list of integers |
Users mentioned in the comment, who receive a Mention notification. |
Example requests
Archive with a resolution and a 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]
}'
Delete permanently:
Archive or Delete Anomalies in Bulk¶
Archives or deletes many anomalies in one call. Returns 204 No Content.
Endpoint: DELETE /anomalies
Permission: Member role, with the Author team permission on the datastore of every anomaly
The body is a list of entries. Each entry takes the anomaly's id and the same fields as Archive or Delete an Anomaly: archive, status, comment, and mentioned_user_ids.
Example request
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 }
]'
Get an Anomaly's Change History¶
Returns the paginated change log behind the anomaly's Timeline: status changes, description edits, tag and assignee changes, and ticket links and unlinks, each with the user who made it and when.
Endpoint: GET /anomalies/{id}/history
Permission: Member role, with the Reporter team permission on the anomaly's datastore
| Query Parameter | Type | Description |
|---|---|---|
include_deleted |
boolean |
Also return the history of an archived anomaly. Defaults to false. |
page |
integer |
The page to return, starting at 1. |
size |
integer |
Entries per page. Defaults to 50, up to 100. |
Each field in changeset is a [before, after] pair. transaction holds the change's id, issued_at time, and user, and operation is insert, update, or delete. Increase page until it reaches pages to read the whole log.
Example response
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
}
List an Anomaly's Comments¶
Returns the comments on an anomaly, newest first, as a list. Replies are returned together with the comment that starts their thread. A comment deleted while it still had replies is returned with an empty message.
Endpoint: GET /anomalies/{id}/comments
Permission: Member role, with the Reporter team permission on the anomaly's datastore
| Query Parameter | Type | Description |
|---|---|---|
start_date |
datetime (ISO 8601) |
Inclusive lower bound on created, for comments not attached to a Timeline entry. |
end_date |
datetime (ISO 8601) |
Exclusive upper bound on created, so back-to-back ranges do not overlap. |
transaction_id |
list of integers |
Return the comments attached to these Timeline entries, whatever the dates. |
Besides its message and created time, each comment carries:
| Field | Type | Description |
|---|---|---|
entity_type |
string |
The kind of asset the comment belongs to: anomaly, container, quality_check, partition, datastore, or field. |
entity_id |
int |
Identifier of that asset. |
transaction_id |
int (nullable) |
The Timeline entry the comment is anchored to. null when the comment is not anchored to a change. |
parent_id |
int (nullable) |
The comment this one replies to. null when the comment starts a thread. |
deleted_at |
datetime (nullable) |
Set when the comment was deleted but kept as a placeholder because it still has replies. |
Example request
Response change
Comments identify their asset with entity_type and entity_id. The previous anomaly_id and partition_id fields are no longer returned. Requests are unaffected: the previous payload shapes are still accepted when creating comments.
Post a Comment¶
Adds a comment to an anomaly, as a new comment, a reply, or a comment on one Timeline entry.
Endpoint: POST /comments
Permission: Member role, with the Viewer team permission on the anomaly's datastore
| Body Field | Type | Description |
|---|---|---|
entity_type |
string |
anomaly for an anomaly comment. The same endpoint also takes container, quality_check (for checks and check templates), partition, datastore, and field. |
entity_id |
integer |
The anomaly ID. |
message |
string |
The comment text. Use @[Full Name](user_id) to mention a user. |
transaction_id |
integer |
Optional. Attaches the comment to this Timeline entry of the same anomaly. |
parent_id |
integer |
Optional. Posts the comment as a reply to this comment of the same anomaly. |
mentioned_user_ids |
list of integers |
Optional. Users to notify. They must be active users. |
Example request
curl -X POST "https://your-instance.qualytics.io/api/comments" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"entity_type": "anomaly",
"entity_id": 12345,
"transaction_id": 9876,
"message": "Expected during the migration window. @[Alice Lee](42) please confirm.",
"mentioned_user_ids": [42]
}'
The response carries the comment's message, author, and mentions. To read entity_type, entity_id, transaction_id, parent_id, or deleted_at, get the comment again with one of the GET endpoints.
A rejected comment returns a status code that says what to correct:
| Status | Returned when |
|---|---|
422 |
The comment replies to a reply (threads are one level deep), the transaction_id is not a Timeline entry of that same asset, the parent_id belongs to a different asset, a mentioned user does not exist or is no longer active, or commenting is not supported for that asset type. |
409 |
The asset is archived, so comments cannot be added, edited, or deleted until it is unarchived. Also returned when replying to a deleted comment or editing a deleted comment. |
403 |
You lack the required team permission on the asset's datastore, or you are trying to edit another user's comment. Only the author can edit a comment. |
404 |
The comment or the asset does not exist. |
Update a Comment¶
Changes the text of a comment. Only its author can update it.
Endpoint: PUT /comments/{id}
Permission: Member role, as the comment's author
| Body Field | Type | Description |
|---|---|---|
message |
string |
The new comment text. |
mentioned_user_ids |
list of integers |
Users mentioned in the comment. Only users newly added are notified. |
Example request
Delete a Comment¶
Deletes a comment. A comment that still has replies is kept as a placeholder, so the replies stay in the thread. Deleting the last reply also removes the placeholder.
Endpoint: DELETE /comments/{id}
Permission: Member role, as the comment's author, or the Admin role
Example request
Get Source Records¶
Returns the rows behind an anomaly, as stored by the Scan in the enrichment datastore.
Endpoint: GET /anomalies/{id}/source-record
Permission: Member role, with the Viewer team permission on the anomaly's datastore. include_masked=true also needs the Editor team permission.
| Query Parameter | Type | Description |
|---|---|---|
limit |
integer |
The most rows to return. Defaults to 10. |
include_masked |
boolean |
Return the real values of masked fields. Each call is recorded in the Masking Audit Log with the masked fields. Defaults to false. |
An anomaly without source records returns 409 Conflict. See Source Records.
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/anomalies/12345/source-record?limit=5" \
-H "Authorization: Bearer YOUR_TOKEN"
Response: source_record holds the rows as a JSON string, so parse it to read them.
Download Source Records¶
Downloads the rows behind an anomaly as a CSV file named source_records_<id>.csv.
Endpoint: GET /anomalies/{id}/source-record/download
Permission: Member role, with the Viewer team permission on the anomaly's datastore. include_masked=true also needs the Editor team permission.
| Query Parameter | Type | Description |
|---|---|---|
limit |
integer |
The most rows to return. With no limit, the file holds every row the Scan kept. |
include_masked |
boolean |
Return the real values of masked fields, recorded in the Masking Audit Log. Defaults to false. |
Example request
Reading source records from the enrichment datastore
Real values of masked fields are only returned by endpoints that accept include_masked: these two, the container scan equivalents (see Get source records of a container scan), and GET /containers/{id}/source-records. All of them check the Editor team permission and write to the Masking Audit Log. Reading the _source_records output with GET /datastores/{id}/source-records always returns ***MASKED*** in the RECORD column. See Read Source Records.
Search Tickets¶
Searches the connected ticketing system for tickets whose key or summary matches, to find an existing ticket before linking it.
Endpoint: GET /integrations/ticketing/tickets/search
Permission: Manager role
| Query Parameter | Type | Description |
|---|---|---|
query |
string |
Text to match against the ticket key or summary. |
status |
string |
Only tickets with this status. |
priority |
string |
Only tickets with this priority. |
project_key |
string |
Jira only. Only tickets of this project. Without it, every configured project is searched. |
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/integrations/ticketing/tickets/search?query=customer" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
{
"items": [
{
"ticket_id": "10002",
"ticket_number": "SCRUM-3",
"title": "Task 3",
"description": null,
"status": "To Do",
"priority": null,
"created_at": "2025-12-02T12:34:20",
"updated_at": "2025-12-02T12:34:21",
"assigned_to": null,
"url": "https://your-domain.atlassian.net/browse/SCRUM-3"
}
],
"total": 1,
"page": 1,
"size": 50,
"pages": 1
}
Search Tickets One Page at a Time¶
Runs the same search a page at a time, with a token for the next page. This is what the Link Ticket dialog uses for its Load more tickets button. It does not count every match first, so it stays quick on large projects.
Endpoint: GET /integrations/ticketing/tickets/search-page
Permission: Manager role
| Query Parameter | Type | Description |
|---|---|---|
query |
string |
Optional. On Jira, matches a ticket key, the key or name of an allowed project, or words in the summary, description, or comments. On ServiceNow, matches words in the short description or description, not the incident number. Leave it empty to list the most recently updated tickets first. |
project_key |
string |
Optional. Jira only. Limits the search to one of the allowed projects. Any other key returns 400. Without it, every allowed project is searched. |
status |
string |
Optional. Only tickets with this status. |
priority |
string |
Optional. Only tickets with this priority. |
limit |
integer |
The most tickets in the page, from 1 to 100. Defaults to 50. A value above 100 returns 422. |
next_page_token |
string |
The token from the previous page. Leave it out for the first page. |
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/integrations/ticketing/tickets/search-page?project_key=SCRUM&limit=2" \
-H "Authorization: Bearer YOUR_TOKEN"
Response: pass next_page_token back unchanged to read the next page. It is null on the last page.
{
"items": [
{
"ticket_id": "10431",
"ticket_number": "SCRUM-43",
"title": "Null customer IDs in orders",
"description": null,
"status": "To Do",
"priority": "Medium",
"created_at": "2026-10-20T14:02:11",
"updated_at": "2026-10-26T09:15:40",
"assigned_to": null,
"url": "https://your-domain.atlassian.net/browse/SCRUM-43"
},
{
"ticket_id": "10430",
"ticket_number": "SCRUM-42",
"title": "Late shipments feed",
"description": null,
"status": "In Progress",
"priority": "Medium",
"created_at": "2026-10-19T11:47:03",
"updated_at": "2026-10-25T16:30:12",
"assigned_to": null,
"url": "https://your-domain.atlassian.net/browse/SCRUM-42"
}
],
"next_page_token": "CAEaAggD"
}
Get a Ticket¶
Returns one ticket from the connected ticketing system.
Endpoint: GET /integrations/ticketing/tickets/{ticket_id}
Permission: Manager role
| Parameter | Type | Description |
|---|---|---|
ticket_id |
string |
The ticket's ID or key, such as the sys_id in ServiceNow or PROJ-123 in Jira. |
Example request
Create a Ticket for an Anomaly¶
Creates a ticket in the connected ticketing system and links it to the anomaly in one call. The anomaly's details are added to the description, with a link back to Qualytics. On Jira, the issue is created with the type set for that project on the integration, Task unless another was picked. A type Jira refuses returns 400, naming the type and the project.
Endpoint: POST /integrations/ticketing/anomalies/{anomaly_id}/tickets
Permission: Manager role, with the Author team permission on the anomaly's datastore
| Body Field | Type | Description |
|---|---|---|
title |
string |
The ticket's summary. |
description |
string |
Optional. The ticket's description. The anomaly's details are added to it. |
status |
string |
Optional. The ticket's initial status. |
priority |
string |
Optional. The ticket's priority. |
urgency, impact |
string |
Optional. ServiceNow only. |
category, subcategory |
string |
Optional. ServiceNow only. |
assigned_to, assigned_group |
string |
Optional. The user and the group responsible for the ticket. |
project_key |
string |
Optional. Jira only. The project to create the ticket in. Without it, the integration's default project is used. |
additional_fields |
object |
Optional. Other fields to send to the ticketing system. |
For the values each field accepts, see Create a Jira Ticket and Create a ServiceNow Ticket.
Example request and response
Request:
curl -X POST "https://your-instance.qualytics.io/api/integrations/ticketing/anomalies/1/tickets" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Investigate CUSTOMER_NAME similarity anomaly",
"description": "Follow-up on an anomaly detected during the daily scan.",
"priority": "Medium"
}'
Response:
{
"ticket": {
"ticket_id": "10428",
"ticket_number": "SCRUM-40",
"title": "Investigate CUSTOMER_NAME similarity anomaly",
"status": "Idea",
"priority": "Medium",
"category": "Task",
"created_at": "2026-01-23T15:22:05",
"url": "https://your-domain.atlassian.net/browse/SCRUM-40",
"metadata": { "project_key": "SCRUM", "issue_type": "Task" }
},
"anomaly_id": 1,
"integration_id": 2,
"message": "Ticket SCRUM-40 created and linked to anomaly 1"
}
List Ticket Links¶
Returns the tickets linked to an anomaly. Each call also asks Qualytics to refresh those tickets from the ticketing system in the background, so the next call returns their latest status.
Endpoint: GET /anomalies/{anomaly_id}/ticket-links
Permission: Member role, with the Reporter team permission on the anomaly's datastore
Example request and response
Request:
curl -X GET "https://your-instance.qualytics.io/api/anomalies/12345/ticket-links" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
[
{
"id": 678,
"anomaly_id": 12345,
"integration_id": 3,
"ticket_id": "abc123sys",
"ticket_number": "INC0010001",
"ticket_url": "https://example.service-now.com/incident.do?sys_id=abc123sys",
"status": "New",
"last_synced": "2026-05-15T18:02:11Z",
"ticket_metadata": null,
"post_updates": true,
"created": "2026-05-15T17:40:00Z"
}
]
Create a Ticket Link¶
Links an existing ticket to an anomaly. Only the link is recorded in Qualytics: nothing is sent to the ticketing system.
Endpoint: POST /anomalies/{anomaly_id}/ticket-links
Permission: Manager role, with the Author team permission on the anomaly's datastore
| Body Field | Type | Description |
|---|---|---|
integration_id |
integer |
The ticketing integration that holds the ticket. |
ticket_id |
string |
The ticket's ID in the ticketing system, such as the ServiceNow sys_id. |
ticket_number |
string |
The ticket's number, such as INC0010001. |
ticket_url |
string |
Optional. A direct link to the ticket. |
status |
string |
The ticket's current status. |
ticket_metadata |
object |
Optional. More data to keep with the link. |
Linking a ticket that is already linked to the anomaly returns 409 Conflict.
Example request
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"
}'
The response carries the new link, with the fields shown in List Ticket Links.
Update a Ticket Link¶
Turns Qualytics writes on or off for one linked ticket, which is what the Two-way / Read only button on the ticket card does. The ticket's status, title, and comments are still read back either way.
Endpoint: PATCH /anomalies/{anomaly_id}/ticket-links/{link_id}
Permission: Manager role, with the Author team permission on the anomaly's datastore
| Body Field | Type | Description |
|---|---|---|
post_updates |
boolean |
Whether Qualytics posts comments and status notes to this ticket. |
Example request
Delete a Ticket Link¶
Removes the link between a ticket and an anomaly. The ticket stays in the ticketing system. Returns 204 No Content.
Endpoint: DELETE /anomalies/{anomaly_id}/ticket-links/{link_id}
Permission: Manager role, with the Author team permission on the anomaly's datastore