Skip to content

Lineage API

You can manage lineage edges and query the lineage graph programmatically using the Qualytics API.

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).

Add-on required

All lineage endpoints require the Lineage add-on to be enabled for your deployment. Requests made when the add-on is disabled return 403 Forbidden.


Create an Edge

Creates a lineage edge connecting a source and a target. Both sides must be at the same granularity: either both containers or both fields.

Endpoint: POST /api/lineage/edges

Role: Manager

Container-level edge

Request:

curl -X POST "https://your-instance.qualytics.io/api/lineage/edges" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source_container_id": 42,
    "target_container_id": 55,
    "source_type": "manual"
  }'

Response (200):

{
  "id": 1,
  "created": "2026-05-01T10:00:00.000000Z",
  "source_container_id": 42,
  "source_field_id": null,
  "target_container_id": 55,
  "target_field_id": null,
  "description": null,
  "source_type": "manual",
  "source_container": {
    "id": 42,
    "name": "raw_orders",
    "display_name": null,
    "container_type": "table",
    "datastore_id": 10,
    "datastore_name": "Production DB"
  },
  "source_field": null,
  "target_container": {
    "id": 55,
    "name": "orders_clean",
    "display_name": null,
    "container_type": "computed_table",
    "datastore_id": 10,
    "datastore_name": "Production DB"
  },
  "target_field": null
}
Field-level edge

Request:

curl -X POST "https://your-instance.qualytics.io/api/lineage/edges" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source_field_id": 100,
    "target_field_id": 200,
    "source_type": "manual"
  }'

Response (200): Same structure as the container-level example, with source_field and target_field populated instead.

Auto container-edge

When creating a field-level edge with source_type: "manual" and the source and target fields belong to different containers, Qualytics automatically creates a container-level edge between those containers if one does not already exist.

Request Body:

Field Type Required Description
source_container_id integer Conditional Source container ID. Mutually exclusive with source_field_id.
source_field_id integer Conditional Source field ID. Mutually exclusive with source_container_id.
target_container_id integer Conditional Target container ID. Mutually exclusive with target_field_id.
target_field_id integer Conditional Target field ID. Mutually exclusive with target_container_id.
description string No Optional transformation note (e.g. UPPER(email)).
source_type string No How the connection was created. One of manual, catalog_import, inferred. Defaults to manual.

Same-granularity rule

Exactly one of source_container_id or source_field_id must be set, and exactly one of target_container_id or target_field_id must be set. Mixing container and field on opposite sides (e.g. source_container_id + target_field_id) is not allowed and returns 422.


List Edges

Returns a paginated list of lineage edges with optional filters.

Endpoint: GET /api/lineage/edges

Role: Member

Query Parameters:

Parameter Type Description
container_id integer Filter to edges where this container is the source or target.
field_id integer Filter to edges where this field is the source or target.
source_type string Filter by source type: manual, catalog_import, or inferred (Qualytics managed).
page integer Page number (default: 1).
size integer Page size (default: 50).
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/lineage/edges?container_id=42" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response:

{
  "items": [
    {
      "id": 1,
      "created": "2026-05-01T10:00:00.000000Z",
      "source_container_id": 42,
      "source_field_id": null,
      "target_container_id": 55,
      "target_field_id": null,
      "description": "Raw orders → cleaned orders",
      "source_type": "manual",
      "source_container": { "id": 42, "name": "raw_orders", "display_name": null, "container_type": "table", "datastore_id": 10, "datastore_name": "Production DB" },
      "source_field": null,
      "target_container": { "id": 55, "name": "orders_clean", "display_name": null, "container_type": "computed_table", "datastore_id": 10, "datastore_name": "Production DB" },
      "target_field": null
    }
  ],
  "total": 1,
  "page": 1,
  "size": 50,
  "pages": 1
}

Get an Edge

Returns a single lineage edge by ID.

Endpoint: GET /api/lineage/edges/{id}

Role: Member

Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/lineage/edges/1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response (200): Same structure as Create an Edge.


Update an Edge

Updates the description and/or source_type of an existing edge. The source and target cannot be changed. Delete and recreate the edge if you need to rewire it.

Endpoint: PUT /api/lineage/edges/{id}

Role: Manager

Example request and response

Request:

curl -X PUT "https://your-instance.qualytics.io/api/lineage/edges/1" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "ETL pipeline v2",
    "source_type": "catalog_import"
  }'

Response (200): Returns the updated edge object.

Request Body:

Field Type Required Description
description string No New transformation description. Omit the field to leave the current value unchanged.
source_type string No Updated source type: manual, catalog_import, or inferred.

Delete an Edge

Deletes a lineage edge. This does not affect the underlying containers or fields.

Endpoint: DELETE /api/lineage/edges/{id}

Role: Manager

Example request
curl -X DELETE "https://your-instance.qualytics.io/api/lineage/edges/1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response: 204 No Content


Get the Lineage Graph

Returns the lineage graph for a container, including all nodes and connections up to the specified depth.

