Variables & environments

Where values come from, how ${{ }} interpolation works, and how to switch between staging and production without editing a flow.

Flows almost never hard-code everything. Base URLs differ between staging and production, tokens come back from login calls, secrets live outside Git. Bandura gives each of those a home.

Setting up .env — the 60-second version

What it is. A plain text file named .env holding one KEY=value per line. It’s the standard way to keep secrets and machine-specific settings out of your flow files.

Why you want it. Your .aether flows are committed to Git and shared with your team. You do not want an API key or password sitting in a committed file. So instead of writing the secret into the flow, you write a reference to it — ${{ env.API_KEY }} — and put the real value in .env, which Git ignores. The flow travels; the secret stays on your machine.

How to create it.

  1. In the file explorer, make a new file named exactly .env at the root of your workspace (the top folder you opened). The leading dot matters.

  2. Add your values, one per line — no quotes needed, no spaces around the =:

    BASE_URL=https://api.example.com
    API_KEY=sk_live_9f2c...
    EMAIL=test@example.com
    PASSWORD=hunter2
  3. Reference them from any node with ${{ env.NAME }}:

    url: ${{ env.BASE_URL }}/todos/1
    headers:
      Authorization: Bearer ${{ env.API_KEY }}

That’s the whole loop: .env holds the value → ${{ env.NAME }} pulls it in → the request sends the real thing at run time. Nothing in the committed .aether file ever contains the secret.

Where Bandura looks. The workspace-root .env applies to every flow. A flow can also have its own .env sitting next to it in the same folder — handy when one suite needs different values. Both are read automatically; you don’t register them anywhere.

It’s picked up live. Create or edit .env and Bandura re-reads it immediately — the ⚠ unset badges clear and ${{ env.X }} resolves without reopening the flow. (Fixed in a recent build; if a value looks stale, it isn’t a caching issue you need to work around.)

Keep it out of Git. Add .env to .gitignore and commit a .env.example (same keys, empty or dummy values) so teammates know what to fill in. The Environments view’s Set up for Git button scaffolds both for you.

Putting a variable inside a JSON body

A common snag: a request body is JSON, and JSON is strict about quotes. A placeholder that stands in for a string value must sit inside the quotes, or the body stops being valid JSON and you get Invalid JSON: Expected ',' or '}':

// ✗ wrong — the placeholder is bare, so the JSON is malformed
{ "token": ${{ env.REFRESH_TOKEN }} }

// ✓ right — the placeholder is inside the string
{ "token": "${{ env.REFRESH_TOKEN }}" }

Bandura substitutes the value first, then sends the body — so as long as the template is valid JSON with the placeholder quoted, the final request is valid too.

The five sources of a value

From lowest to highest precedence:

  1. Manifest environment — the selected environment’s variables from bandura.json (e.g. staging’s BASE_URL).
  2. .env file — key=value pairs in your workspace root. This is where secrets go; keep it in .gitignore.
  3. Flow variables — the variables: block at the top of the .aether file. Defaults that belong to the flow itself and travel with it in Git.
  4. Captured values — written into variables at run time by a request node’s capture or an ai-action’s output.
  5. Runtime overrides — values you type into a node’s Variables tab in the inspector. Highest precedence, stored locally on your machine only — never written into the flow file, so they can’t leak into a commit.

Referencing values

Inside YAML string values, use ${{ }} interpolation:

url: ${{ variables.baseUrl }}/users/${{ variables.userId }}
headers:
  Authorization: Bearer ${{ env.TOKEN }}

Inside JavaScript fields (check, expression, over, capture entries, script code), reference variables, env, and response directly — no ${{ }}.

env.X reaches .env and manifest-environment values; variables.X reaches flow variables, captures, and overrides.

Capturing values between steps

The login-then-use-the-token pattern:

- id: login
  type: request
  config:
    method: POST
    url: ${{ variables.baseUrl }}/auth/login
    body: '{ "email": "${{ env.EMAIL }}", "password": "${{ env.PASSWORD }}" }'
    capture:
      accessToken: response.body.token
  next: get_profile

- id: get_profile
  type: request
  config:
    method: GET
    url: ${{ variables.baseUrl }}/me
    headers:
      Authorization: Bearer ${{ variables.accessToken }}
  next: null

The graph badges the login node with ⤓ accessToken so the data flow is visible. (Plain response also works between adjacent requests, but it’s overwritten by every new request — capture is the durable way.)

Named environments: bandura.json

To switch a whole workspace between staging and production, define environments in a bandura.json manifest at the workspace root:

{
  "name": "My API Tests",
  "environments": {
    "staging": { "BASE_URL": "https://staging.api.example.com" },
    "production": { "BASE_URL": "https://api.example.com" }
  },
  "requiredEnv": [{ "name": "API_KEY", "secret": true }]
}
  • Create it from the Environments sidebar view (globe icon) — one click on Initialize bandura.json — or from Settings → Workspace.
  • Switch environments from the selector in the status bar (bottom-left), the Environments view, or the command palette (Environment: staging). Choose None (.env only) to use just your .env.
  • requiredEnv declares which .env keys the project expects, so a teammate who clones the repo can see at a glance what to fill in. Your selection is stored locally and remembered between sessions.

Commit bandura.json (it holds names and URLs); never commit .env (it holds secrets).

Unset values and the run guard

A node referencing a variable with no value gets a badge on the graph and in its Variables tab. Pressing Run with unset references doesn’t fail mysteriously mid-run — Bandura selects the first unresolved node, opens its Variables tab, and turns the button into Run anyway. Fill the value (or type a local override) and run, or press again to proceed regardless.

In the Variables tab, values that come from .env are masked by default — use the per-row reveal toggle when you need to see one.