Running Flows in CI
A flow you built by hand on the canvas doesn't have to stay a manual check. packages/flow_engine — the same executor that runs a flow inside the app — ships as a pure-Dart package with no Flutter and no flutter_rust_bridge dependency, so it runs under a plain dart VM. That's what powers the probe_flow CLI: a way to run a saved flow from a shell, a pre-commit hook, or a CI pipeline, with no Probe UI open.
The probe_flow CLI
Section titled “The probe_flow CLI”dart run flow_engine:probe_flow run <flow-id|name|path/to/flow.json> [options]dart run flow_engine:probe_flow list [--json]run takes a flow id, its name, or a path to an exported flow .json file. list prints every flow found in the flows directory.
| Flag | Description |
|---|---|
| --flows-dir <dir> | Flows directory. Default: ~/.probe/flows. |
| --collections-dir <dir> | Compose collections directory. Default: ~/.probe/compose. |
| --environment <name> | Select a Compose environment by name across the loaded collections. Default: each collection's own active environment. |
| --env KEY=VALUE | Set or override one variable. Repeatable. Highest precedence. |
| --env-file <path> | Load variables from a file — a JSON object ({"key": "value"}) or a dotenv-style file (KEY=VALUE per line, # comments, quoted values supported). |
| --proxy host:port | Route the flow's requests through a running Probe instance instead of sending them directly — use this if you want the run to show up in Probe's own traffic log. Default: direct (no proxy). |
| --json | Emit a machine-readable JSON report on stdout instead of the human-readable summary. |
| --quiet | Print only the final summary line. |
Environment precedence
Section titled “Environment precedence”When the same variable is defined in more than one place, the first match wins, in this order:
--env KEY=VALUE— highest precedence, always wins.--env-file.- The selected environment's own collection variables (
--environment, or each collection's active environment if unset).
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
| 0 | The flow ran and every node passed. |
| 1 | The flow ran but at least one node failed. |
| 2 | Usage or configuration error (bad flag, missing flow, malformed --env). |
| 3 | Execution error (couldn't load/parse the flow or its collections). |
That makes probe_flow run a normal CI gate: a non-zero exit fails the job.
Limitations running headless
Section titled “Limitations running headless”- Script nodes and script assertions need the app's embedded JavaScript engine, which doesn't exist headlessly. A script node fails with
script engine unavailable, and script assertions produce a failed outcome with the same message. If a flow you want to run in CI has script nodes, either replace them with declarative extractions/assertions, or accept that they'll fail every headless run — see Scripting in Flows. - App-only secret storage isn't reachable headlessly. SharedPreferences-backed globals and Keychain-stored secrets are read by the running app, not by the CLI process. Any required input that would normally come from one of those needs to be passed explicitly via
--envor--env-file; an unresolved required input warns on stderr rather than failing silently.
GitHub Actions example
Section titled “GitHub Actions example”name: API smoke test
on: [push]
jobs: flow-smoke-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dart-lang/setup-dart@v1
- name: Run smoke flow working-directory: packages/flow_engine run: | dart pub get dart run flow_engine:probe_flow run smoke \ --flows-dir "$GITHUB_WORKSPACE/flows" \ --collections-dir "$GITHUB_WORKSPACE/collections" \ --env apiKey="${{ secrets.API_KEY }}" \ --jsonCheck exported flows and collections into the repo (or a dedicated fixtures directory) so --flows-dir/--collections-dir can point at them without needing a live ~/.probe/.
The REST / MCP alternative
Section titled “The REST / MCP alternative”If you'd rather trigger a run from a process that talks to a running Probe instance instead of a headless CLI — an internal tool, a script, or an AI agent — use the internal REST API or MCP tools instead. Unlike the CLI, these require Probe's UI to be open (they ask the running app to execute the flow, so script nodes and app-stored secrets work normally):
GET /api/v1/flows— list saved flows.GET /api/v1/flows/{id}— get a flow's full definition.POST /api/v1/flows/{id}/run— run a flow; accepts an optionalenvironmentobject of variable overrides in the request body, and returns the run report. Returns503if no Probe instance is running to answer,504if it doesn't respond within the timeout (runs can take up to two minutes).
See the REST API reference for authentication and the full request/response shape. Over MCP, the same operations are probe_list_flows, probe_get_flow, and probe_run_flow — see the MCP Tool Reference.