SWIRLS_
Language

Graphs

Build DAGs of nodes connected by edges to define workflow logic.

A graph is a directed acyclic graph (DAG) of nodes connected by edges. Each node performs a discrete unit of work. The flow block defines the edges between nodes.

Graph structure

FieldTypeRequiredDescription
labelstringYesDisplay name
descriptionstringNoDescription

Cycles are not allowed in the outer flow graph. For in-workflow iteration, use type: map or type: while nodes (child graphs), or trigger separate runs via a schedule.

Root node

Every graph has exactly one root node. The root node is the entry point for the graph run. It defines:

  • inputSchema: the shape of data the graph accepts from a trigger
  • outputSchema: the data passed to downstream nodes

Use the root { ... } syntax (not node root).

Regular nodes

Additional nodes use node name { ... }. Each node has a type and type-specific configuration. Downstream nodes define only schema (the node’s output shape). Their input is derived from the outputs of upstream nodes, accessed via context.nodes.

Flow block

The flow { ... } block defines edges between nodes.

  • source -> target: a simple edge
  • source -["label"]-> target: a labeled edge, used with switch nodes

Example: multi-node graph

This graph normalizes form input, summarizes it with an LLM, and sends a confirmation email.

schema contact_payload {
  label: "Contact message"
  schema: @json {
    {
      "type": "object",
      "required": ["name", "email", "message"],
      "properties": {
        "name": { "type": "string" },
        "email": { "type": "string" },
        "message": { "type": "string" }
      },
      "additionalProperties": false
    }
  }
}

graph process_form {
  label: "Process Form"
  description: "Normalize input, summarize with LLM, send email"

  root {
    type: code
    label: "Entry"
    inputSchema: contact_payload
    outputSchema: contact_payload
    code: @ts {
      const { name, email, message } = context.nodes.root.input
      return {
        name: name.trim(),
        email: email.trim().toLowerCase(),
        message: message.trim(),
      }
    }
  }

  node summarize {
    type: ai
    kind: object
    label: "Summarize"
    schema: @json {
      { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" } } }
    }
    model: "gpt-4o-mini"
    prompt: @ts {
      const { name, message } = context.nodes.root.output
      return `Summarize this message from ${name}: ${message}`
    }
  }

  node notify {
    type: email
    label: "Send confirmation"
    from: @ts { return "[email protected]" }
    to: @ts { return context.nodes.root.output.email }
    subject: @ts { return "We received your message" }
    text: @ts {
      const summary = context.nodes.summarize.output.text ?? ""
      return `Thanks for reaching out. Summary: ${summary}`
    }
  }

  flow {
    root -> summarize
    summarize -> notify
  }
}

Branching with switch nodes

A switch node routes execution to one of several labeled branches. The router function returns the name of the branch to follow. Each branch is then connected using a labeled edge in the flow block.

schema score_payload {
  label: "Score input/output"
  schema: @json {
    {
      "type": "object",
      "required": ["score"],
      "properties": { "score": { "type": "number" } },
      "additionalProperties": false
    }
  }
}

schema route_branch_result {
  label: "Switch branch result"
  schema: @json {
    {
      "type": "object",
      "required": ["result"],
      "properties": { "result": { "type": "string" } },
      "additionalProperties": false
    }
  }
}

graph route_by_score {
  label: "Route by Score"

  root {
    type: code
    label: "Entry"
    inputSchema: score_payload
    outputSchema: score_payload
    code: @ts {
      return { score: Number(context.nodes.root.input.score) || 0 }
    }
  }

  node route {
    type: switch
    label: "Route"
    cases: ["high", "low"]
    router: @ts {
      return context.nodes.root.output.score > 50 ? "high" : "low"
    }
  }

  node handle_high {
    type: code
    label: "High path"
    schema: route_branch_result
    code: @ts { return { result: "High priority" } }
  }

  node handle_low {
    type: code
    label: "Low path"
    schema: route_branch_result
    code: @ts { return { result: "Low priority" } }
  }

  flow {
    root -> route
    route -["high"]-> handle_high
    route -["low"]-> handle_low
  }
}

Further reading

  • Node types: all available node types and their configuration
  • Context: type-safe access to node outputs via context.nodes
  • Streams: typed graph output persisted for other graphs and integrations

On this page