Terminal & CI

API testing in CI: the flow you just ran is the check that gates the pull request.

Bandura flows are plain-text files, so running them in CI is one command with an exit code. Not an export, not a sync, not a hosted runner you rent. The desktop app, the bandura CLI, and the MCP server share one execution engine. What passes on your machine passes in the pipeline: same code, same file.

v1.0.0-rc.8 is out now on Windows and Linux. v1.0.0 final, and signed macOS builds, in August 2026.

Download for Windows or Linux
or

One email when the signed macOS build and 1.0.0 ship, no reselling, opt out any time.

Windows · Linux · macOS when notarized  ·  Reporters & exit codes ↓  ·  GitHub Actions ↓  ·  Catch spec drift in CI ↓  ·  What it won't do ↓

The whole integration

One command, and the exit code does the rest.

On npm now

# One command. The exit code is the whole contract.
bandura run "flows/**/*.aether" -r junit,github -o report.xml

@bandura/cli is published: a small installer that fetches a native binary for your platform. The 1.0.0 tag is held for launch day, so today you pin a release candidate. CLI reference →

No config file, no runner to register, no account. The exit code is the only interface your pipeline needs.

No quota on your own computer

There is no account to create, no backend to reach, and no metered runner. The engine executes locally: in the app, on your laptop, or on whatever machine the job already runs on. A nightly suite costs the same as a single run: nothing.

Flows are selected by path, by glob, or by a named collection from bandura.json, giving you an ordered suite reported as one result.

Reporters & exit codes

Output your pipeline already knows how to read.

Comma-separate the reporters to emit several at once: human output in the log, machine output in the artifact.

The four reporters
Reporter What it's for
pretty The default. Human-readable per-node results, with anything a node printed via log() dimmed underneath it.
json Structured results for your own tooling. Write it with -o.
junit JUnit XML for the test dashboard every CI system already has. log() output travels in <system-out>.
github GitHub Actions annotations, so a failure lands as an inline comment on the pull request rather than a line in a log nobody opens.
What each exit code means
Exit code Meaning
0All flows passed.
1A test failure: an assertion or a request failed.
2Usage or config error: bad flags, no flows matched, missing env.
3Parse or schema error in an .aether file or bandura.json.
4Internal error.

The split is the point: "the tests failed" and "the tool broke" must not look alike to a pipeline. bandura validate exits 3 on any schema error without executing anything, which is fast enough for a pre-commit hook or a network-free PR gate.

GitHub Actions

How to run API tests in GitHub Actions: annotations on the PR, JUnit in the artifacts.

The official action is a thin composite wrapper over the same CLI, with no logic of its own. It runs your flows with GitHub annotations, always writes the JUnit XML report and uploads it as an artifact, and fails the job on a non-zero exit.

# .github/workflows/api-tests.yml
- uses: actions/checkout@v4
- run: npx @bandura/cli run "flows/**/*.aether" --reporter github,junit -o report.xml
  env:
    BASE_URL: ${{ secrets.STAGING_URL }}
    ACME_API_KEY: ${{ secrets.ACME_API_KEY }}

Checkout stays your job. Secrets are never action inputs. The manifest's requiredEnv names map one-to-one onto secrets supplied through the workflow's env: block. The action publishes to the GitHub Marketplace at launch alongside the CLI.

# .gitlab-ci.yml. Any CI works: it's one command and an artifact.
api-tests:
  script:
    - npx @bandura/cli run "flows/**/*.aether" -r junit -o report.xml
  artifacts:
    reports:
      junit: report.xml

Nothing about the runner is GitHub-specific: GitLab, CircleCI, Jenkins, Buildkite, or a cron job on a box all work the same way: run the command, read the exit code, keep the JUnit file. The desktop app can also generate the workflow file for you via Export to CI/CD. Working pipeline recipes →

One suite, many environments

Point the same flows at staging without editing a file.

Named environments

-e staging loads that environment's non-secret values from bandura.json; an unknown name exits 2 instead of quietly running against the wrong host. Variables & environments →

Secrets stay outside the repo

Values arrive from the process environment or a dotenv file. Flows reference them as env.*, so credentials are never a field inside a committed .aether file.

Overrides and ceilings

--var KEY=VALUE injects a value at the highest precedence, --timeout sets a whole-run deadline per flow, and --bail stops at the first failing flow when a smoke check should fail fast.

Keep the suite honest

Let the pipeline notice the API moved.

The usual way an API test suite dies is quietly: the API changes, the tests keep asserting last quarter's shape, and nobody notices until they are all red at once.

# Fail the build when the committed spec no longer matches the flows
bandura export openapi --check -o specs/openapi.yaml

--check regenerates the spec from your flows and exits 1 when the committed one no longer matches. A staleness gate, not a nightly email. Deterministic and offline: no model is called.

The other direction, too

Drift detection compares a flow against an OpenAPI spec and names what moved: a renamed path, a changed method, a newly required parameter, each with a proposed fix you apply in the app, or that an agent applies for you through the MCP server. Same deterministic comparison in both places.

