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):
403 Forbidden (add-on disabled by admin):
404 Not Found:
409 Conflict:
422 Unprocessable Entity (mixed granularity):
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 |