The machine under the canvas
Suede AI Agent Studio runs a flow in topological order: each node starts once every node feeding it has finished, branches skip the paths they do not take, and an error halts only the nodes that depend on it. Every cost-bearing node is checked against a per-run spend ceiling ($5 by default), each step streams to you live over Server-Sent Events, and Live runs an immutable saved version you promoted from Test. The overview names the four parts; this page specifies how they behave, with the numbers the engine enforces.
- What you'll do
- Learn exactly how the engine runs a flow: ordering, branch skipping, error halting, the cost ceiling, streaming, versions, and settlement.
- What you'll have
- The guarantees you can design against, with the limits the engine enforces on every run.
Quick definitions: dry run and SSE
- Dry run
- A test run that executes your flow's logic for real but stubs every step that would cost money or reach an outside system, so nothing is spent.
- SSE
- Server-Sent Events, a long-lived HTTP response the server streams events over as they happen.
Four units, one contract
Everything meets at the FlowGraph. The canvas writes it, the contract types and validates it, the engine executes it, and the runtime wraps the engine in HTTP, payments, and scheduling. No unit reaches around the contract to talk to another.
Canvas
The visual editor, built on @xyflow/react. It edits a plain data structure, never code: dragging a node adds an entry to a list, wiring an edge adds a connection between typed ports, and port compatibility is checked at wire time. The canvas imports only the client-safe catalog projection, so no executor code ever reaches the browser bundle.
Contract
One typed FlowGraph shared by everything: a list of nodes with typed params and a list of edges connecting output ports to input ports. zod validates it at every external boundary, and the graph codec is versioned, so flows saved under an older schema still parse. Because the contract is plain data, the same graph can be edited, exported as a template, validated at launch, and executed, without translation between representations.
Engine
An async generator that walks the graph in topological order and yields run events as it goes. It owns every execution guarantee on this page: dependency ordering, branch skipping, error halting, the cost ledger, the spend ceiling, the depth guard, and the dry-run gate.
Runtime
The hosted API around the engine: flow CRUD, streamed runs over SSE, cron-driven schedules, HMAC-signed webhooks, and, after launch, a public run endpoint per agent (free preview, paid per call once payment is enabled, or not callable yet) plus machine-readable discovery documents. The runtime decides who may run what and whether money moves; the engine only ever executes what it is handed.
What the engine guarantees on every run
Topological order. A node executes when every node feeding its inputs has finished. Edges are simultaneously the data flow and the execution order; there is no hidden shared state a node could read around them.
Branches skip, they don't fail. When a Branch or Switch activates one outgoing path, nodes on the inactive paths are marked skipped, a first-class node status distinct from error. A skipped node charges nothing and emits nothing downstream.
Errors halt the branch, not the run. When a node fails, everything downstream of it is halted so no later step runs against missing data, and nothing downstream is charged. Independent branches that don't depend on the failed node finish normally. The run then reports status error with every per-node outcome preserved.
The cost ledger. Every node execution is recorded to the run's ledger: node id, node type, terminal status, what it cost, and whether that cost settled for real. The final run event carries the total. This is the same ledger the run dock renders live and the one you can audit after the fact.
The spend ceiling. Before every cost-bearing node, the engine checks one in-run ceiling: the per-run cap (the RUN_COST_CEILING_USDC environment variable, $5 when unset). A paid caller's run is also bounded by the amount reserved for that call, whichever is lower. Crossing it aborts the run with an explicit cost-ceiling marker on both the failing node event and the final run event. Subflows and loops share the parent run's ceiling by reference, so a nested run cannot escape the budget by being nested, and a ceiling abort inside an iteration aborts the whole run instead of hiding in a per-item error list.
The depth guard. Subflow and Loop nodes run their inner flow one level deeper than the run that called them, and the engine refuses any run deeper than 16 levels, which stops runaway recursion such as a flow that calls itself.
Dry-run is deny-by-default. The engine stubs any node declared cost-bearing or side-effecting before its executor runs. The gate fails safe: a node that requires a stub but doesn't declare one is refused outright rather than allowed to run for real, and an enumeration test pins the full catalog so a new node cannot ship without declaring its dry-run behavior. Which nodes are stubbed is listed per node in the node reference.
Tenant isolation at the graph boundary. A Subflow or Loop node can only load flows belonging to the same owner, and a flow that is missing returns the same error as a flow owned by someone else, so a graph cannot be used to probe for the existence of another tenant's private flows.
Runs are event streams, not polling loops
The engine yields typed events as it executes, and the runtime forwards them verbatim as Server-Sent Events. The run dock in the studio and an API caller watching a run consume the identical stream:
run:start { runId, at }
node:start { runId, nodeId, nodeType }
node:log { runId, nodeId, level: "info" | "error", msg }
node:done { runId, nodeId, nodeType, outputs, costUsdc }
node:error { runId, nodeId, nodeType, error, costCeilingExceeded? }
run:done { runId, totalCostUsdc, status, abortedReason? }Two details worth noticing. Cost arrives per node, on each node:done, so a watcher sees spend accumulate in real time rather than discovering it in a summary. And a cost-ceiling abort is distinguishable from an ordinary failure on both the node event and the final run:done, so a client can tell “this step broke” from “this run ran out of budget” without parsing error strings.
Drafts change, versions don't, Live points at a version
Every flow keeps one mutable draft and any number of immutable saved versions. Deployment is environment-based, like software: you promote a saved version to the Test environment, watch it run, and only then promote to Live. Promotion is deliberate on purpose: the API requires a typed confirmation (PROMOTE TEST or PROMOTE LIVE) and pins the exact version being deployed by hash, so a concurrent edit cannot swap the graph between your approval and the deploy.
What Live runs is therefore exactly what you promoted: editing the draft after promotion changes nothing in production until you promote again. Version pinning extends through subflows, and the Live path refuses graphs containing the Connector Lab's simulation-only API Operation node, the same rule launch validation enforces.
Per-call payment is optional at every layer
Your own runs bill the usage credit on your card-funded plan. Selling per call is an add-on, and caller settlement defaults to dry-run globally, per agent, and per request: no caller payment moves until an agent's owner switches it on, and an explicit dry-run request always stays free. Once it is on, a priced live call is verified and settled before the flow executes, and the response's settled field is true when a real payment settled. The caller's view of that handshake is in API for callers; plans, usage credit and per-call sales are in Billing.
Last updated .