Skip to content

OpenLineage

OpenLineage is an open standard for collecting data lineage. This guide explains how to send OpenLineage RunEvent, JobEvent, and DatasetEvent data to Guance from Airflow, Spark, dbt, or an application using an OpenLineage client.

Prerequisites

Before you begin, prepare the following:

  • The data ingestion URL for your site, such as https://openway.guance.com. Use the actual URL for other sites or private deployments.
  • The token for the target workspace. Treat the token as a secret; do not hard-code it or commit it to version control.
  • A data job or client that supports the OpenLineage HTTP transport.
  • HTTPS connectivity from the job runtime to the ingestion URL.

The examples use these placeholders:

Placeholder Description
<OPENLINEAGE_BASE_URL> Data ingestion URL without a path or query parameters
<WORKSPACE_TOKEN> Token for the target workspace

Ingestion Protocol

Item Requirement
Method POST
Path /v1/write/openlineage
Content-Type application/json; media type parameters such as charset are allowed
Content-Encoding Omit for an uncompressed body; use gzip for compression
Request body One OpenLineage RunEvent, JobEvent, or DatasetEvent JSON object
Default body limit 64 MiB; for gzip requests, the received compressed size is counted; deployments may override this value; each decoded event must be no larger than 8 MiB

The complete URL is:

<OPENLINEAGE_BASE_URL>/v1/write/openlineage

Each request accepts one event. JSON arrays and the OpenLineage batch endpoint are not supported. Keep each event well below the limit so that large facets do not interfere with delivery or retries.

Authentication

The endpoint supports these authentication methods:

Method Example Recommendation
Bearer Authorization: Bearer <WORKSPACE_TOKEN> Recommended; compatible with the OpenLineage HTTP transport api_key authentication
Header X-Token: <WORKSPACE_TOKEN> Use for clients that can set a custom header
Query ?token=<WORKSPACE_TOKEN> Compatibility only; URLs may be captured by proxies or access logs

If a request includes more than one token, the precedence is query token, X-Token, then Bearer. Do not configure different tokens in the same request.

Event Requirements

Events must follow the OpenLineage specification. All three types require eventTime (an ISO 8601 timestamp), producer (a producer URI), and schemaURL (the specification version URL). Guance identifies the type from the top-level run, job, and dataset objects:

Event type Required identity fields Purpose
RunEvent run.runId, job.namespace, job.name, eventType Describes a run and its inputs/outputs. Supported eventType values are START, RUNNING, COMPLETE, ABORT, FAIL, and OTHER.
JobEvent job.namespace and job.name; no run or dataset Describes Job metadata; may include inputs and outputs.
DatasetEvent dataset.namespace and dataset.name; no run or job Describes standalone Dataset metadata and schema/column-lineage facets.

Include inputs and outputs when a RunEvent or JobEvent has input or output datasets; each dataset should have a namespace and name. Events for the same run must keep the same runId, job.namespace, and job.name. A run normally emits START followed by a terminal COMPLETE, FAIL, or ABORT.

Event Sources and Record Types

After ingestion, source identifies the original OpenLineage event type; lineage_record_type identifies the record Guance projected from that event. These are different dimensions: one RunEvent can produce multiple records, all with source=run_event.

source Original event Possible lineage_record_type values
run_event RunEvent: run state, inputs, and outputs event, dataset_edge; also schema_version or field_edge when relevant facets exist
job_event JobEvent: static or changed Job metadata event, job, dataset_edge; also schema_version or field_edge when relevant facets exist
dataset_event DatasetEvent: standalone Dataset metadata event, dataset; also schema_version or field_edge when relevant facets exist

An event record stores the complete, redacted event JSON in lineage_message. job and dataset are entity records; dataset_edge links a Job/Run to an input or output Dataset; schema_version stores a schema summary; and field_edge represents column-level lineage. Without relevant inputs, outputs, or facets, the corresponding derived records are not created. Rejected-event audit records may use source=unknown_event and lineage_record_type=ingest_error; this is not a fourth supported input type.

