Documentation

    Getting started

Workflow DSL reference

This reference describes the workflow YAML you see in the YAML tab of the workflow editor. It matches what the canvas saves and what the runtime executes. Read Concepts first for terminology; use this page when you author or edit YAML by hand.
YAML tab showing the full workflow definition — document header, inputs, agents, and steps
YAML tab showing the full workflow definition — document header, inputs, agents, and steps

Document structure

A workflow file is a single YAML mapping. The editor always includes a document header and a steps mapping. Everything else is optional.

document

Scroll horizontally to compare
FieldTypeDescription
dslstringDSL version. Use 1.0.0.
namestringWorkflow name (shown in the UI).
versionstringDraft version label; commonly 0.1.0.

Top-level fields

Scroll horizontally to compare
FieldTypeDescription
documentmappingRequired. Metadata above.
agentsmappingOptional. Declares agent names referenced in steps. Values are often empty objects {} or resolved resource URIs after publish.
functionsmappingOptional. Same pattern for function names used in steps.
mcp_serversmappingOptional. Same pattern for MCP server names.
secretsmappingOptional. Workflow-wide secret bindings (parameter_name: "$secret:<id>").
inputmappingOptional. Input schema for the workflow (see Workflow inputs).
chat_hintsstringOptional. Hints surfaced in chat-style runs.
stepsmappingRequired. Named steps (see Steps).
outputmappingOptional. Named results built from expressions (see Workflow output).

Workflow inputs

The input block defines fields your run provides. Each field is either a shorthand string (the type name) or a mapping.
Scroll horizontally to compare
FieldTypeDescription
typestringField type (for example string, number, boolean).
requiredbooleanDefault true. Set to false to allow omission.
descriptionstringHuman-readable help.
defaultanyUsed when the field is missing (if not required).
yaml
document:
  dsl: "1.0.0"
  name: Example
  version: "0.1.0"
input:
  customer_message:
    type: string
    required: true
    description: Incoming user message
  priority:
    type: string
    required: false
    default: "normal"
steps:
  ready:
    call: conditional
    condition: "${ true }"
The human-readable title is document.name. There is no separate workflow description field in this YAML.

Workflow output

If you omit output, the runtime uses the last step in execution order as the workflow result. To pin a specific shape, set output to a mapping of names → expressions (see Expressions):
yaml
output:
  result: "${ steps.final_step.output }"
Values are evaluated the same way as step fields using ${ ... } expressions.

Steps

Each key under steps is a step name (use letters, digits, underscores). The value is a step definition: always a call field plus type-specific fields.

Common fields (all step types)

Scroll horizontally to compare
FieldTypeDescription
callstringStep kind: agent, function, mcp_tool, connector, conditional, for_each, while, parallel_analyzer, handoff_router, judge_loop.
inputmappingOptional. Parameter object; values may contain expressions.
input_templatestringOptional. A single string template (often used for agents and connectors); may contain expressions.
secretsmappingOptional. For function steps: maps parameter names to "$secret:<id>" bindings.
outputstringOptional. Canvas node id for this step. If omitted, the step name is used.
afterlist of stringsOptional. Explicit dependencies: this step runs after the listed step names.
whenstringOptional. If present, the step runs only when this expression evaluates to true (see Boolean guards).
Dependencies are also inferred from expressions that reference other steps (for example ${ steps.other.output }). The graph must stay acyclic.

agent

Runs a workspace agent with a text prompt built from input_template (and optionally structured input for future or auxiliary use—the runtime primarily sends the evaluated input_template string to the agent).
Scroll horizontally to compare
FieldTypeDescription
agentstringAgent name as shown in the editor. After publishing, the editor resolves this to a res://agents/... URI; when writing YAML by hand, use the plain name.
input_templatestringPrompt text; use ${ ... } to inject input and prior step outputs.
Use this when the step should reason, call tools, or produce natural-language results.
yaml
steps:
  greet:
    call: agent
    agent: Triage_Agent
    input_template: "User said: ${ input.customer_message }"

function

Runs a workspace Python function with a JSON-serializable payload.
Scroll horizontally to compare
FieldTypeDescription
functionstringFunction name as shown in the editor. After publishing, the editor resolves this to a res://functions/... URI; when writing YAML by hand, use the plain name.
inputmappingArguments passed to the function.
input_templatestringOptional. If input is empty, some runners parse this as JSON or wrap it as input.
secretsmappingOptional. Merge secret bindings into parameters.
Use this for deterministic code, transforms, or API calls implemented as functions.
yaml
steps:
  run:
    call: function
    function: echo
    input:
      message: "${ input.name }"