Endpoint: GET /api/lineage/graph

Role: Member

Query Parameters:

Parameter Type Required Description
container_id integer Yes The container to center the graph on.
depth integer No How many hops to include (1–20). Defaults to 2.
side string No Restrict direction: upstream, downstream, or omit for both.
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/lineage/graph?container_id=55&depth=2&side=upstream" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response:

{
  "nodes": [
    {
      "id": 42,
      "name": "raw_orders",
      "display_name": null,
      "container_type": "table",
      "table_type": "table",
      "datastore": { "id": 10, "name": "Production DB", "store_type": "jdbc", "type": "snowflake", "enrichment_only": false },
      "field_count": 12,
      "linked_field_count": 3,
      "is_target": false,
      "active_anomaly_count": 0,
      "has_upstream": false,
      "has_downstream": true
    },
    {
      "id": 55,
      "name": "orders_clean",
      "display_name": null,
      "container_type": "computed_table",
      "table_type": null,
      "datastore": { "id": 10, "name": "Production DB", "store_type": "jdbc", "type": "snowflake", "enrichment_only": false },
      "field_count": 10,
      "linked_field_count": 3,
      "is_target": true,
      "active_anomaly_count": 2,
      "has_upstream": true,
      "has_downstream": false
    }
  ],
  "edges": [
    {
      "id": 1,
      "source_container_id": 42,
      "source_field_id": null,
      "target_container_id": 55,
      "target_field_id": null,
      "description": "Raw orders → cleaned orders",
      "source_type": "manual"
    }
  ]
}

Graph depth and flags

The has_upstream and has_downstream flags on each node indicate whether that container has any connections in that direction anywhere in your deployment, regardless of the requested depth. A true flag on an edge node means connections may exist beyond the current result and you can re-query with a larger depth to explore them.


List Container Fields

Returns a paginated list of fields for a container, including whether each field has any lineage connections.

Endpoint: GET /api/lineage/containers/{container_id}/fields

Role: Member

Query Parameters:

Parameter Type Description
name string Filter fields by name (partial match).
page integer Page number (default: 1).
size integer Page size (default: 50).
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/lineage/containers/55/fields" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response:

{
  "items": [
    {
      "id": 200,
      "name": "email_upper",
      "display_name": null,
      "status": "active",
      "latest_profile_id": 301,
      "type": "string",
      "is_computed_field": true,
      "active_anomaly_count": 0,
      "has_lineage": true
    },
    {
      "id": 201,
      "name": "order_date",
      "display_name": null,
      "status": "active",
      "latest_profile_id": 302,
      "type": "date",
      "is_computed_field": false,
      "active_anomaly_count": 1,
      "has_lineage": false
    }
  ],
  "total": 10,
  "page": 1,
  "size": 50,
  "pages": 1
}

Get Field Connections

Returns all fields connected to a given field through field-level connections, along with the edges between them. Optionally restrict results to fields within specific containers.

Endpoint: GET /api/lineage/fields/{field_id}/connections

Role: Member

Query Parameters:

Parameter Type Description
container_ids list of integers Restrict results to fields within these containers. Can be repeated: ?container_ids=42&container_ids=55.
Example request and response

Request:

curl -X GET "https://your-instance.qualytics.io/api/lineage/fields/100/connections" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response:

{
  "fields": [
    {
      "id": 200,
      "name": "email_upper",
      "display_name": null,
      "status": "active",
      "latest_profile_id": 301,
      "type": "string",
      "container_id": 55
    }
  ],
  "edges": [
    {
      "id": 2,
      "source_container_id": null,
      "source_field_id": 100,
      "target_container_id": null,
      "target_field_id": 200,
      "description": "UPPER(email)",
      "source_type": "manual"
    }
  ]
}

Error Responses

Status Code Description
401 Unauthorized Missing or invalid API token.
403 Forbidden User does not have the required role, or the Lineage add-on is not available/disabled.
404 Not Found Edge, container, or field with the specified ID does not exist.
409 Conflict A lineage edge with the same source and target already exists.
422 Unprocessable Entity Invalid request body (e.g., mixed-granularity edge, missing source/target).
Error response examples

403 Forbidden (add-on not available for deployment):

{ "detail": "'lineage' is not available for your deployment. Contact support to unlock it" }

403 Forbidden (add-on disabled by admin):

{ "detail": "'lineage' is currently disabled. Ask your admin to enable it" }

404 Not Found:

{ "detail": "Lineage edge 999 not found" }

409 Conflict:

{ "detail": "A lineage edge with the same source and target already exists" }

422 Unprocessable Entity (mixed granularity):

{
  "detail": [
    {
      "type": "value_error",
      "msg": "Value error, source and target must be at the same granularity (both containers or both fields)"
    }
  ]
}

Role Summary

Operation Minimum Role
List edges Member
Get an edge Member
Get the lineage graph Member
List container fields Member
Get field connections Member
Create an edge Manager
Update an edge Manager
Delete an edge Manager