Guance reserves source for event classification. If the original top-level JSON contains a string source, projected records preserve it as src_source instead of replacing Guance's source. It also remains in the event record's lineage_message.

Queryable Field Inventory

The following are top-level fields extracted by Guance, not every field in the original OpenLineage JSON. Presence depends on the event type, record type, and available facets; an absent field is not necessarily an empty value. For the complete event, inspect lineage_message on a lineage_record_type=event record (sensitive values may be redacted). Use event_id to relate records projected from the same event; run_id, job_id, and dataset_id relate runs, jobs, and datasets respectively.

Common Fields and Event Payload

Field Meaning
__namespace Always lineage; corresponds to the LN DQL namespace
source Event classification: run_event, job_event, or dataset_event; unknown_event for rejected-event records
src_source Original top-level string source, if present
lineage_record_type Projected record type: event, job, dataset, dataset_edge, schema_version, field_edge, or ingest_error
event_id Event identifier derived from the original content within a workspace; shared by records projected from the same event
date, date_ns Original eventTime as Unix milliseconds and nanoseconds; queries use this time
create_time When Guance received the event, in Unix milliseconds
__docid Storage ID for an individual record; not an entity deduplication key
projection_version, normalization_version Guance projection format and Dataset identity normalization versions
lineage_message Complete redacted event JSON; only on event records
producer, schema_url Original producer and schemaURL; primarily on event, also on job and dataset records
ingest_delay_ms Difference between receipt time and eventTime, in milliseconds; only on event
facet_names Names of facets included in the event; only on event
redaction_policy_version, redacted_count Redaction policy version and number of redactions; only on event
ingest_warning Degradation warning codes for optional facet parsing or projection limits; only on event

Run and Job Fields

Identity fields propagate to records derived from the same event. Job names, state, and facet-extracted fields are mainly on event; Job metadata from a JobEvent is also on the job record.

Field Meaning
event_type, run_id RunEvent status and original run.runId
job_id Stable Job identifier Guance derives from the workspace, job.namespace, and job.name
job_namespace, job_name Original Job identity; only on event and job records
parent_run_id, root_run_id Parent and root run IDs from the parent Run facet
parent_job_id, parent_job_namespace, parent_job_name Parent Job identity and name
root_job_id, root_job_namespace, root_job_name Root Job identity and name
is_terminal, is_success, has_error Whether terminal, COMPLETE, or failed/aborted/contains an error facet
error_type Recognizable explicit error type from an error facet; not the full error message
error_message, error_language, stacktrace_hash Redacted error message, programming language, and stack trace digest
input_count, output_count Counts of original input and output datasets
integration, job_type, processing_type Job jobType facet
engine_name, engine_version, openlineage_adapter_version Run processing_engine facet
nominal_start_time, nominal_end_time Nominal run start and end times, in Unix milliseconds
has_sql, sql_hash Whether an SQL facet exists and its digest; full SQL is not exposed here
has_config, config_hash Whether a configuration facet exists and its digest
application_id, spark_master, deploy_mode, driver_host, spark_user Spark application details when the relevant facet exists
trace_ids, trace_links Optional trace association IDs and link details

Dataset, Schema, and Lineage Edge Fields

Only DatasetEvent produces a dataset record. RunEvent and JobEvent produce dataset_edge records from their inputs and outputs. schema_version holds a schema summary; the complete schema remains in the original facet inside lineage_message.

Field Meaning
dataset_id Stable identifier Guance derives from the workspace and normalized Dataset identity
dataset_namespace, dataset_name Original Dataset namespace and name
dataset_key Normalized Dataset identity key
dataset_type, data_platform Inferred Dataset type and data platform
account_name, database_name, schema_name, table_name Hierarchy names parsed from the Dataset identity, when available
data_source_name, data_source_uri Dataset dataSource facet: the specific data source, distinct from data_platform
lifecycle_state_change Dataset lifecycle-change state
io_type Direction of a dataset_edge or related schema_version: INPUT / OUTPUT
row_count, size_bytes, file_count Row, byte, and file counts from input/output statistics facets
schema_hash, schema_field_count Schema field-list digest and count; may appear on Dataset, edge, and schema-version records
run_dataset_edge_id, job_dataset_edge_id Stable identifiers for Run/Job-to-Dataset input/output relationships
observed_at Observation time on job, dataset, and schema_version records, in Unix milliseconds
input_dataset_id, input_field Upstream Dataset and column on a field_edge
output_dataset_id, output_field Downstream Dataset and column on a field_edge
field_edge_id Stable identifier of a column-lineage relationship
transformation_type, transformation_subtype, transformation_hash Column transformation type, subtype, and digest
is_masking Whether the column transformation masks data

