Skip to content

Flow API

You can create, read, update, execute, and delete Flows programmatically, and manage their executions.

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. Reading Flows and executions requires the Member role. Every write operation, execution, and deletion also requires the Editor team permission on every datastore the Flow relates to, which Admins are exempt from. See Flow Permissions for the full matrix.

Trigger type values

The API identifies trigger types by these values: Schedule, Operation (Operation Completed), PartitionScan (Anomalous Table or File Detected), Anomaly (Anomaly Detected), AnomalyStatusChange (Anomaly Status Changed), and Manual.

Note

The action payloads and the notification-specific endpoints (tokens, specifications, and test notifications) are documented on the Actions API page.


Error Responses

Status Code Description
400 Bad Request The request conflicts with the execution's state, such as aborting an execution that has already completed.
401 Unauthorized Missing or invalid API token.
403 Forbidden The user does not have the required role, lacks the Editor team permission on a datastore the Flow relates to, or the Flow is deactivated and cannot be executed.
404 Not Found No Flow or execution exists with the specified ID, or the request refers to a datastore that does not exist.
422 Unprocessable Entity Invalid request body, such as an unknown trigger type, an invalid timezone, a Flow without actions, or an inconsistent action chain.
Error response examples

404 Not Found:

{ "detail": "Flow id: 999 not found" }

400 Bad Request (aborting a finished execution):

{ "detail": "Flow execution id: 1001 has already completed with result: success" }

422 Unprocessable Entity (validation errors return a list of issues):

{
  "detail": [
    {
      "type": "value_error",
      "loc": ["body", "timezone"],
      "msg": "Value error, Invalid timezone: 'America/NewYork'. Must be a valid IANA timezone (e.g. 'America/New_York').",
      "input": "America/NewYork"
    }
  ]
}

List Flows

Returns a paginated list of Flows, sorted by creation date with the most recently created first.

Endpoint: GET /flows

Permission: Member role or above

Query Parameter Type Description
id integer Return the Flow with this ID.
name string Return Flows with this exact name.
search string Match a piece of the name or the ID.
datastore list of integers Only Flows related to these datastore IDs, including Flows that reach every datastore.
datastore_named_only boolean With datastore, match only Flows that name one of the datastores, in the trigger's datastore filter or in an action. A Flow that reaches every datastore is left out unless it also names one of them, for example in an operation action.
trigger_type list of strings Only Flows with these trigger types.
tag list of strings Only Flows whose trigger filters on any of these tag names.
created_by list of integers Only Flows created by any of these user IDs.
recipient list of strings Only Flows with an Email action that notifies any of these recipients. An email address matches the Flows that send to it, whether it belongs to a picked user, a member of a picked team, or a typed address, ignoring case; a partial address matches nothing. team:<id> matches the Flows that pick that team. Inside filters, recipient takes in, not_in, and all_of.
only_deactivated boolean true returns only deactivated Flows; false returns only active Flows. Leave it out to list both.
filters string (JSON) Narrows the list by one or more filters, written as a JSON object. See Filtering Lists.
sort_created, sort_name, sort_last_triggered asc / desc Sort by creation date, name, or last triggered time.
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/flows?search=scan&sort_created=desc" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (abbreviated; each item carries the full Flow):

{
  "items": [
    {
      "id": 42,
      "name": "Notify on scan failures",
      "description": "Sends an in-app notification when a scan fails",
      "deactivated": false,
      "trigger_type": "Operation",
      "filter_operation_type": ["Scan"],
      "filter_operation_result": "failure",
      "last_triggered": "2026-09-07T14:03:22Z",
      "user_can_manage": true
    }
  ],
  "total": 1,
  "page": 1,
  "size": 50
}

Filtering Lists

GET /api/flows and GET /api/flows/executions accept 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/flows" \
  --data-urlencode 'filters={"trigger_type":{"not_in":["Manual"]}}' \
  -H "Authorization: Bearer YOUR_TOKEN"

This returns the Flows whose trigger isn't Manual.

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.
  • empty can't be combined with other keys on the same filter.
  • A filter sent both as its own parameter and inside filters returns a 422. Put all of that filter's conditions in filters.
  • in, not_in and all_of each take between 1 and 20 values.
  • A filter or key the endpoint doesn't accept returns a 422 that names it. The API docs list what each endpoint accepts.

Get a Flow

Returns the details of one Flow, including its trigger configuration and actions.

Endpoint: GET /flows/{id}

