Skip to content

The API client ​

A request builder and response viewer, scoped to a project, storing its collections as files in that project's repository.

Collections are files ​

A collection is a directory containing bruno.json — Bruno's own format. Jarvis finds them by looking in the project root and one level below it. Requests are .bru files, environments live in environments/.

<project>/api/
  bruno.json
  orders/
    list.bru
    create.bru
  environments/
    local.bru

This means your requests are diffable, reviewable in a pull request, openable in Bruno desktop, and runnable in CI with bru run. It also means a collection whose bruno.json sits at the root of an ordinary repository shows only the folders that actually contain requests — not src/, config/ and every __pycache__.

+ request, + folder, + collection and import are in the sidebar; ✎ and × on a row rename and delete. Renaming a request moves the file and rewrites its meta.name, since that is the name everything displays.

Sending ​

⌘Enter sends, ⌘S saves. Copy as code copies the request as a cURL command, JavaScript fetch or Python requests, with variables resolved — a command with still in it is a note, not a command.

Find in response, above the body, marks every match in it (ignoring case), counts them, and steps through them with Enter and Shift+Enter; Escape clears it. The search stays when you send again, so watching for a value across retries is one field, not a re-type.

Requests are issued from the main process, not from a browser origin. That is why CORS never applies here, and it is the reason this is a native tab rather than a hosted web app.

Variables and environments ​

resolves from the selected environment. env opens the editor: add, rename and remove variables, and mark one secret.

A variable with no value is left as its literal and reported beside the response, rather than blanked. A request to https:///api is a confusing failure; /api says exactly what is missing. When the URL itself depends on a missing variable, the failure names it.

Auth ​

bearer, basic, apikey (header or query) and oauth2. The OAuth2 grants are client credentials, password, and authorization code — the last drives its redirect through a Workspace tab, so you approve in the app rather than copying a code back from another browser.

Bodies ​

json, text, xml, graphql, form-urlencoded and multipart. The JSON body says whether it is valid JSON and why not when it isn't; format pretty- prints it and leaves anything half-typed alone. Multipart file fields are chosen through the system picker — a hand-typed path is how you get a request that fails at send time on a file that never existed.

Assertions, scripts and tests ​

An assert block is evaluated: a target (res.status, res.body.a.b, res.headers.content-type, res.responseTime), an operator, an operand. Each result says what it actually got, because a failure that only says "failed" sends you back to guess.

script:pre-request, script:post-response and tests run, in a context holding bru, req, res, console, expect and test. Variables a pre-request script sets are available to the request that follows. What they print appears on the console tab; their assertions are counted beside the declarative ones.

These scripts come from your own repository, and Jarvis already runs that repository's code — the Terminal tab is a login shell in it and the Editor is a full VS Code. A .bru script is not a new trust boundary. What the sandbox provides is containment against accident and a timeout so a runaway loop cannot take the window with it; it is not a security boundary and is not treated as one.

Cookies, history and network settings ​

cookies shows the jar — per project, persisted, with the flags that decide whether each cookie is sent. A login that answers 302 with Set-Cookie is handled: redirects are followed by hand so the cookie is caught on the hop that set it.

hist shows the last 200 calls per project. net sets a proxy, a timeout, and whether to verify certificates. Verification is on unless you turn it off.

From the paired phone ​

The same collections, requests and environments are reachable from a paired phone (see remote access) — sending and saving work the same way, with three differences the phone-side trust boundary enforces: a send from the phone never runs script:pre-request/script:post-response or tests, and never starts an OAuth2 grant (those stay laptop-only); the laptop never reads a path off the phone's request for a multipart file field — the file is uploaded first, and the request carries only the id that upload returns; and a save from the phone never writes a script or hook block, only the fields the phone's own editor can actually show. Importing a Postman collection from the phone goes through the same upload step, but only a phone-originated import is bounded — a cap on how many requests it may produce, a decoded size/depth/node-count check, and a scan for control characters in the names and values the conversion produces; the same import triggered locally from the laptop's own file picker carries none of those bounds.

What is not here ​

No cookie editor (only viewing and deleting), no code generation beyond cURL, JavaScript fetch and Python requests, and no per-request network override — the settings are per project. Postman import handles v2.0 and v2.1 collections.

MIT licensed. Third-party components keep their own licences — see NOTICE.