ingest_error records additionally have ingest_error_reason_code (rejection reason), ingest_error_payload_hash (payload digest), and ingest_error_payload_size (payload size in bytes). They never store the rejected raw payload.

Quick Verification

Save the following as event.json, and generate a new runId for each test:

{
  "eventType": "START",
  "eventTime": "2026-10-21T08:00:00Z",
  "run": {
    "runId": "4a8f90a7-79c0-4f80-89e7-6b6c5d4fc79a"
  },
  "job": {
    "namespace": "example.scheduler",
    "name": "daily_orders"
  },
  "inputs": [
    {
      "namespace": "postgresql://warehouse.example:5432",
      "name": "sales.orders"
    }
  ],
  "outputs": [
    {
      "namespace": "postgresql://warehouse.example:5432",
      "name": "analytics.daily_orders"
    }
  ],
  "producer": "https://example.com/openlineage/integration",
  "schemaURL": "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/RunEvent"
}

Send the event with a Bearer token:

curl --fail-with-body --silent --show-error \
  -X POST '<OPENLINEAGE_BASE_URL>/v1/write/openlineage' \
  -H 'Authorization: Bearer <WORKSPACE_TOKEN>' \
  -H 'Content-Type: application/json' \
  --data-binary @event.json

Send a gzip-compressed event:

gzip -c event.json | curl --fail-with-body --silent --show-error \
  -X POST '<OPENLINEAGE_BASE_URL>/v1/write/openlineage' \
  -H 'Authorization: Bearer <WORKSPACE_TOKEN>' \
  -H 'Content-Type: application/json' \
  -H 'Content-Encoding: gzip' \
  --data-binary @-

After a successful request, send a COMPLETE event with the same runId and job identity to verify a complete run lifecycle.

Client Configuration

OpenLineage Python Client

The OpenLineage Python Client uses the api_key setting to generate a Bearer header. The following configuration also enables gzip and retries for transient errors:

export OPENLINEAGE__TRANSPORT__TYPE=http
export OPENLINEAGE__TRANSPORT__URL='<OPENLINEAGE_BASE_URL>'
export OPENLINEAGE__TRANSPORT__ENDPOINT='/v1/write/openlineage'
export OPENLINEAGE__TRANSPORT__AUTH__TYPE=api_key
export OPENLINEAGE__TRANSPORT__AUTH__APIKEY='<WORKSPACE_TOKEN>'
export OPENLINEAGE__TRANSPORT__COMPRESSION=gzip
export OPENLINEAGE__TRANSPORT__RETRY='{"total":5,"connect":5,"read":5,"backoff_factor":0.3,"status_forcelist":[429,500,502,503,504],"allowed_methods":["POST"]}'

Configuration support varies between client versions. Check the Python Client configuration reference before upgrading.

Apache Airflow

Install an OpenLineage Provider version compatible with your Airflow release, then configure the HTTP transport:

pip install apache-airflow-providers-openlineage

export AIRFLOW__OPENLINEAGE__NAMESPACE='production-airflow'
export AIRFLOW__OPENLINEAGE__TRANSPORT='{"type":"http","url":"<OPENLINEAGE_BASE_URL>","endpoint":"/v1/write/openlineage","auth":{"type":"api_key","apiKey":"<WORKSPACE_TOKEN>"},"compression":"gzip"}'

The Provider has specific Airflow version requirements. See the Airflow OpenLineage Provider documentation before installing or upgrading it.

