---
title: "List orchestration runs"
method: GET
path: "/v1/projects/{project_id}/orchestration-runs"
tags: ["Orchestrations"]
---

# List orchestration runs

`GET /v1/projects/{project_id}/orchestration-runs`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/orchestration-runs`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Returns orchestration runs the caller can access, optionally filtered by orchestration, by parent run, by status, or by whether the run has a parent at all.

Note when aggregating: a run's `usage` covers its whole subtree, so summing it over a list that contains both a parent and its children counts the children more than once. Pass `nested=false` to sum over runs a caller started.

## Query parameters

- `orchestration_id` string
- `parent_orchestration_run_id` string
- `nested` boolean
- `status` string[]
- `limit` integer
- `offset` integer

## Response `200`

List of runs

- object
  - `data` OrchestrationRun[], required
    - `id` string, required: Public ID (run_...).
    - `orchestration_id` string, required: Public ID of the parent orchestration.
    - `orchestration_version` integer, nullable: The orchestration version this run executes, fixed when the run started. Every later step of the run — the first drive, a wake from `sleeping`, a human or approval resume, a redrive after a crash — resolves the graph from this version, so editing the orchestration never re-shapes a run already in flight. Fetch the graph it names at `GET /v1/projects/{project_id}/orchestrations/{orchestration_id}/versions/{version}`. Null for runs created before pinning existed, which execute the live graph.
    - `project_id` string, required: Public ID of the owning project.
    - `status` 'queued' | 'running' | 'sleeping' | 'awaiting_input' | 'completed' | 'failed' | 'cancelled' | 'expired', required: Run lifecycle state. `queued` awaits a worker; `running` is actively executing; `sleeping` is parked on a delay/poll wait (no worker); `awaiting_input` is parked on a human node; `completed`/`failed`/ `cancelled` are terminal; `expired` is a wait that passed its deadline.
    - `state` object, required: Current accumulated state.
    - `active_nodes` string[], required: Node IDs currently active.
    - `artifacts` object, required: Map of node ID to output artifact.
    - `error` StoredError: Why a resource's work failed, in the shape of an error response's `error` without `hint` and `docs_url`. Every `error` field a resource records carries it, so `code` can be read without first checking the type of `error`.
      - `code` string, required: A key from the server's ERROR_CODES registry, documented on the Error Codes page. `INTERNAL_ERROR`, with the message `Internal Server Error`, records an unexpected failure whose detail reaches the server log only.
      - `message` string, required
      - `meta` object: The failure's own structured context, when it names any.
    - `trace_id` string, nullable
    - `input` object, nullable: Initial input provided at run creation.
    - `metadata` MetadataBag: Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase. No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.
    - `idempotency_key` string, nullable: The deduplication key the run was started under, unique within the project and claimed for as long as the run record exists. Null for a run started without one, which every platform-started run is.
    - `parent_orchestration_run_id` string, nullable: The run whose node started this one — set only on a child a `loop` or `sub_orchestration` node spawned, null for a run a caller started. A child is its own run with its own usage events, so this is what makes a delegated run's spend attributable to the run that ordered it.
    - `parent_node_id` string, nullable: The node within `parent_orchestration_run_id` that started this run. Null when `parent_orchestration_run_id` is null.
    - `orchestration_run_depth` integer: `loop` / `sub_orchestration` edges between this run and the run a caller started: `0` for a caller-started run, one more than its parent's for a child. Starting a child past the effective bound — the smaller of the deployment's `MAX_ORCHESTRATION_RUN_DEPTH` (default 10) and the project's `max_orchestration_run_depth` — is refused with `ORCHESTRATION_RUN_DEPTH_LIMIT`, which fails the run that tried to descend. That bounds a graph whose `sub_orchestration` node names itself, directly or through a cycle of two graphs, which the intra-graph cycle check cannot see.
    - `output` object, nullable: What a completed run returns: the orchestration's `output_mapping` evaluated over the final state, or, when it declares none, the terminal node artifacts keyed by node id. Null unless the run completed.
    - `usage` UsageTotals: Token counts and cost for one slice of the meter. Tokens and cost only: a `compute_second` or `gb_day` meter is reported by the aggregate's `components` array. The three input dimensions partition the prompt — `input_tokens` = `uncached_input_tokens` + `cached_tokens` + `cache_write_tokens` — and are separate because they are separately priced. Note that the `input_tokens` **component** on a usage event is the uncached figure alone; here `input_tokens` is the whole prompt.
      - `cost_usd` number, nullable: Sum of priced component costs; null when nothing in the slice was priced. Always null at step altitude — the ledger prices one event per generation at write time, and a price read back per step would disagree with it after any price change.
      - `input_tokens` integer: Full prompt tokens, reconstructed from the components.
      - `uncached_input_tokens` integer: Prompt tokens priced at the plain input rate — the prompt minus cache reads and cache writes.
      - `output_tokens` integer
      - `cached_tokens` integer: Prompt tokens served from the provider's prompt cache.
      - `cache_write_tokens` integer: Prompt tokens written into the provider's prompt cache.
      - `reasoning_tokens` integer: A non-billable subset of `output_tokens`, reported where the provider breaks it out.
    - `usage_own` UsageTotals: Token counts and cost for one slice of the meter. Tokens and cost only: a `compute_second` or `gb_day` meter is reported by the aggregate's `components` array. The three input dimensions partition the prompt — `input_tokens` = `uncached_input_tokens` + `cached_tokens` + `cache_write_tokens` — and are separate because they are separately priced. Note that the `input_tokens` **component** on a usage event is the uncached figure alone; here `input_tokens` is the whole prompt.
      - `cost_usd` number, nullable: Sum of priced component costs; null when nothing in the slice was priced. Always null at step altitude — the ledger prices one event per generation at write time, and a price read back per step would disagree with it after any price change.
      - `input_tokens` integer: Full prompt tokens, reconstructed from the components.
      - `uncached_input_tokens` integer: Prompt tokens priced at the plain input rate — the prompt minus cache reads and cache writes.
      - `output_tokens` integer
      - `cached_tokens` integer: Prompt tokens served from the provider's prompt cache.
      - `cache_write_tokens` integer: Prompt tokens written into the provider's prompt cache.
      - `reasoning_tokens` integer: A non-billable subset of `output_tokens`, reported where the provider breaks it out.
    - `required_action` RequiredAction: Why an awaiting_input run is parked. `node_id`, `prompt` and `context` describe a node's own pause and are present for every kind but `paused`, which is an operator pause with no node of its own and carries `reason` instead.
      - `type` 'human_input' | 'webhook_receive' | 'approval' | 'paused', required: Discriminator identifying the kind of pause. Open enum — new pause kinds may be added in minor releases; clients must tolerate unknown values.
      - `node_id` string: The node the run is parked at. Absent for a `paused` action, which no node produced.
      - `prompt` string
      - `context` object
      - `reason` string, nullable: Present for `paused` — the reason the operator gave, or null when they gave none.
      - `options` string[], nullable
      - `approval_spec` object: Present only for `approval` pauses — the frozen tool proposal the engine emits as an ApprovalItem when the run parks. Copied as a value; inner keys stay exactly as authored.
      - `approval_id` string: Present once the approval item is emitted.
      - `expires_at` string, date-time: Present for `approval` pauses — when the item expires.
    - `pause_requested_at` string, date-time, nullable: When an operator pause was requested, or null when none is in force. Independent of `status`: a `running` run keeps running until its next checkpoint, and a run parked on a node keeps that node's `required_action`.
    - `pause_reason` string, nullable: The reason supplied with the pause, when one was.
    - `started_at` string, date-time, nullable
    - `completed_at` string, date-time, nullable
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
  - `total` integer, required
  - `limit` integer, required
  - `offset` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
