Pipelines Guide

This guide describes how to record pipeline executions in ADOC with acceldata-sdk-python.

Introduction

ADOC treats data pipelines as first-class observability objects. Each definition captures how work is structured (assets and jobs), and each run captures what happened during a given execution. acceldata-sdk-python creates those definitions, opens runs, attaches hierarchical spans, emits metrics and custom events, and closes runs with a clear outcome, so the Catalog and Pipelines views in ADOC stay aligned with your orchestration code.

Core Concepts

Concept

What It Represents

Pipeline

The durable definition of an ETL or processing flow: jobs, inputs and outputs, and how they connect. ADOC uses this definition to reason about lineage across versions.

Pipeline run

One execution of that definition. The same pipeline can run many times; each run gets its own identity and a span tree.

Job

A unit of work inside a run. Inputs and outputs can point at catalog assets or at other jobs. You can optionally create a job together with a dedicated span for tighter timing and failure correlation.

Span

A hierarchical node under a run used to group steps and metrics. Child spans model nested phases without losing the parent context.

Span events

Structured signals attached to a span (GenericEvent, LogEvent, and lifecycle transitions). These record row counts, timings, checkpoints, or domain-specific markers during execution.

Pipeline APIs focus on orchestration observability: definitions, runs, spans, jobs, and events. Catalog APIs focus on what is stored and how it is profiled or crawled. Most production setups use both: you register and profile assets through catalog calls, while pipeline calls describe how those assets are produced or consumed in each batch or stream.

The rest of this guide is organized by topic: definition, runs, spans, jobs, events, completion, and retrieval. A full execution typically defines or loads a pipeline, opens a run, records spans and jobs, emits events as work proceeds, and then updates the run to a terminal state.

Creating a Pipeline

Use CreatePipelineInputRequest to define and create a pipeline in ADOC. This request includes required identifiers along with several optional fields for metadata and organization.

Key Fields

Field

Description

uid

Unique identifier for the pipeline.

name

Display name of the pipeline.

description (optional)

Human-readable description of the pipeline.

meta (optional)

Additional metadata, such as owner, team, or code location.

tags (optional)

List of tags used for discovery and categorization in ADOC.

Tags (Optional)

Use the tags field to attach metadata for discovery and display in the ADOC UI.

Property

Description

tags

A list of Tag objects. Each tag includes name (a stable identifier, typically in key:value format) and displayName (a human-readable label shown in the UI).

If you don't provide tags, ADOC creates the pipeline without any tags.

from acceldata.client.adoc_client import AdocClient from acceldata.models.api.pipeline.meta import Meta from acceldata.models.api.pipeline.tag import Tag from acceldata.models.sdk.pipeline.create_pipeline_input_request import ( CreatePipelineInputRequest, ) client = AdocClient( url="https://<your-adoc-url>", access_key="<your-access-key>", secret_key="<your-secret-key>", ) pipeline_resource = client.create_pipeline( CreatePipelineInputRequest( uid="e2e_customers_pipeline", name="E2E customers pipeline", description="End-to-end pipeline lifecycle example", meta=Meta(owner="data-team", team="analytics", codeLocation="..."), tags=[ Tag(name="env:prod", displayName="Environment: Production"), Tag(name="domain:finance", displayName="Domain: Finance"), ], ) )

Replacing Tags

from acceldata.models.api.pipeline.tag import Tag # Replacing tags: pipeline_resource = pipeline_resource.replace_tags( [ Tag(name="env:stage", displayName="Environment: Staging"), Tag(name="owner:data-platform", displayName="Owner: Data Platform"), ] )

Pipeline Runs

run_resource = pipeline_resource.create_pipeline_run() LOGGER.info("Started pipeline run id=%s", run_resource.id)

PipelineRunResource exposes helpers such as create_root_span(), get_root_span(), and update_pipeline_run().

Spans: Root Span and Hierarchy

Creating a root span automatically initializes it with the current timestamp. You can attach additional metadata to the span by sending events.