Apache Spark

Load an OpenLineage Spark integration package compatible with your Spark release, then set these properties:

spark.extraListeners=io.openlineage.spark.agent.OpenLineageSparkListener
spark.openlineage.transport.type=http
spark.openlineage.transport.url=<OPENLINEAGE_BASE_URL>
spark.openlineage.transport.endpoint=/v1/write/openlineage
spark.openlineage.transport.auth.type=api_key
spark.openlineage.transport.auth.apiKey=<WORKSPACE_TOKEN>
spark.openlineage.transport.compression=gzip
spark.openlineage.namespace=production-spark

For package loading and version compatibility, see the OpenLineage Spark installation guide.

dbt

Install openlineage-dbt, reuse the Python Client transport settings, and run dbt-ol instead of dbt:

pip install openlineage-dbt

export OPENLINEAGE__TRANSPORT__TYPE=http
export OPENLINEAGE__TRANSPORT__URL='<OPENLINEAGE_BASE_URL>'
export OPENLINEAGE__TRANSPORT__ENDPOINT='/v1/write/openlineage'
export OPENLINEAGE__TRANSPORT__AUTH__TYPE=api_key
export OPENLINEAGE__TRANSPORT__AUTH__APIKEY='<WORKSPACE_TOKEN>'
export OPENLINEAGE__TRANSPORT__COMPRESSION=gzip
export OPENLINEAGE_NAMESPACE='production-dbt'

dbt-ol run

Do not set only OPENLINEAGE_URL: clients normally default to /api/v1/lineage, while this integration requires an explicit /v1/write/openlineage endpoint. See the OpenLineage dbt integration guide for execution modes and supported adapters.

Responses and Retries

Response Meaning Recommended action
2xx The request was accepted Do not retry
200 with error_code set to dataway.cached The request was accepted for later delivery; it may not be queryable yet Do not retry immediately; check visibility later
400 or 422 The JSON or OpenLineage event is invalid Correct the event before sending it again
401 or 403 The token is missing, invalid, or unauthorized Correct authentication before sending it again
413 The request body exceeds the limit Reduce the facet or event size
415 Content-Type or Content-Encoding is unsupported Correct the headers or compression method
429, 500, 502, 503, or 504 Temporary unavailability, throttling, or a service error Honor Retry-After; otherwise use exponential backoff with jitter

Senders must tolerate duplicate delivery and keep a stable runId for the same run. Do not retry deterministic 4xx errors, because invalid events can otherwise remain in the sending queue indefinitely.

Verification and Troubleshooting

After configuration, verify the integration in this order:

  1. Send a minimal START event with curl and confirm a 2xx response.
  2. Send a terminal event with the same run and job identity.
  3. In Guance, search for the record by job namespace, job name, or runId.
  4. Check producer logs for repeated retries, authentication errors, or serialization errors.

Common issues:

Symptom What to check
401 or 403 The token belongs to the target workspace; the authorization value starts with Bearer followed by one space; environment variables are injected correctly
404 The endpoint is explicitly /v1/write/openlineage, not the client default /api/v1/lineage
413 The event or a facet is too large; gzip reduces transfer size, but the event should still be reduced
415 Content-Type is JSON; compressed requests also set Content-Encoding: gzip
429 or 503 The sender honors Retry-After; reduce concurrency and add exponential backoff with jitter
The request succeeds but data is not immediately visible Check for dataway.cached; verify the job namespace/name and runId; wait and check again
A run has START but no terminal state The producer emits COMPLETE, FAIL, or ABORT on its success, failure, and cancellation paths

Security Recommendations

  • Use HTTPS in production and keep certificate verification enabled.
  • Prefer Bearer or X-Token; avoid putting tokens in URLs.
  • Inject tokens through a Secret, secret manager, or runtime environment. Do not put them in images, configuration templates, shell history, or logs.
  • Rotate tokens regularly and revoke an exposed token immediately.
  • Facets can contain SQL, schemas, or business metadata. Review sensitive content and collect only what is necessary.

References

Feedback

Is this page helpful?