mcp_tool

Calls a tool on an MCP server.
Scroll horizontally to compare
FieldTypeDescription
serverstringMCP server name as shown in the editor. After publishing, the editor resolves this to a res://mcp_servers/... URI; when writing YAML by hand, use the plain name.
toolstringTool name on that server.
inputmappingTool arguments (expressions allowed).
Use this when a step must invoke a specific MCP tool with structured input.
yaml
steps:
  tool_step:
    call: mcp_tool
    server: my_mcp
    tool: search
    input:
      q: "${ input.query }"

connector

Runs a connector action (for example messaging or ticketing providers).
Scroll horizontally to compare
FieldTypeDescription
connectorstringProvider id (for example slack).
actionstringAction id (for example send_message).
inputmappingProvider-specific payload.
input_templatestringOptional. Alternative single payload string.
Use this for provider integrations configured for your workspace.
yaml
steps:
  send:
    call: connector
    connector: slack
    action: send_message
    input:
      channel: "#alerts"
      text: "${ input.message }"

conditional

Evaluates an expression and stores the result as this step’s output (often a boolean). Downstream steps can branch using when or read the value via ${ steps.<name>.output }.
Scroll horizontally to compare
FieldTypeDescription
conditionstringExpression; if omitted, behaves like true.
Use this to compute a reusable condition result in the graph.
yaml
steps:
  gate:
    call: conditional
    condition: "${ input.enabled == true }"

for_each

Runs a nested body once per item in a list (or over a single value coerced to a one-element list).
Scroll horizontally to compare
FieldTypeDescription
itemsstringExpression that yields an iterable (often ${ input.files } or a path like input.items).
asstringLoop variable name added to the input context (default item).
max_iterationsintegerOptional. Caps iterations (default 50).
parallelbooleanOptional. Default false. When true, iterations run concurrently instead of sequentially, bounded to at most 10 at a time.
domappingNested step name → step definition (same shape as top-level steps).
The loop exposes the current item as input.<as> inside the nested steps. Use this to process lists of files, rows, or API results.
yaml
steps:
  loop:
    call: for_each
    items: "${ input.files }"
    as: file
    do:
      body:
        call: function
        function: echo
        input:
          message: "${ input.file }"

while

Repeats nested steps while a condition stays true, up to max_iterations.
Scroll horizontally to compare
FieldTypeDescription
conditionstringBoolean expression.
max_iterationsintegerOptional. Default 10.
domappingNested steps to repeat.
Use this for retry-style or state-driven loops (not unbounded—always set max_iterations sensibly).
yaml
steps:
  loop:
    call: while
    condition: "${ input.continue_flag }"
    max_iterations: 5
    do:
      body:
        call: function
        function: echo
        input:
          message: "tick"

parallel_analyzer

Runs multiple analyst agents in parallel, then an aggregator agent to combine results.
Scroll horizontally to compare
FieldTypeDescription
analystslist of mappingsEach item has agent and role (for example analyst).
aggregatorstringAgent that merges analyst outputs.
input_templatestringShared prompt template for the pattern.
Use this when you want independent perspectives before a single combined answer.
yaml
steps:
  review:
    call: parallel_analyzer
    analysts:
      - agent: Policy_Analyst
        role: analyst
    aggregator: Lead_Agent
    input_template: "${ input.topic }"

handoff_router

Runs a multi-agent handoff: one entry agent handles the conversation first and may transfer to specialists by role (similar in spirit to the Support Router template: triage plus focused specialists).
YAML tab showing a handoff_router step with entry_agent, specialists, and expressions
YAML tab showing a handoff_router step with entry_agent, specialists, and expressions
Scroll horizontally to compare
FieldTypeDescription
entry_agentstringAgent that receives the first turn.
specialistslist of mappingsEach item has agent and role (specialist routing labels).
input_templatestringInitial user/context text.
yaml
steps:
  route:
    call: handoff_router
    entry_agent: Triage_Agent
    specialists:
      - agent: Order_Specialist
        role: orders
      - agent: Returns_Specialist
        role: returns
    input_template: "${ input.customer_message }"

judge_loop

