> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eigenpal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflows

> Directed graphs of typed steps that automate a repetitive task.

A **workflow** is a directed acyclic graph of steps connected by edges. Each
step has a type (for example `ai.parse` or `transform.script`), a typed config,
and a typed output. The engine topologically sorts the graph and runs each step
through its registered processor, passing outputs forward along the edges.

```
Workflow {
  steps        # the nodes, each a typed step
  edges        # data dependencies between steps
  entryStepId  # where execution starts
  outputStepId # whose output becomes the run result
}
```

## Steps

Steps come in four categories:

* **AI steps** (`ai.*`), model-backed parsing, extraction, classification, and
  splitting.
* **Transform steps** (`transform.*`), deterministic data shaping: scripts,
  templates, merges, regex extraction, and format conversions.
* **Action steps** (`action.*`), external side effects such as HTTP requests
  and invoking other workflows.
* **Control steps** (`control.*`), flow control: conditions, loops, parallel
  fan-out, and typed failure.

Every step type is documented under [Workflow steps](/concepts/steps-overview),
one page each, with its configuration and output schema rendered straight from
the code.

## Identifiers

Workflows have both a stable id and a slug:

* `wf_...` is the workflow id returned by create/list APIs.
* `workflows.<slug>` is the typed target string used by run commands, SDKs, and
  API calls.

Most CLI commands accept either form. Prefer `workflows.<slug>` in examples and
application code because it is readable, and use `wf_...` when a response gives
you an id or when scripts need a stable database-backed identifier.

## Referencing step output

Downstream steps read upstream output with template expressions:

```
{{ steps.<step-name>.output.<field> }}
```

The `.output` segment is required because each step result is stored as
`{ output, status }`. Scalar inputs to the workflow are available as
`{{ input.<name> }}`.

## Retries

Durable retries are off by default. In Studio, open **Workflow settings →
Retries** to set the workflow default, then use the **Retry** control on an
eligible step to inherit that default, retry automatically, or never retry.
Studio supports two or three total attempts, including the first attempt.

The same settings can be authored in workflow YAML:

```yaml theme={null}
settings:
  retry:
    mode: automatic
    maxAttempts: 3

steps:
  - name: fetch-catalog
    type: action.http
    retry: inherit
    with:
      method: GET
      url: 'https://api.example.com/catalog'

  - name: read-product-page
    type: action.website-reader
    retry: never
    with:
      url: '{{ input.url }}'
```

At workflow level, use `automatic` or `never`. At step level, `inherit` uses
the workflow default, `automatic` opts in, and `never` opts out. `automatic`
can also include `maxAttempts` in object form, such as
`{ mode: automatic, maxAttempts: 3 }`. `never` may use `{ mode: never }`;
`inherit` is string-only. The schema accepts up to 10 attempts, but the
current worker ceiling is three.

Automatic retries apply only to [HTTP Request](/steps/action/http) steps using
`GET` or `HEAD`, and to [Website Reader](/steps/action/website-reader). They
cover transient timeouts, rate limits, and selected retryable server failures.
Retry delays use bounded exponential backoff, honor `Retry-After`, and stop
after a five-minute elapsed budget.

Other HTTP methods are not replayed because they may have side effects. Invoke
Workflow, AI steps, and transforms (including steps that write files) are not
durably retried. Control containers are not attempts
themselves, but eligible leaves inside sequential If, Switch, and For Each
scopes can retry. Concurrent Parallel and Parallel Map branches do not retry
durably. AI providers may perform their own request retries; those are separate
from durable workflow retries.

Legacy whole-run retry counts are still accepted when reading older workflow
definitions, but they no longer restart failed runs. Move retry intent to the
workflow retry default or an eligible step.

## Runs

Executing a workflow creates a **run**. Runs are claimed from a queue by
workers, carry a status (`created`, `queued`, `running`, `completed`, `failed`,
`cancelled`), and store their inputs and per-step outputs. Inputs are frozen
once a run leaves the `created` state. To change inputs after enqueue, start a
new run.

## Evaluating workflows

Workflows are tested like code: you attach a **dataset** of examples (inputs
plus expected outputs) and run **evaluators** (exact-diff, LLM-judge, or a
custom script) to score each run. The `eigenpal workflow` CLI commands push
datasets and evaluators, run experiments, and compare results.
