Skip to main content
This page is the formal reference for the .toon file format. For tutorials, see the TOON Overview.

File Structure

A .toon file is a TOON (Token-Oriented Object Notation) document with the following top-level keys:
imports:        # optional — external schemas, services, components, plugins, agents
name:           # required — workflow name
agents:         # optional — named agent declarations (not needed if agents are imported or steps use run/handler)
input:          # required — input schema
schemas:        # optional — reusable output schemas
components:     # optional — reusable step groups
steps:          # required — the workflow graph

Top-Level Keys

name

Required. String. The workflow identifier used in persistence, logs, CLI, and API.
name: bugfix-pipeline

input

Required. Either an inline schema block or a reference to an imported Schema.Class. Inline:
input:
  ticketId: string
  description: string
Imported:
input: TicketInput

agents

Optional. Named agent declarations. Required only when steps use prompt: and agents are not imported.
agents:
  coder:
    type: claude-code
    subscription: true
    instructions: You are a senior software engineer.

  reviewer:
    type: codex
    fullAuto: true

  analyst:
    type: anthropic
    model: claude-opus-4-6
    instructions: You are a data analyst.
Each agent has:
FieldTypeRequiredDescription
typestringyesAgent runtime: claude-code, codex, gemini, pi, kimi, forge, anthropic, openai, api
modelstringconditionalModel identifier. Required for anthropic, openai, and api types. Optional for CLI agents (claude-code, codex, gemini, pi, kimi, forge) which have built-in defaults.
instructionsstringnoSystem prompt / persona
tools”string[]“noTool names the agent can use
subscriptionbooleannoUse subscription billing (Claude Code)
timeoutMsnumbernoHard timeout in milliseconds
idleTimeoutMsnumbernoInactivity timeout in milliseconds
Additional type-specific fields are passed through to the underlying agent constructor (e.g., fullAuto for Codex, permissionMode for Claude Code, sandbox for Gemini).

type: api

The api type is a generic provider-based agent that requires explicit provider and model fields. Currently supported providers are anthropic and openai.
agents:
  custom-agent:
    type: api
    provider: anthropic
    model: claude-sonnet-4-20250514
    instructions: You are a helpful assistant.
FieldTypeRequiredDescription
providerstringyesProvider name: anthropic or openai
modelstringyesModel identifier for the provider

schemas

Optional. Named output schemas that can be referenced by multiple steps.
schemas:
  Review:
    approved: boolean
    feedback: string

components

Optional. Named, parameterized step groups.
components:
  ReviewCycle:
    params:
      content: string
    steps[1]:
      - id: "{id}-review"
        prompt: Review {params.content}.
        output:
          approved: boolean

steps

Required. Array of nodes that define the workflow graph.

imports

Optional. Object with sub-keys: schemas, services, components, workflows, plugins, agents.

Field Types

TOON SyntaxSemanticsEffect Equivalent
stringUTF-8 stringSchema.String
numberIEEE 754 doubleSchema.Number
booleantrue or falseSchema.Boolean
"a"String literalSchema.Literal("a")
"a" | "b"String literal unionSchema.Literal("a", "b")
"string[]"Array of stringsSchema.Array(Schema.String)
"number[]"Array of numbersSchema.Array(Schema.Number)
string?Optional stringSchema.optional(Schema.String)
T?Optional TSchema.optional(T)
nested blockStructSchema.Struct({...})
- field: typeArray of objectsSchema.Array(Schema.Struct({...}))

Node Kinds

Every entry in steps: is a node. Nodes are either steps (default) or control-flow nodes (identified by kind:).

Step Node (default)

- id: analyze
  agent: coder
  prompt: Analyze the code.
  output: AnalysisOutput
  needs[2]: step-id-1,step-id-2
  maxAttempts: 3
  timeout: 5m
  skipIf: "expression"
Each step must define exactly one of prompt, run, or handler.
PropertyTypeRequiredDescription
idstringyesUnique step identifier
agentstringfor promptReferences a declared agent
promptstringnoPrompt text (mutually exclusive with run/handler)
runstringnoInline TypeScript (mutually exclusive with prompt/handler)
handlerstringnoTypeScript function reference (mutually exclusive with prompt/run)
outputschema/stringyesOutput schema — inline block or named reference
needsstring/arraynoExplicit dependencies on other step ids
maxAttemptsnumbernoRetry count (flat alternative to nested retry: block)
retryobjectnoRetry policy with maxAttempts, backoff, initialDelay
timeoutdurationnoStep timeout (e.g. 5m, 30s)
cacheobjectnoCache policy with by and version
skipIfstringnoCondition expression to skip the step