Runs a generate-evaluate-iterate cycle: a generator agent produces output, an evaluator agent scores or critiques it, and the loop repeats until the pass condition is met or max_iterations is reached.
Scroll horizontally to compare
FieldTypeDescription
generatorstringAgent that produces each draft.
evaluatorstringAgent that judges the generator's output.
pass_conditionstringExpression; when it evaluates to true the loop exits early.
max_iterationsintegerOptional. Maximum rounds before stopping.
input_templatestringInitial prompt or context for the generator.
Use this when output quality benefits from iterative refinement — for example, having one agent draft a response and another critique it until it meets a quality bar.
yaml
steps:
  refine:
    call: judge_loop
    generator: Draft_Agent
    evaluator: Critic_Agent
    pass_condition: "${ steps.refine.output.approved == true }"
    max_iterations: 3
    input_template: "${ input.task_description }"

Expressions

Strings in input, input_template, when, items, condition, and workflow output may embed ${ ... } expressions. The runtime evaluates them against:
  • Workflow input — ${ input.<field> } or paths like input.<field>.<nested>
  • Step outputs — ${ steps.<step_name>.output } and nested paths like ${ steps.research.output.text }

Single expression vs interpolation

  • If the entire string is one ${ ... }, the value keeps its type (boolean, number, object, and so on).
  • If expressions are embedded in a larger string, each ${ ... } is replaced with its string form (or blank for null).

Paths without input. or steps.

Paths that do not start with input or steps are read from workflow input by default (so query behaves like input.query in path resolution).

Defaults

Use a pipe default (note the spaces around | and default):
yaml
message: '${ input.optional_field | default "fallback" }'
If the path lookup fails, the default literal is used. Literals support strings in single or double quotes, numbers, true / false, null, {}, [].

Boolean combinations

  • AND: ${ expr1 && expr2 } — both must be truthy.
  • OR (coalesce): ${ left || right } — if left is truthy (or resolves successfully), it wins; otherwise right is evaluated.

Comparisons

Inside ${ }, you can write comparisons:
  • Operators: ==, !=, <, >, <=, >=
  • Left side: a dotted path (input.tier, steps.prev.output.count, …).
  • Right side: a quoted string, a path (steps.other.output.field), or a numeric literal.
Example:
yaml
when: "${ input.region == \"eu\" }"

JSON outputs

If a step returns a JSON string that parses to an object or array, the runtime may normalize it so you can traverse fields (for example ${ steps.parse.output.items.0.id }).

Boolean guards

when uses boolean evaluation: empty string, false, 0, null, and similar are treated as false.

Common patterns

Sequential steps

List steps and use after so each waits on the previous (or rely only on ${ steps... } references, which also create dependencies):
yaml
steps:
  first:
    call: function
    function: echo
    input:
      message: "one"
  second:
    call: function
    function: echo
    input:
      message: "${ steps.first.output }"
    after: [first]

Parallel fan-out, then join

Steps with no ordering dependency between them can run in parallel. Add a join step that lists all branches in after or references their outputs:
yaml
steps:
  branch_a:
    call: function
    function: echo
    input:
      message: "A"
  branch_b:
    call: function
    function: echo
    input:
      message: "B"
  merge:
    call: function
    function: combine
    input:
      a: "${ steps.branch_a.output }"
      b: "${ steps.branch_b.output }"
    after: [branch_a, branch_b]

Conditional branching

Use a conditional step or attach when to a step so it only runs if a guard expression is true:
yaml
steps:
  maybe_extra:
    call: function
    function: echo
    input:
      message: "only if"
    after: [first]
    when: "${ input.include_extra == true }"

Loop over files

Point items at any expression that evaluates to a list (workflow input array, a prior step’s structured output, and so on). Bind each element with as:
yaml
steps:
  each_file:
    call: for_each
    items: "${ input.files }"
    as: file
    do:
      process_one:
        call: agent
        agent: File_Reviewer_Agent
        input_template: "Process this file: ${ input.file }"
Define input.files in the input schema (or populate it from an earlier step) so it is a list at run time.
If anything above is unclear for your case, open the YAML tab alongside a small test workflow and iterate until validation errors disappear.

Next steps

  • API Reference — create, publish, and run workflows via the HTTP API
  • Function SDK Reference — writing the Python code behind function steps
  • Models Reference — choosing and importing models for agent steps
  • Tutorial: Document analysis — apply these DSL concepts to a real use case
Previous

Core concepts

Next

API reference