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
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Display name |
description | string | No | Description |
persistence | block | No | Stream persistence config |
Cycles are not allowed. If you need to loop, model the iteration as separate graph runs or use 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 triggeroutputSchema: 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 outputSchema. 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 edgesource -["label"]-> target: a labeled edge, used withswitchnodes
Example: multi-node graph
This graph normalizes form input, summarizes it with an LLM, and sends a confirmation email.
graph process_form {
label: "Process Form"
description: "Normalize input, summarize with LLM, send email"
root {
type: code
label: "Entry"
inputSchema: @json {
{
"type": "object",
"required": ["name", "email", "message"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"message": { "type": "string" }
},
"additionalProperties": false
}
}
outputSchema: @json {
{
"type": "object",
"required": ["name", "email", "message"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"message": { "type": "string" }
},
"additionalProperties": false
}
}
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"
outputSchema: @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.
graph route_by_score {
label: "Route by Score"
root {
type: code
label: "Entry"
inputSchema: @json {
{
"type": "object",
"required": ["score"],
"properties": { "score": { "type": "number" } },
"additionalProperties": false
}
}
outputSchema: @json {
{
"type": "object",
"required": ["score"],
"properties": { "score": { "type": "number" } },
"additionalProperties": false
}
}
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"
outputSchema: @json {
{
"type": "object",
"required": ["result"],
"properties": { "result": { "type": "string" } },
"additionalProperties": false
}
}
code: @ts { return { result: "High priority" } }
}
node handle_low {
type: code
label: "Low path"
outputSchema: @json {
{
"type": "object",
"required": ["result"],
"properties": { "result": { "type": "string" } },
"additionalProperties": false
}
}
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 - Persistence: configure stream persistence on a graph