1. Visual Flow Builder
Drag nodes from the palette, pan/zoom freely, and wire outputs to inputs. Rename nodes in the inspector, search/collapse the palette, and toggle Dormant to temporarily disable nodes without rewiring. When you double-click the canvas or drop a node from the palette, a quick-add picker appears exactly where you clicked.
- Resizable side panel plus collapsible/searchable node list.
- Inline node controls (eye icon) blur dormant nodes and mark them as skipped during execution.
- Light/dark theme toggle affects the whole workspace.
- Double-click canvas / drag out of the palette to open the node quick-add menu right where you need it.
- Node cards display their ID with a one-click copy action; IDs match those shown in logs/variables.
- Keyboard shortcuts: R to run, Esc deselects,⌘/Ctrl + S quick-saves, and (upcoming) Delete removes the selected node.
2. Node Types
HTTP Node
Send REST requests with templated URLs, headers, and JSON bodies. Responses can be persisted into shared variables, optional retry policies help stabilize flaky endpoints, and request previews/exporters (JSON, cURL, wget) keep your inspector in sync with real tooling.
POST https://api.example.com/login
Headers:
Content-Type: application/json
Body:
{
"email": "dev@example.com",
"password": "{{ variables.password }}"
}
Persist response body → alias: loginResponse
Extract Node
Copy a JSON value from the prior node (or any earlier node) into a named variable.
JSONPath: {{ response.body.data[0].id }}
Variable name: firstUserId
Assertion Node
Validate values using equals / exists / contains. Failing assertions halt the execution.
Type: equals
Path: {{ response.status }}
Expected: 201
Log Node
Write templated text into the debugger log—perfect for breadcrumbs or printing variables.
Message: Order {{ variables.orderResponse.id }} created
Wait Node
Pause execution for a fixed duration (up to 10 minutes).
Delay (seconds): 30
Conditional Node
Evaluate a boolean expression and branch via the green (true) or red (false) handle.
{{ response.status }} === 200
{{ response.body.message }} includes "success"
{{ variables.loginResponse.token }} isNotNull
Loop Node
Repeat a section of the flow for each item (or for a fixed count). Drop nodes inside the loop container to define the body.
Items: {{ variables.items }}
Item alias: item
Index alias: index
Max iterations: 25
Set Variable
Create or override a variable with JSON or a template value.
Variable: items
Value: [{"id":1},{"id":2}]
Value (template): {{ response.body.items }}
3. Importers & inspectors
The HTTP inspector shows JSON / cURL / wget previews and can import any of those formats. Paste a snippet and click Apply to hydrate the form, or copy the generated command directly into your terminal.
- Preview tabs: switch between JSON, cURL, and wget to copy exactly what will run.
- HTTP import: drop JSON that mirrors the preview, a
curl -X … statement, orwget --method=… command. Headers, body, and method are parsed automatically. - Postman import: use the editor toolbar to import a Postman collection or environment. Collections hydrate linked HTTP + Extract nodes in order; environments map enabled variables into the Environment dialog.
- Inspector sections include retry controls, response persistence, Monaco JSON editor, and collapsible help so the panel stays compact.
4. Node settings modal
Selecting any node opens a modal with two tabs: Settings for configuration and Runtime for the latest execution output. Run the flow to populate runtime details, and close the modal with the top right button. The Runtime tab offers a GUI view for HTTP nodes and a JSON view for full details.
Use the Skip toggle to bypass a node while still allowing downstream nodes to execute. Use Hide (Dormant) to disable a node entirely until re-enabled.
If validation fails, the Runtime tab shows a list of issues so you can fix missing URLs, paths, or assertions before running again.
5. Data flow & template scope
Every node evaluates templates inside a scope containing the latest upstream response, previous node results (`nodes[nodeId]`), and the shared `variables` object. Template examples:
{{ response.body.token }}
{{ nodes["node-123"].response.body.user.id }}
{{ variables.loginResponse.token }}
{{ env.API_TOKEN }}
{{ env.baseUrl }}
Use Extract nodes or HTTP nodes with “Persist response body” to add new entries to variables. Environment variables (set in the toolbar Environment dialog) are always reachable via {{ env.MY_KEY }}, and shared base URLs/default headers are accessible through the same env object (e.g. {{ env.baseUrl }}).
6. Environment profiles
Click the Environment button in the toolbar to define shared settings:
- Base URL – automatically prefixes any relative HTTP node URL. Enter
/orders and SpecRunner sends https://api.example.com/orders (assuming your base ishttps://api.example.com). - Default headers – merged into each HTTP node before its own headers; node-level values override matching keys.
- Environment variables – arbitrary key/value pairs plus automatic entries like
env.baseUrl andenv.defaultHeaders. Reference them anywhere with {{ env.API_TOKEN }} and inspect them inside the debugger’s environment block.
Environment data is saved alongside your flow (snapshots, import/export, and persistence) so switching between projects keeps the right defaults.
6. If node operators
===
Checks strict equality.
includes
Substring or array membership.
isGreater / isLower
Numeric comparison (> / <).
isArray / isString / isNumber
Type guards.
isNull / isNotNull
Nullish checks.
Unary operators (isArray / isNull / etc.) ignore the right-hand value. Expressions should use spaces, e.g.{{ response.status }} isLower 400
7. Execution engine
- Topologically sorts nodes (left → right, respecting edges).
- Honors branch handles; nodes are skipped if their incoming branch is false or their dormant toggle is on.
- Status colors: yellow = running, green = success, red = failed, gray = skipped/dormant.
- Execution stops on first failure and writes detailed logs + stored variables to the debugger.
- Enable Continue on error from the toolbar to keep executing nodes even after a failure (great for collecting all assertion results in one run).
8. Debugger & runtime
- Logs – terminal-style stream with timestamps, inline search/highlight, and quick copy. Expand the debugger to give logs more vertical space while testing complex flows.
- Variables – JSON view of the shared
variables object plus per-node request/response payloads. Content wraps, so you never have to scroll horizontally. - Runtime tab – per-node output lives in the modal Runtime tab, with request/response payloads for HTTP nodes.
9. Persistence, import/export & UX enhancements
- Flows persist automatically in local storage (nodes + edges).
- Theme preference (light/dark) is saved via `useUIStore`.
- Side panel width, palette collapse, and search help organize large projects.
- Monaco editor adopts the current theme for consistent JSON editing.
- Use the toolbar to Export Flow, import Postman collections/environments, or load a sample flow from the toolbar dropdown (Quick HTTP or Loop + Products).
- Clear all resets the canvas, environment, and runtime debugger state.
- Zoom indicator shows live zoom percentage while you pan/zoom the canvas.
- Backend proxy routes HTTP requests through a Netlify function when enabled in the toolbar menu.
- Postman/Insomnia export exports HTTP nodes in order; non-HTTP nodes are skipped.
10. Sample end-to-end flow
Recreate this scenario to see how nodes, branching, and variables tie together. You can also load the Loop + Products sample from the toolbar to see a loop container with HTTP + Log nodes inside.
- HTTP “Get user” –
GET {{ env.baseUrl }}/users/{{ env.sampleUserId }}. - Extract “User” – store
{{ response.body }} → variables.user. - HTTP “List products” –
GET {{ env.baseUrl }}/products?limit=5. - Extract “First product” – store
{{ response.body.products[0] }} → variables.firstProduct. - HTTP “Get product details” –
GET {{ env.baseUrl }}/products/{{ variables.firstProduct.id }}. - Assertion –
{{ response.body.title }} isNotNull. - Log –
User {{ variables.user.username }} previewed {{ response.body.title }} (${{ response.body.price }}).