root_span_resource = run_resource.create_root_span(uid="snippet_customers_root") root_span_resource.send_event(LogEvent("Run started", context_data={"run_id": run_resource.id}))

Child Spans (SpanResource.create_child_span)

Argument

Description

uid

Unique child span ID (required, positional).

associated_job_uids

Optional list of job UIDs to attach to the child span.

with_explicit_time, created_at, updated_at

Same semantics as top-level span creation, used for backfills.

Span Helpers and Run Completion

SpanResource exposes is_root(), has_children(), end(), abort(), and failed() to mirror orchestration state. Use GenericEvent for domain metrics and LogEvent for log lines. On failure, prefer failed() or abort() over end(). Close the root span and the run when the work finishes.

from datetime import datetime from acceldata.models.sdk.pipeline.events.generic_event import GenericEvent from acceldata.models.sdk.pipeline.pipeline_run_input import ( PipelineRunResult, PipelineRunStatus, ) pipeline_run = pipeline_resource.create_pipeline_run() # get_root_span() returns the run root when present root_span = run_resource.get_root_span() # is_root returns True if the span is root, otherwise False is_root = bool(root_span.is_root()) # has_children returns True if the span has children, otherwise False has_children = bool(root_span.has_children()) span_ctx = run_resource.create_root_span(uid="monthly.generate.data.span") span_ctx.start() child_span = span_ctx.create_child_span("monthly.generate.customer.span") child_span.send_event( GenericEvent( event_uid="order.customer.join.result", context_data={"client_time": str(datetime.now()), "row_count": 100}, ) ) child_span.end() span_ctx.end() run_resource.update_pipeline_run( context_data={"name": "backend", "key1": "value2"}, result=PipelineRunResult.SUCCESS, status=PipelineRunStatus.COMPLETED, )

Jobs

pipeline.create_job and pipeline_run.create_job perform the same operation; only the run binding differs. On PipelineResource, CreateJobInput.pipeline_run_id is required. On PipelineRunResource, it is optional and defaults to that run's ID (when you set it, it must match).


pipeline.create_job(CreateJobInput(...))

pipeline_run.create_job(CreateJobInput(...))

Run binding

pipeline_run_id is required.

pipeline_run_id is optional (defaults to this run; must match when set).

Typical context

A PipelineResource and an explicit run ID are available.

pipeline_run is already available from create_pipeline_run() or a fetch.

CreateJobInput Fields

Field

Description

uid

Unique job ID for the run (required).

name

Human-readable name (required).

description

Longer explanation of the job.

inputs / outputs

Lists of JobInputOutputRef. Reference the UID of an asset with asset_uid, or the UID of another job with job_uid.

meta

Structured ownership or source metadata (Meta).

context

Arbitrary key/value bag stored with the job.

pipeline_run_id

Run ID. Mandatory on pipeline.create_job, optional on pipeline_run.create_job.

bounded_by_span

When True, the SDK also creates a span under the root span bound to this job. If a root span is absent, the SDK creates one first and binds the job to a span under it. Default: False.

span_uid

Required when bounded_by_span=True; must be unique within the run.

with_explicit_time

For backfills. Set alongside the historical span and job guidance so timestamps are explicit.

span_created_at / span_updated_at

Optional span row timestamps, used when bounded_by_span=True and you control timestamps explicitly.

Example Usage

from acceldata.models.api.pipeline.meta import Meta from acceldata.models.sdk.pipeline.create_job_input import CreateJobInput, JobInputOutputRef node_from_pipeline = pipeline_resource.create_job( CreateJobInput( uid="monthly_sales_aggregate", name="Monthly sales aggregate", description="Builds monthly aggregates for reporting.", pipeline_run_id=run_resource.id, inputs=[JobInputOutputRef(asset_uid="warehouse_ds.db.schema.source_table")], outputs=[JobInputOutputRef(job_uid="downstream_job_uid")], meta=Meta(owner="analytics", team="finance", code_location="..."), context={"batch": "2026-04"}, bounded_by_span=True, span_uid="monthly_sales_aggregate_span", ) ) node_from_run = run_resource.create_job( CreateJobInput( uid="monthly_sales_aggregate_r", name="Monthly sales aggregate", description="Same job with a shorter call site when the run object is already available.", inputs=[JobInputOutputRef(asset_uid="warehouse_ds.db.schema.source_table")], outputs=[JobInputOutputRef(job_uid="downstream_job_uid")], meta=Meta(owner="analytics", team="finance", code_location="..."), context={"batch": "2026-04"}, bounded_by_span=True, span_uid="monthly_sales_aggregate_r_span", ) )