handler (advanced)

The handler field references an external TypeScript module that exports a function. The format is ./path/to/module.ts or ./path/to/module.ts#namedExport. If no export name is specified, the default export is used. The function receives the step context (with input, upstream step outputs, and services) and returns the step output.
- id: transform
  handler: ./handlers/transform.ts#processData
  output:
    result: string

kind: sequence

- kind: sequence
  children[2]:
    - id: a
      ...
    - id: b
      ...

kind: parallel

- kind: parallel
  maxConcurrency: 2       # optional — defaults to unlimited
  children[2]:
    - id: x
      ...
    - id: y
      ...

kind: loop

- kind: loop
  id: review-loop         # optional — loop identifier
  until: "expression"     # required — stop condition
  maxIterations: 5        # optional — safety limit (default: 5)
  onMaxReached: fail | return-last  # optional — default: return-last
  children[1]:
    - id: check
      ...

kind: approval

- kind: approval
  id: approve-deploy      # required — gate identifier
  needs[1]: build         # optional — dependencies
  request:                # required — approval request
    title: "Deploy?"
    summary: "Details..."
  onDeny: fail | continue | skip  # optional — default: fail

kind: branch

- kind: branch
  condition: "expression" # required — branch condition
  then[1]:                # required — steps if true
    - id: a
      ...
  else[1]:                # optional — steps if false
    - id: b
      ...

kind: worktree

- kind: worktree
  path: ./feature         # required — worktree path
  children[1]:
    - id: implement
      ...

kind: workflow

Invokes an imported workflow as a sub-workflow. The use field references a workflow alias declared in imports.workflows. Input fields are mapped via the input block and support interpolation.
- kind: workflow
  id: run-tests            # required — step identifier
  use: testSuite           # required — workflow alias from imports
  input:                   # optional — input mapping
    repo: "{input.repoUrl}"
    branch: "{checkout.branch}"

kind: component

- id: my-review           # required — caller id (used for ID interpolation)
  kind: component
  use: ReviewCycle         # required — component name
  with:                    # required — parameter values
    content: "{draft.content}"
    reviewer: "tech lead"

Interpolation Grammar

Interpolation occurs inside prompt:, request:, skipIf:, until:, condition:, and component with: values.

Syntax

{expression}     — interpolate a value
{{               — literal {
}}               — literal }

Expression Forms

FormExampleResolves To
input.field{input.topic}Workflow input field
stepId.field{analyze.summary}Upstream step output field
stepId.nested.field{config.deploy.env}Nested output field
params.field{params.content}Component parameter
loop.iteration{loop.iteration}Current loop iteration (1-based)
id{id}Caller id (inside components)
Ternary{input.score > 7 ? 'good' : 'bad'}Conditional value
Method call{input.tags.join(', ')}Result of method
String concat{'Hello ' + input.name}Concatenated string
Expressions inside {} are evaluated as JavaScript. All standard operators, ternaries, and method calls are supported.

Duration Syntax

Timeouts and delays use duration strings:
FormatExample
Milliseconds250ms
Seconds30s
Minutes5m
Hours1h

Import Blocks

Imports use TOON’s tabular array format for compact declarations. Each import category is a tabular array with {from,use} or {from,as} field headers.

Schema Import

imports:
  schemas[1]{from,use}:
    ./path.ts,TypeName

Service Import

imports:
  services[1]{from,use}:
    ./path.ts,ServiceTag

Component Import

imports:
  components[1]{from,use}:
    ./path.toon,ComponentName

Workflow Import

imports:
  workflows[1]{from,as}:
    ./path.toon,aliasName

Plugin Import

Plugins have per-entry config objects, so they use expanded list items:
imports:
  plugins[1]:
    - from: package-name
      config:
        key: value

Execution Semantics

  • Steps in steps: execute sequentially by default (top to bottom)
  • kind: parallel children execute concurrently
  • kind: sequence is explicit sequential grouping (useful inside parallel)
  • kind: loop repeats children until until: is true or maxIterations is reached
  • kind: approval suspends execution durably
  • kind: branch takes one path based on condition:
  • kind: workflow invokes an imported workflow as a sub-workflow
  • Dependencies declared in needs: are resolved before the step runs
  • Implicit dependencies from {stepId.field} interpolation are also resolved

Compilation Target

A .toon file compiles to the same WorkflowNode graph as the Effect builder API. At runtime, there is no behavioral difference between a TOON workflow and an Effect builder workflow.