Input & output nodes¶
Every agent begins at an input node and ends at an output node. The input node carries whatever you pass to a run into the graph; the output node's value is what the run returns. Both are boundary nodes — they mark the edges of the graph and perform no work of their own. A fresh graph in the editor already contains one of each, wired together, so it passes validation and runs (echoing its input) from the start.
graph LR
IN([input]) --> OUT([output])
The input node¶
The single entry point of the agent. The value you pass when you run the agent appears on its out port, ready for downstream nodes to read with $in. It is a boundary node and is never skipped — it has no inbound edges, so it always runs first.
Ports¶
| Port | Direction | Required | Description |
|---|---|---|---|
out |
out | — | Carries the entire run input to whatever is wired downstream. |
An input node has no in-ports.
Configuration¶
| Field | Type | Default | Description |
|---|---|---|---|
modality |
enum | text |
The expected shape of the input: text, audio, image, video, json, or file. |
schema |
JSON Schema | null |
An optional description of the input's structure. |
Both fields are declarative. They document what the agent expects and help the editor and triggers map an incoming payload — they are not enforced at run time. The value on out is exactly what was passed in.
Behavior notes¶
- The whole input lands on
out. Downstream nodes read it with the bare$intoken (the default in-port). To pull a field out of a structured input, drill into it —$in.in.<field>— from a node that reads it. See Referencing inputs. - Composing several inputs. The input node has a single
outport; it does not split values for you. When an agent needs to combine several upstream values — say a document and a question — you give the consuming node (typically an llm) more than one named in-port and wire a separate edge into each. That node then addresses them distinctly as$in.<port>. This is how multi-input agents work: a run input like{"path": "...", "question": "..."}fans out to nodes whose named ports each pick up their piece. - Triggers live here, not as nodes. When you select the input node, the inspector shows a read-only Triggers panel listing how this agent can be invoked (schedule, webhook, token). Triggers are added after saving the agent, not as graph nodes — see Triggers and webhooks.
Example¶
An input node feeding two downstream readers that each take a slice of a structured input:
graph LR
IN([input]) --> A[tool: read file]
IN --> B[tool: carry question]
A --> LLM[llm]
B --> LLM
LLM --> OUT([output])
{
"id": "n_in",
"type": "input",
"kind": "boundary",
"config": { "modality": "json" },
"ports": { "in": [], "out": [{ "id": "out" }] }
}
Works well with¶
- The llm node — the usual first consumer of the input
- Referencing inputs —
$in,$in.<port>, and field drilling - Runs and sessions — what a "run input" is
The output node¶
The exit point. Whatever value reaches its in port is the run's canonical output — the value returned by the run and stored against it. It is a boundary node and performs no side effect; it just marks where the answer comes from.
Ports¶
| Port | Direction | Required | Description |
|---|---|---|---|
in |
in | yes | The value that becomes the run's output. |
An output node has no out-ports.
Configuration¶
| Field | Type | Default | Description |
|---|---|---|---|
modality |
enum | text |
The shape of the result: text, audio, image, or json. |
schema |
JSON Schema | null |
An optional description of the output's structure. |
For an audio or image result the value is a reference to a produced artifact, not the raw bytes — see Audio & images.
Behavior notes¶
The output node has two rules worth knowing, because both surface as loud failures rather than silent surprises:
- Exactly one in-port. An output node is a single-value consumer: it takes the value from one in-port. Declaring more than one in-port is a validation error — the editor will not let you save it, because which value is "the output" would be ambiguous.
- At most one output may run per run. A graph is allowed to contain several output nodes — for example, a happy-path output and a refusal output on the two branches of a guardrail. But no more than one may actually execute in a single run. If two output nodes both go live, the run fails with a clear message: a second output node executed — the run output would be ambiguous. Route exclusive branches (with a router or a guardrail) so that exactly one output is reachable.
- Empty output is honest. If the output node's in-port is fed only by a branch that was not taken — for example an upstream tool bound its
errport and its success path was skipped — the run does not report a green empty success. It carries an honest error note explaining the upstream cause.
Example¶
A guardrail that routes to one of two outputs — a valid two-output pattern, because only one is ever live:
graph LR
IN([input]) --> G{guardrail}
G -->|pass| LLM[llm]
LLM --> OUT([output: answer])
G -->|block| REFUSE([output: refusal])
{
"id": "n_out",
"type": "output",
"kind": "boundary",
"config": { "modality": "text" },
"ports": { "in": [{ "id": "in" }], "out": [] }
}
Works well with¶
- The guardrail node — refuse on a separate
blockoutput before expensive work - The router node — send the run down exactly one branch
- Runs and sessions — where the run output is stored and read back