Skip to content

Steps ​

A step is one unit of work in a pipeline. Every step does exactly one thing: it takes some inputs, runs an action, and produces outputs. Think of each step as a single well-named function call in your workflow.

The simplest step:

yaml
- name: summarize
  action: ai
  prompt: "Summarize this: {{ input.text }}"

That's it. A name, an action, and something to do.

Step fields ​

Every step can have these fields:

FieldRequiredDescription
nameyesUnique identifier — becomes the reference name for outputs
actionyesWhat to do — ai, code, etc. (see Actions)
promptdependsFor ai steps: the prompt to send
inputnoData to pass to the action
outputnoOverride the default output variable name
confignoStep-level config overrides (model, temperature, timeout)
on_errornoWhat to do if this step fails

How data flows ​

Data flows forward through double-brace template references. A step's name becomes the namespace for its outputs.

Start with a single step:

yaml
- name: classify
  action: ai
  prompt: "Classify this support ticket: {{ input.message }}"

After this step runs, you can reference classify.text in any later step.

Now chain two steps together:

yaml
- name: classify
  action: ai
  prompt: "Classify this ticket as 'bug', 'feature', or 'question': {{ input.message }}"

- name: respond
  action: ai
  prompt: |
    The ticket was classified as: {{ classify.text }}
    Now write a helpful response to: {{ input.message }}

The respond step sees classify.text — the output of the first step. Data flows forward automatically.

Name steps after what they produce

A step named classify produces classify.text. A step named summarize produces summarize.text. Name steps after what they do and the data flow reads naturally.

Referencing step outputs ​

Every step produces output under its name. The exact fields depend on the action:

ActionDefault output fieldExample reference
ai (text response).textsummarize.text
ai with output_schema.dataextract.data.vendor
codecustom (you declare them)fetch.body

Step ordering ​

Steps run sequentially by default — in the order they appear in the YAML file.

yaml
steps:
  - name: step_one    # runs first
    action: ai
    prompt: "..."

  - name: step_two    # runs second, can reference {{ step_one.text }}
    action: ai
    prompt: "Based on: {{ step_one.text }}, ..."

The runtime determines execution order from data dependencies: if step_two references step_one's output, step_one must finish first. JigSpec handles this automatically.

Step-level config overrides Implemented ​

You can override pipeline defaults for individual steps:

yaml
- name: creative_write
  action: ai
  prompt: "Write a poem about {{ input.topic }}"
  config:
    model: anthropic/claude-sonnet-4-5
    temperature: 0.9   # high creativity for this step

- name: extract_facts
  action: ai
  prompt: "Extract key facts from: {{ input.text }}"
  config:
    temperature: 0.1   # low temperature for factual extraction

Step-level config overrides the pipeline-level default for that step only. Everything else inherits from pipeline.config.

Input field ​

Some actions — like code — take an explicit input map:

yaml
- name: process
  action: code
  input:
    url: "{{ input.url }}"
    token: "{{ secrets.API_TOKEN }}"
  run: |
    const resp = await fetch(input.url, {
      headers: { Authorization: `Bearer ${input.token}` }
    })
    return { body: await resp.text() }
  outputs:
    - body

The input map lets you explicitly define what the step receives, making steps self-documenting and easier to test.

Step names must be unique

Each step name must be unique within a pipeline. The name becomes the data reference namespace — duplicate names cause undefined behavior.