Permission: Member role or above

Parameter Type Description
id integer The Flow ID.
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/flows/42" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (abbreviated):

{
  "id": 42,
  "name": "Notify on scan failures",
  "description": "Sends an in-app notification when a scan fails",
  "deactivated": false,
  "trigger_type": "Operation",
  "crontab": null,
  "timezone": null,
  "next_trigger": null,
  "filter_datastores": [{ "id": 7, "name": "Payments DB" }],
  "filter_operation_type": ["Scan"],
  "filter_operation_result": "failure",
  "actions": [
    {
      "id": 310,
      "type": "notification",
      "notification_type": "InApp",
      "tokenized_message": "Scan on {{datastore_name}} finished: {{operation_result}}"
    }
  ],
  "last_triggered": "2026-09-07T14:03:22Z"
}

Create a Flow

Creates a new Flow with its trigger configuration and its actions. A Flow needs at least one action.

Endpoint: POST /flows

Permission: Member role or above, plus the Editor team permission on every datastore the Flow relates to (Admins are exempt)

Request Body (main fields):

Field Type Required Description
name string The Flow's name, up to 255 characters.
description string The Flow's description.
trigger_type string One of the trigger type values above.
crontab string The cron expression. Required for the Schedule trigger.
timezone string IANA timezone name for the schedule (defaults to UTC).
filter_datastores list of integers Datastore IDs the trigger is filtered to. On an event trigger, leaving it empty fires on events from every datastore, which needs the Editor team permission on all of them. With filter_operators.filter_datastores set to not_in, the Flow runs for every datastore except these, including datastores added later, which also needs the Editor team permission on all of them. It is not part of filter_match. An ID that does not exist returns 404.
filter_operation_type list of strings Operation types (Sync, Profile, Scan) for the Operation trigger.
filter_operation_result string success or failure for the Operation trigger.
tags list of strings Tags of the triggering asset. By default the asset must have all of them; see filter_operators.tags. Leave it empty with empty or not_empty.
check_rule_types list of strings Check rule types for the anomaly triggers.
impact_level_threshold string Anomaly impact level (low, medium, high, or critical) for the Anomaly and AnomalyStatusChange triggers. By default, anomalies at or above this level start the Flow. To compare differently, set filter_operators.impact_level_threshold to gte (at least), lte (at most), or eq (exactly).
weight_threshold integer Deprecated: use impact_level_threshold. Used only when impact_level_threshold is left out, and then saved as a level with the same comparison. With gte, a minimum of 0 or 1 is saved as low, 2 to 7 as medium, 8 to 14 as high, and 15 or more as critical. With lte or eq, it is saved as low. The number is not compared with anomalies.
filter_anomaly_status list of strings Target statuses for the AnomalyStatusChange trigger.
filter_match string all (the default) starts the Flow only when every trigger filter matches, any when at least one does. Empty filters are ignored, except tags with the empty or not_empty operator, which take no tags and still count. filter_datastores always applies.
filter_operators object The operator of each trigger filter, by field name. A filter left out uses its default. tags: all_of (the default), in, not_in, empty, or not_empty. filter_datastores, filter_operation_type, check_rule_types, and filter_anomaly_status: in (the default) or not_in. filter_operation_result: in (the default, the result is this value) or not_in (any other result). impact_level_threshold: gte (the default), lte, or eq.
deactivated boolean Create the Flow deactivated (defaults to false).
actions list of objects The action chain, with at least one action. Each action carries a uuid you generate and a type (operation, notification, anomaly, create_ticket, ticket_status_update) plus type-specific fields. A request with actions left out, null, or empty returns 422 and creates nothing.

Building the actions list

Action payloads vary by type. The practical way to compose one is to build a similar Flow in the UI once, fetch it with GET /flows/{id}, and use its actions array as the template.

Example request and response

Request (an Operation-triggered Flow with one in-app notification):

curl -X POST "https://your-instance.qualytics.io/api/flows" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Notify on scan failures",
    "description": "Sends an in-app notification when a scan fails",
    "trigger_type": "Operation",
    "filter_datastores": [7],
    "filter_operation_type": ["Scan"],
    "filter_operation_result": "failure",
    "actions": [
      {
        "uuid": "3f8c1e42-9d77-4b6a-8f21-5c0a7b6d1e44",
        "type": "notification",
        "notification_type": "InApp",
        "tokenized_message": "{{operation_type}} on {{datastore_name}}: {{operation_result}}",
        "parameters": {}
      }
    ]
  }'

