Architecture and data exchange
A flow in Orvanta is a JSON-serializable value in the OpenFlow format: an input spec (similar to Scripts) plus a sequence of steps, also called modules. Each step is one of:
- A reference to a Script from the Orvanta Hub.
- A reference to a Script in your workspace.
- An inlined Script in TypeScript (Deno), Python, Go, Bash, SQL, or any other supported language.
- A trigger script: a Script meant to run as the first step of a scheduled flow, watching for external events and exiting early once there’s nothing new left to process.
- A for loop that iterates over a list and runs an embedded flow for each element. The list itself is computed dynamically via an input transform.
- A branch to the first subflow whose predicate evaluates truthy, checked in order.
- A branch to all: every subflow runs, and their results are collected into an array.
- An approval/suspend step, which pauses the flow at no cost until an approval or resume signal arrives.
- An inner flow.
How flows are executed
Section titled “How flows are executed”Each step runs as its own job, picked up by whichever worker is free. Steps in sequence run one after another; steps inside a parallel branch are queued at the same time and can land on different workers. A join step — typically a final aggregation — waits for every branch to finish before it gets queued. Add enough workers and the branches genuinely overlap, so the flow’s total wall-clock time tracks its slowest branch rather than the sum of all of them.
Input transform
Section titled “Input transform”Because any step’s input can pull from any earlier step’s output — not just the one immediately before it — a flow is really a Directed Acyclic Graph (DAG) rather than a plain sequence. You reference a prior step’s result by its step ID.
Every step’s input transform can draw from:
- the flow’s own input
- any step’s result, not only the previous step’s
- a Connection or Variable
and map that onto the step’s parameters.
The mapping is written as a JavaScript expression, evaluated in a restricted sandbox that exposes a reduced standard library plus a small set of extra functions:
flow_input: the object holding the flow’s own parameters.results.{id}: the result of the step with that ID.resource(path): the Connection at that path.variable(path): the Variable at that path.
Because every parameter can be computed this way, a step can pull whatever inputs it needs from anywhere else in the flow — which is what lets a single flow engine drive such a wide range of module types.
JavaScript evaluation engine
Section titled “JavaScript evaluation engine”Flow expressions (input transforms, branch predicates, for-loop iterators) are evaluated using a JavaScript engine. Two engines are available:
- QuickJS: A lightweight JavaScript engine with faster startup times for simple expressions than the full V8 engine. It uses lazy evaluation:
resource()andvariable()calls are resolved on demand rather than pre-fetched, which is more efficient for expressions that don’t use all available connections. - Deno (V8): The full V8-based JavaScript engine, used elsewhere in Orvanta to run complete TypeScript step scripts.
QuickJS is the engine used for flow expression evaluation across every Orvanta edition; the Deno/V8 runtime is reserved for executing full TypeScript scripts, not for evaluating the expressions that wire steps together.
Connecting flow steps
Section titled “Connecting flow steps”For each field, you can either write the JavaScript directly or use the quick-connect button when the field maps one-to-one onto a field of flow_input, previous_result, or any step’s result.
From the editor, you can directly get:
- Static inputs: fixed values set directly in the step’s input fields.
- Flow env variables: flow-level constants referenced from any step’s input using
flow_env.VARIABLE_NAMEorflow_env["VARIABLE_NAME"]. - Dynamic inputs:
- by the step ID
- by clicking the plug icon, which lets you pick flow inputs or a previous step’s result (after testing the flow or that step).
You can also have Orvanta AI wire up step inputs for you automatically.
Custom flow states
Section titled “Custom flow states”A state is a value stored as a connection of the state connection type, and it persists across separate executions of the same Script — this is the mechanism that lets trigger scripts remember what they’ve already seen from one poll to the next.
Flow-level state works differently: it’s scoped to the current run rather than kept across runs. From any step, you can set or read a value under an arbitrary key, and every other step in that same run can see it. The value lives exactly as long as the flow job itself does — once the run ends, so does the state.
It’s a useful escape hatch for cases where threading a value through every step’s regular output/input chain is awkward, without resorting to reading and writing a workspace connection just to move data between two steps of the same run.
TypeScript example:
import * as orvanta from "npm:orvanta-client@^1.9.0"export async function main(x: string) { await orvanta.setFlowUserState("FOO", 42) return await orvanta.getFlowUserState("FOO")}Python example:
import orvanta#extra_requirements:#orvanta>=1.9.0def main(x: str): orvanta.set_flow_user_state("foobar", 43) return orvanta.get_flow_user_state("foobar")Shared directory
Section titled “Shared directory”By default, flows on Orvanta pass data on a result basis (see above): a step takes the results of earlier steps as its input. That works well for lightweight automation.
For heavier ETL work, or any output that doesn’t serialize cleanly to JSON, use the Shared Directory instead. Every step in a flow that opts into it shares a ./shared folder on disk, where a step can write larger artifacts for the next step to pick up directly from the filesystem.
Get more details on the Persistent storage & databases dedicated page.