Self-healing against spec drift →

Where the line is

What Bandura will not do for your pipeline.

"Runs where your pipeline runs" is the whole design, and it cuts both ways. Four things you should expect to solve elsewhere.

No hosted monitors or schedules

There is no Bandura service running your flows every fifteen minutes against production. Remote and scheduled execution is a planned feature with no backend behind it yet. Until then, a cron trigger in your own CI is the answer, and it is the answer we would give even if the service existed.

The report is the artifact; there is no dashboard

The CLI writes JUnit XML and prints annotations; whatever your CI does with those is the trend view you get. Run history with full captured bodies is a desktop feature, stored in a local SQLite file, and it does not follow the suite into the pipeline.

The Action is not on the Marketplace yet

You can run the CLI in any pipeline today, because @bandura/cli is on npm as a release candidate. What is held for launch day is the 1.0.0 tag and the bandura-io/run Action, so the uses: workflow above is the shape to plan for rather than a line you can pin this afternoon. The npx recipe beside it works now.

One integration, not a catalogue

There is no CircleCI orb, no Jenkins plugin, no Buildkite step, and none is planned. Everywhere except GitHub you invoke a binary and read an exit code. That is a deliberate trade: a per-vendor plugin is a per-vendor thing to keep working, and a command is stable in a way a catalogue entry is not. Pipeline recipes →

Questions, answered straight

FAQ

How do I run API tests in a CI pipeline with Bandura?

One command: bandura run "flows/**/*.aether". It exits 0 when everything passed and non-zero otherwise, which is all any CI system needs. Add -r junit -o report.xml for a report your dashboard can read, and -r github on top for inline PR annotations. There is no account, no runner registration, and no per-run quota, because the engine executes on the machine the job is already paying for.

Do local runs and CI runs behave the same?

They are the same engine reading the same file. The desktop app, the CLI, and the MCP server all embed @bandura/execution-engine, so a flow that passes in the editor passes in the pipeline unless the environment differs, which is the failure you actually want CI to find.

How are secrets handled?

As environment variables, never as fields in the flow files. Flows read env.* from the process environment plus a dotenv file (--env-file, default .env), so the .aether files stay clean enough to commit. With the GitHub Action, secrets are supplied through the workflow's env: block. They are deliberately not action inputs, because inputs get echoed into logs.

What do the exit codes mean?

0 means every flow passed. 1 is a real test failure: an assertion or a request. 2 is a usage or config error, such as a bad flag, a glob that matched nothing, or a missing environment. 3 is a parse or schema error in an .aether file or bandura.json. 4 is an internal error. The split exists so a pipeline can tell 'the tests failed' apart from 'the tool broke'.

Can I run the suite faster than one flow at a time?

Use --concurrency <n> to run up to N flows in parallel; each flow still runs its own nodes in order. Output is buffered back into input order, so the report stays byte-identical regardless of how many ran at once. A parallel run does not produce a shuffled log.

Does CI need Node.js installed?

The CLI is distributed as a self-contained native binary per platform: macOS (arm64/x64), Linux (x64 and arm64, glibc and musl, so Alpine works), and Windows x64. Once it is installed there is no runtime to load. The npm installer that places it does run under Node, and the GitHub Action sets Node up for that step.

Can CI check that my OpenAPI spec still matches the flows?

Yes. bandura export openapi --check regenerates the spec from your flows and exits 1 if the committed file is out of date, which turns spec staleness into an ordinary failing check. The comparison is deterministic and offline: it never calls a model.

Keep reading

What makes a one-command pipeline possible.

Tests as files in your repo

CI can only run what it can clone. Flows are plain-YAML files that live beside the code they test, so there is nothing to export before a build.

Coming from Postman and Newman

Collections, environments, and pm.* scripts import, and the CI step becomes one binary instead of a Newman install to maintain.

AI without a third-party endpoint

ai-action nodes run headlessly against any OpenAI-compatible endpoint, including a model served on the runner itself.

Fix the red build with an agent

A local MCP server lets Claude Code or Cursor run the failing flow, read the actual response, and edit the YAML.

No login step in the pipeline

Why the binary reaches nothing but the API under test, and where every other piece of your data sits on disk.

gRPC, WebSocket, and SSE in the same suite

All ten node types run headlessly, so the streaming endpoints gate the pull request with the same one command as the REST calls.

v1.0.0-rc.8 is out now. v1.0.0 final in August 2026

Green locally, green in the pipeline.

The CLI is on npm now, so the pipeline step is two lines you can write this afternoon and one exit code your CI already knows how to read. Windows and Linux desktop builds are up alongside it. On a Mac, or waiting for the 1.0.0 tag and the Action? Leave an address and you get both links the day they exist.

Download for Windows or Linux

One email when 1.0.0, the Action, and the signed macOS build ship, no reselling, opt out any time.

Writing the step first? The pipeline recipes are working YAML for GitHub Actions and GitLab, and the CLI reference has every flag.