Response (abbreviated):

{
  "id": 42,
  "name": "Notify on scan failures",
  "trigger_type": "Operation",
  "deactivated": false,
  "actions": [
    { "id": 108, "uuid": "3f8c1e42-9d77-4b6a-8f21-5c0a7b6d1e44", "type": "notification" }
  ],
  "last_triggered": null
}

422 Unprocessable Entity (no actions; the ID in the message belongs to a Flow that is never saved):

{ "detail": "Invalid schema provided for flow '42' due to missing actions. Please add at least one action to the flow." }

Update a Flow

Replaces a Flow's configuration. The body has to describe the Flow in full, so fetch it with Get a Flow, change what you need, and send the whole thing back.

Endpoint: PUT /flows/{id}

Permission: Member role or above, plus the Editor team permission on every datastore the Flow relates to (Admins are exempt)

The body accepts the same fields as Create a Flow, and name and trigger_type are required. Leave actions out, or send it as null, to keep the stored actions. When you send actions, each action is matched by its uuid: send an existing action with both its uuid and its id to keep or edit it, send a new uuid without an id to add one, and leave an action out to delete it. A flow must keep at least one action, so "actions": [] returns 422 with the missing actions message. Changing trigger_type needs the actions list too, or the request returns 422: "Actions are required when the trigger type changes, so they can be checked against the new trigger."

Send every value you want to keep

This is not a partial update. Trigger settings left out of the body are cleared, including description, crontab, timezone, filter_operation_type, filter_operation_result, check_rule_types, weight_threshold, and filter_anomaly_status. Leaving out deactivated reactivates a deactivated Flow, and an actions list you send replaces the stored one in full. Only filter_datastores, tags, filter_match, and filter_operators keep their stored values when omitted or sent as null. impact_level_threshold keeps its value when omitted, but sending impact_level_threshold: null clears the impact filter. A filter_operators object you send replaces the stored one, so any filter it leaves out goes back to its default operator.

On a Flow whose trigger_type is Schedule, a body without crontab is rejected with 422 and the message crontab is required when trigger_type is Schedule.

Rename a Flow without losing its configuration

Step 1: read the Flow as it stands.

curl -X GET "https://your-instance.qualytics.io/api/flows/42" \
  -H "Authorization: Bearer YOUR_TOKEN"

Step 2: send the whole configuration back with the new name.

curl -X PUT "https://your-instance.qualytics.io/api/flows/42" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Notify on scan failures (payments)",
    "trigger_type": "Operation",
    "description": "Alerts the data team when a payments scan fails",
    "deactivated": false,
    "filter_datastores": [1],
    "filter_operation_type": ["scan"],
    "filter_operation_result": "failure",
    "actions": [
      {
        "id": 108,
        "uuid": "3f8c1e42-9d77-4b6a-8f21-5c0a7b6d1e44",
        "type": "notification",
        "notification_type": "InApp",
        "tokenized_message": "{{operation_type}} on {{datastore_name}}: {{operation_result}}",
        "parameters": {}
      }
    ]
  }'

Response (abbreviated):

{
  "id": 42,
  "name": "Notify on scan failures (payments)",
  "trigger_type": "Operation",
  "description": "Alerts the data team when a payments scan fails"
}

Activate or Deactivate Flows

Sets one Flow's deactivated state, or updates it for several Flows at once. A deactivated Flow keeps its configuration but does not start on its trigger.

Endpoints: PATCH /flows/{id}/deactivated (single) and PATCH /flows/deactivation (bulk)

Permission: Member role or above, plus the Editor team permission on every datastore the Flow relates to (Admins are exempt)

Deactivate one Flow

Request:

curl -X PATCH "https://your-instance.qualytics.io/api/flows/42/deactivated" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "deactivated": true }'

Response (abbreviated):

{ "id": 42, "name": "Notify on scan failures", "deactivated": true }
Activate two Flows in bulk

Request:

curl -X PATCH "https://your-instance.qualytics.io/api/flows/deactivation" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    { "id": 42, "deactivated": false },
    { "id": 43, "deactivated": false }
  ]'

Response: the list of updated Flows.


Execute a Flow

Runs a Flow and returns the new execution, created in the running state. This is the same endpoint the platform's scheduler calls for scheduled Flows.

Endpoint: POST /flows/{id}/execute

Permission: Member role or above, plus the Editor team permission on every datastore the Flow relates to (Admins are exempt)