Use bounded_by_span=True to create a job and its span together, then fetch the span by UID.

from acceldata.models.api.pipeline.meta import Meta from acceldata.models.sdk.pipeline.create_job_input import CreateJobInput, JobInputOutputRef from acceldata.models.sdk.pipeline.events.generic_event import GenericEvent job_node = run_resource.create_job( CreateJobInput( uid="build_reports", name="Build reports", description="Materializes monthly reporting tables from the curated layer.", pipeline_run_id=run_resource.id, bounded_by_span=True, with_explicit_time=False, span_uid="build_reports_span", inputs=[JobInputOutputRef(asset_uid="snowflake_ds.raw.input_table")], outputs=[JobInputOutputRef(asset_uid="snowflake_ds.curated.output_table")], meta=Meta(owner="data-team", team="analytics", code_location="..."), context={"batch_id": "2026-04", "downstream": "looker"}, ) ) job_span = run_resource.get_span("build_reports_span") # Span creation automatically starts the span. job_span.send_event( GenericEvent( event_uid="build_reports_started", context_data={"job_uid": job_node.uid}, ) )

Alternative with bounded_by_span=False — create the span explicitly and associate the job UID:

from acceldata.models.api.pipeline.meta import Meta from acceldata.models.sdk.pipeline.create_job_input import CreateJobInput, JobInputOutputRef from acceldata.models.sdk.pipeline.events.generic_event import GenericEvent manual_job = run_resource.create_job( CreateJobInput( uid="build_reports_manual", name="Build reports manual span binding", description="Same job pattern with a child span created explicitly under the root span.", pipeline_run_id=run_resource.id, bounded_by_span=False, with_explicit_time=False, inputs=[JobInputOutputRef(asset_uid="s3_ds.staging.customers_raw_snapshot")], outputs=[JobInputOutputRef(asset_uid="snowflake_ds.warehouse.customers_curated")], meta=Meta(owner="data-team", team="analytics", code_location="..."), context={"bind_mode": "manual_span"}, ) ) root_span = run_resource.get_root_span() # Span creation automatically starts the span. manual_job_span = root_span.create_child_span( uid="build_reports_manual_span", associated_job_uids=[manual_job.uid], ) manual_job_span.send_event( GenericEvent( event_uid="manual_job_started", context_data={"job_uid": manual_job.uid}, ) ) manual_job_span.end()

Set asset_uid on JobInputOutputRef(asset_uid=...) by copying the value from the ADOC UI.

Child Spans Under a Job

Child spans under a job span provide phase-level tracking (extract, transform, publish, and so on) while keeping the job as the parent context.

# When this block runs alone, load the bounded job span first. job_span = run_resource.get_span("build_reports_span") job_span.start() # Span creation automatically starts the span. extract_span = job_span.create_child_span( uid="build_reports_extract_span", associated_job_uids=[job_node.uid], ) extract_span.send_event( GenericEvent( event_uid="extract_phase_started", context_data={"step": "source_extract"}, ) ) extract_span.end() # Span creation automatically starts the span. publish_span = job_span.create_child_span( uid="build_reports_publish_span", associated_job_uids=[job_node.uid], ) publish_span.send_event( GenericEvent( event_uid="publish_phase_started", context_data={"step": "warehouse_publish"}, ) ) publish_span.end() job_span.end()

Span Events

