Click any node on the graph and the inspector on the right fills with everything about it, organized into tabs: the node’s own YAML, the variables it reads, structured request fields, the result of the last run, and an AI chat that already knows which node you are looking at. Each tab is described below in the order it appears.
Node
The selected node’s complete YAML (type, config, and routing) in a small code editor.
Anything about a node can be changed here: the URL, a header, next to rewire the flow, a
field no structured panel exposes. Edits are validated as you type; a parse error shows a red
banner until fixed.
The other panels are shortcuts into this same YAML, not alternatives to it. Routing can also be rewired by dragging on the graph.
The ? button in the panel header opens the in-app reference at the selected node’s type: the fields it takes and a worked example, without leaving the app or reaching for a browser. F1 opens the same reference at its table of contents: every node type plus variables and environments, data-driven runs, hooks, retry and polling, and logging. It’s bundled with the app at build time, so it works offline.
Variables
Every env.X and variables.X reference this node consumes, one row each:
- Source badge shows where the value comes from:
.env, a flow variable, captured by {node} (click it to jump to the producing node), an override, or unset. - Value preview: values from
.envare masked by default; each row has a reveal toggle. - Override takes a value that applies to runs from this machine only. Overrides
are stored locally, never written into the
.aetherfile, so a temporary token or a teammate-specific value can’t leak into Git. - Promote is for when you decide an override belongs in the flow; one click writes it into
the flow’s
variables:block and clears the override.
Unset references get a ⚠ badge here and on the graph. The full resolution rules are in Variables & environments.
Params (request nodes)
A form for the whole request, so a routine edit doesn’t mean hand-editing YAML. Top to bottom:
- Endpoint holds the method (a dropdown) and the url. Placeholders are welcome:
https://api.example.com/users/{id}. - Headers: key/value rows. Values take
${{ }}interpolation, so an auth header can readBearer ${{ variables.token }}. - Path params get a row automatically for every
{name}or:nameplaceholder in the URL; fill in each value inline. No placeholders, no rows. - Query params are key/value rows with an Add row at the bottom and a remove button per
row. They’re stored as structured
queryParams, not smuggled into the URL string. - Capture lists the variable names this node produces and the expressions that produce them
(
token←response.body.token). Everything captured here is readable by later nodes; see Variables & environments. - Options covers the request’s timeout (ms) (empty means the default 30 seconds) and its retry policy: attempts, delay (ms), backoff, and until.
- Streaming (SSE) is a checkbox and three fields, for reading a
text/event-streamresponse intoresponse.bodyas an array of events. Tick Read the response as a stream and you get expectEvents (stop after N), waitFor (JavaScript run after each event, witheventas the one just received andeventsas everything so far), and a timeout (ms) that is deliberately separate from the request timeout above: that one bounds getting the response, and a stream that starts promptly then runs for minutes is the normal case. Full semantics: Server-Sent Events.
Retry is worth a sentence, because the two modes look the same and aren’t. With attempts
alone, the node re-runs only when it throws (a connection reset, a timeout). Add an until
expression (response.status === 200) and it becomes polling: the node re-runs until that
expression is truthy, whether or not the call failed. The panel says which of the two you’ve
configured underneath the fields. Full semantics:
retry & poll-until.
Every value here can reference variables (${{ variables.userId }}) just like the URL itself.
Body (request nodes)
A JSON-aware editor for the request body, with syntax highlighting, live validation, and a Format JSON button that reindents a one-liner into readable JSON:
- The status line tells you where you stand: Valid JSON, Valid JSON with
${{ }}interpolations, or the exact parse error. Non-JSON bodies (form-encoded, plain text) are fine; the panel says “Plain text body” and stays out of your way. - If the body is JSON but the request has no
Content-Typeheader, a one-click chip addsContent-Type: application/json.
A body is not always a string, though, and the tab follows the shape of the one you have.
GraphQL requests
A node with a graphql block gets the GraphQL editor here, since the query is that node’s
body. It holds the query (a Monaco editor with GraphQL syntax highlighting), variables as
a JSON object, an operationName field, and a checkbox for letting a response carrying errors
pass.
Two things the panel tells you that the YAML does not. The variables box reports the exact JSON
error rather than silently keeping the last good value, and operationName is only pointed out as
necessary when the document declares more than one operation. The highlighting is highlighting and
nothing more: there is no GraphQL schema loaded, so nothing here checks a field name against your
API.
The errors checkbox is worth reading before you tick it. GraphQL servers answer 200 even when
execution failed, so by default a non-empty errors array fails the node. Ticking it means you
intend to assert on response.body.errors yourself.
File uploads and binary bodies
A multipart or binary body is a structured block rather than a string, and each has its own
editor here:
- multipart/form-data lists the fields, and each one switches between a text value and a file read from disk. Choose file… opens your operating system’s file picker. A file field also takes filename, which is what the server is told the file is called and is worth setting when an upload endpoint validates it, and contentType.
- binary picks between a filePath and an inline base64 string, plus the contentType the format requires.
The picker answers with an absolute path, which always works. A relative path resolves against the flow file’s own folder, which is what you want for a fixture checked in beside the flow, so the panel says so rather than rewriting your path for you.
Both editors hold an edit back until it validates. Adding a field, or switching one from text to file, passes through a state the format does not accept, so the panel shows what you are typing while the file keeps the last version that parsed, and says as much underneath. Everything these editors write is ordinary YAML: the panel is the convenient route to the block, not a different way of storing it.
gRPC & WebSocket (their own nodes)
grpc and websocket nodes don’t use Params/Body. Each gets a dedicated config panel in
their place. The gRPC panel covers the service and method, the .proto file or server
reflection, and the request message; the WebSocket panel covers the URL, subprotocols,
the messages to send, and how long to collect replies. The Variables and Result sub-tabs
work the same as for any other node.
Result
What this node did on the last run:
- Request (request nodes) shows the exact method, URL, headers, and body that went out, with every variable already resolved. When a request misbehaves, this is the ground truth of what was actually sent.
- Result: for a request, the response status, headers, and body; for a condition, which branch it took and why; for captures, scripts, and AI actions, the variables they produced and their values.
Beside the status sit two badges: how long the node took (823 ms, or 1.4 s past a
second) and, for a request, how big the response was. Every node type is timed, not just
requests, which is how you find the script node that’s quietly costing you four seconds.
Before the first run it shows a placeholder; run the flow to fill it.
Reading a response that isn’t JSON
Pretty re-indents more than JSON. An XML or SOAP document is reflowed with one element per line, which is the difference between reading a SOAP envelope and scrolling one. It is a formatter and not a parser, deliberately: a malformed document gets reflowed rather than rejected, because a malformed document is usually the one you are trying to look at.
A CSV or TSV body renders as a table with a sticky header, since a table is the only reading of a CSV anybody actually does. Very large exports are truncated with a note saying so, on the grounds that a 50,000-row export is a download rather than a read.
A binary body says it is binary and points at Save response below, instead of arriving as a screenful of mojibake that looks like a broken viewer.
Copy the request as code
Copy as… on the Request tab turns the resolved request into working code in six languages:
cURL, JavaScript fetch, Node axios, Python requests, Go (standard library,
no third-party import) and HTTPie. It is generated from the request that actually went out,
variables already substituted, so what you paste is what ran. Useful for handing a reproduction
to someone who does not have Bandura, or for dropping a call into application code once the
flow proves it works. The same button sits on every recorded request in
Execution history.
Filter the response with JSONPath
Under the response body is a JSONPath box. Type $.items[*].id and the panel shows just
the matches, which beats scrolling a 400-line payload to check one field. It supports the
common syntax: $ root, dot and bracket child access, array indexes including negative ones,
wildcards, recursive descent (..name) and slices ([1:3]). Filter expressions and script
expressions are not supported. A path that does not parse shows a quiet inline message and
leaves the rest of the panel alone.
Save the response to a file
Save response writes the body to disk exactly as it came off the wire, through your operating system’s normal save dialog. The target can be anywhere you can write, not just inside the workspace, because a downloaded PDF or CSV usually does not belong next to your flows.
Chat
An AI chat that knows your flow. Ask why a node failed, how to write a check, or what a response means. Enter sends, Shift+Enter adds a newline. Requires an AI provider, either Anthropic or any OpenAI-compatible endpoint (Connect an AI provider).
The bottom panel
Below the editor (toggle with ⌘J): Console (timestamped execution log), Variables (the live snapshot from the last run), History (Execution history), and Terminal (The built-in terminal).