A deactivated Flow cannot be executed; the request returns 403 Forbidden with "Flow id: 42 is deactivated".

Example request and response

Request (no body required):

curl -X POST "https://your-instance.qualytics.io/api/flows/42/execute" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (abbreviated):

{
  "id": 1001,
  "flow": { "id": 42, "name": "Notify on scan failures", "user_can_manage": true },
  "trigger_type": "Operation",
  "start_time": "2026-09-08T12:00:00Z",
  "end_time": null,
  "result": "running",
  "action_executions": []
}

Delete a Flow

Deletes a Flow, its execution history, and its schedules. Operations the Flow triggered remain, but their link to the Flow is removed.

Endpoint: DELETE /flows/{id}

Permission: Member role or above, plus the Editor team permission on every datastore the Flow relates to (Admins are exempt)

Example request and response

Request:

curl -X DELETE "https://your-instance.qualytics.io/api/flows/42" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response: 204 No Content


Get a Flow's Change History

Returns a paginated change log with every version of the Flow and the user who made each change.

Endpoint: GET /flows/{id}/history

Permission: Member role or above

Example request
curl -X GET "https://your-instance.qualytics.io/api/flows/42/history" \
  -H "Authorization: Bearer YOUR_TOKEN"

List Executions

Returns a paginated list of Flow executions.

Endpoint: GET /flows/executions

Permission: Member role or above

Query Parameter Type Description
flow list of integers Only executions of these Flow IDs.
search string Match an execution ID.
created date Only executions created on this date.
trigger_type list of strings Only executions started by these trigger types.
tag list of strings Only executions of Flows whose trigger filtered on any of these tag names when the run started. Runs older than the Flow's saved history match by the Flow's current tags.
result list of strings success, failure, aborted, queued, or running.
filters string (JSON) Narrows the list by one or more filters, written as a JSON object. See Filtering Lists.
sort_created, sort_duration asc / desc Sort by creation date or duration.
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/flows/executions?result=failure&sort_created=desc" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (abbreviated):

{
  "items": [
    {
      "id": 1001,
      "flow": { "id": 42, "name": "Notify on scan failures", "user_can_manage": true },
      "trigger_type": "Operation",
      "trigger_id": 8842,
      "start_time": "2026-09-07T14:03:22Z",
      "end_time": "2026-09-07T14:03:40Z",
      "result": "failure",
      "message": "One or more actions failed"
    }
  ],
  "total": 1,
  "page": 1,
  "size": 50
}

Get an Execution

Returns the details of one Flow execution, including each action's execution and result.

Endpoint: GET /flows/executions/{id}

Permission: Member role or above

Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/flows/executions/1001" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (abbreviated):

{
  "id": 1001,
  "flow": { "id": 42, "name": "Notify on scan failures" },
  "trigger_type": "Operation",
  "start_time": "2026-09-07T14:03:22Z",
  "end_time": "2026-09-07T14:03:40Z",
  "result": "success",
  "action_executions": [
    {
      "action": { "id": 310, "type": "notification", "notification_type": "Slack" },
      "result": "success",
      "message": null
    }
  ]
}

Abort Executions

Aborts a running execution together with its action executions and any running operations they started. The bulk variant aborts several executions at once.

Endpoints: POST /flows/executions/{id}/abort (single) and POST /flows/executions/abort (bulk)

Permission: Member role or above, plus the Editor team permission on every datastore the execution's Flow relates to (Admins are exempt)

Aborting an execution that has already finished returns 400 Bad Request naming its final result.

Abort one execution

Request (no body required):

curl -X POST "https://your-instance.qualytics.io/api/flows/executions/1001/abort" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response: the aborted execution, with "result": "aborted".

Abort executions in bulk

Request:

curl -X POST "https://your-instance.qualytics.io/api/flows/executions/abort" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[ { "id": 1001 }, { "id": 1002 } ]'

Response: the list of aborted executions.


Delete Executions

Deletes an execution, or several at once by ID. Returns 204 No Content on success.

Endpoints: DELETE /flows/executions/{id} (single) and DELETE /flows/executions (bulk)

Permission: Member role or above, plus the Editor team permission on every datastore the execution's Flow relates to (Admins are exempt)

Delete executions in bulk

Request:

curl -X DELETE "https://your-instance.qualytics.io/api/flows/executions" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[ { "id": 1001 }, { "id": 1002 } ]'

Response: 204 No Content