LogEvent carries log-style text. GenericEvent carries a stable event_uid plus arbitrary context_data for domain metrics or checkpoints.

root_span_resource.send_event(LogEvent("Started report generation", context_data={"batch": "2026-04"})) root_span_resource.send_event( GenericEvent( event_uid="reports_generated", context_data={"tables_created": 4}, ) )

Ending Spans

Call end() when the span's work finishes normally. Use failed() or abort() to record an error or cancellation instead of a clean completion.

root_span_resource.end(context_data={"dag_status": "SUCCESS"})

Completing the Pipeline Run

from acceldata.models.sdk.pipeline.pipeline_run_input import ( PipelineRunResult, PipelineRunStatus, ) run_resource.update_pipeline_run( result=PipelineRunResult.SUCCESS, status=PipelineRunStatus.COMPLETED, )

update_pipeline_run() returns the same PipelineRunResource with an updated payload. It accepts both result and status:

  • status is the current lifecycle state of the run (for example, STARTED, COMPLETED, FAILED, ABORTED).

  • result is the final business outcome (for example, SUCCESS or FAILURE) and applies when the run is terminal.

  • When you omit both, the SDK defaults to result=SUCCESS and status=COMPLETED.

While work is still in flight, keep status=STARTED and typically result=RUNNING. When the run succeeds, set status=COMPLETED and result=SUCCESS. On failure, use status=FAILED with result=FAILURE.

Retrieve and Manage Existing Pipelines and Runs

Resolve a Pipeline (client.get_pipeline)

pipeline_identity is the string the server accepts to locate a single pipeline — typically the pipeline UID from creation, or the pipeline ID as a string when only the numeric key is available. The returned PipelineResource exposes create_pipeline_run, get_latest_pipeline_run, get_runs, and create_job (with an explicit pipeline_run_id on the input).

pipeline_resource = client.get_pipeline("monthly_reporting_pipeline")

Get Latest Pipeline Run

Parameter

Description

pipeline_run_id

Run ID of the pipeline run.

continuation_id

Continuation ID of the pipeline run.

pipeline_id

ID of the pipeline the run belongs to.

pipeline_resource = client.get_pipeline("monthly_reporting_pipeline") pipeline_run = pipeline_resource.get_latest_pipeline_run() pipeline_run = client.get_pipeline_run(pipeline_run_id=run_resource.id) pipeline_run = client.get_pipeline_run( continuation_id="demo_do_not_end_pipeline_run_8", pipeline_id=pipeline_resource.id, ) pipeline_run = pipeline_resource.get_run(continuation_id="demo_do_not_end_pipeline_run_8")

Get Pipeline Details for a Run

pipeline_details = run_resource.get_details()

Get All Spans for a Run

pipeline_run_spans = run_resource.get_spans()

Get Runs for a Pipeline

runs = client.get_pipeline_runs(pipeline_resource.id) runs = pipeline_resource.get_runs()

Get All Pipelines

get_pipelines() returns a listing object. Iterate pipelines for each row, including pipeline_summary with uid and name.

listing = client.get_pipelines() for row in listing.pipelines: print(row.pipeline_summary.uid, row.pipeline_summary.name)

Delete a Pipeline

delete_response = pipeline_resource.delete()

Common Mistakes

Jobs and span events require an active run and open spans. Creating jobs before a run exists, emitting events after a span has ended, or reusing job or span UIDs within the same run all produce confusing failures. Leaving the pipeline run without a terminal update_pipeline_run call also leaves ADOC with an ambiguous outcome.

What's Next

After you complete this section, explore:

  • End-to-End Pipeline Lifecycle Example Using acceldata-sdk-python – See a complete script that creates a pipeline, runs it, and closes it out.

  • Pipeline continuation ID (multi-script runs) – Learn how multiple scripts or services can update the same pipeline run.

  • Historical pipeline instrumentation with explicit times – Learn how to backfill past pipeline executions with explicit timestamps.

  • Datasources and Assets Guide – Learn how job inputs and outputs reference catalog assets.