Bruno API Client 4.2 Setup: Replace Postman in 13 Steps [2026]

Postman’s desktop app has spent the better part of 2026 pushing users toward cloud sync, team workspaces, and a pricing page that gets more confusing every quarter. That shift is the biggest reason Bruno, a git-native, offline-first API client, has moved from “interesting open-source project” to a tool developers actually install on new machines by default. Bruno hit version 4.2.0 on September 23, 2026, adding scripting support across the full gRPC request lifecycle and read-only Collection Docs rendering for GraphQL and gRPC calls, followed a week later by a 4.2.1 security patch on September 30, 2026.

This tutorial walks through installing Bruno 4.2.1, migrating an existing Postman collection, building REST, GraphQL, and gRPC requests, writing pre-request and post-response scripts, setting up environments and secrets, and wiring the whole thing into a CI pipeline with Bruno’s CLI tool. By the end you will have a working, git-committed API test suite that runs the same way on your laptop and inside GitHub Actions. No images, no video required, just a terminal and about 90 minutes.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

Why developers are moving off Postman in 2026

Bruno’s pitch is simple: your API collections are just files on disk, written in a plain-text markup format called Bru, and they belong in the same git repository as the code that calls those APIs. Postman, Insomnia, and most hosted alternatives store collections in a cloud account by default, which means every request, every secret reference, and every test script lives behind a login wall you don’t control. Bruno flips that. A collection is a folder. Commit it, branch it, review it in a pull request, diff it like any other text file.

That git-native design is also why Bruno has stayed lightweight. The project describes itself as a fast, offline-first alternative to Postman and Insomnia, and it ships as a native desktop app without an embedded browser runtime bloating the install. For teams that got burned by a SaaS API tool changing its free-tier limits overnight, owning the collection files outright removes that risk entirely. It’s the same reasoning that pushed a lot of teams toward self-hosted or locally-run developer tools over cloud-locked ones this year.

The 4.2.0 release specifically targeted a gap that used to send people back to Postman: gRPC testing. Before this release, Bruno could send gRPC calls but scripting around them was limited. Now you can run JavaScript before a gRPC request fires and after a response lands, matching the scripting model that already existed for REST and GraphQL. Combined with read-only Collection Docs for GraphQL and gRPC requests, a collection now functions as living documentation that teammates can open without even running a request.

Prerequisites and versions

Before starting, confirm you have the following in place. Version mismatches are the single biggest source of “it works on my machine” friction with Bruno, since the Bru file format has evolved across major releases.

RequirementMinimum versionNotes
Bruno desktop app4.2.1 (Sept 30, 2026)Includes the September 30 security patch; don’t stay on 4.1.x
Node.js18 LTS or newerRequired only if you use the Bruno CLI (bru) for CI runs
npm or pnpmnpm 9+ / pnpm 8+Used to install the CLI package globally or per-project
Git2.30+Collections are plain folders, so any modern git works
A test APIAny REST, GraphQL, or gRPC serviceThis guide uses a public REST endpoint and a local gRPC demo server
Operating systemWindows 10/11, macOS 12+, or Linux (deb/rpm/AppImage)Bruno ships native builds for all three

You do not need a Postman account to follow along, but if you’re migrating an existing team collection, keep your Postman export handy for Step 4. You also don’t need Docker, though it’s useful if you want to spin up a disposable gRPC test server rather than pointing at a real one.

Step 1: Install Bruno 4.2.1

Bruno publishes native installers for every major platform. Grab the one matching your OS from the official Bruno site rather than a third-party mirror, since the installer is also how you get automatic update checks going forward.

macOS users can install via Homebrew, which is the fastest path to staying current:

brew install bruno

Windows users can use the Chocolatey or winget package managers:

winget install Bruno.Bruno

Linux users have Snap, AppImage, deb, and rpm builds. For Debian-based distros:

sudo snap install bruno

After installation, open Bruno and check the version in the About panel. If it reports anything older than 4.2.1, run the in-app update check manually, since some package managers lag behind the official release feed by a few days. The Bruno changelog is the fastest way to confirm you’re current without digging through release notes scattered across platforms.

Step 2: Install the Bruno CLI for scripted and CI runs

The desktop app is where you build and debug requests, but the CLI (bru) is what actually runs your collection in a pipeline with no GUI attached. Install it globally with npm:

npm install -g @usebruno/cli
bru --version

If you’d rather not pollute your global npm space, install it as a dev dependency per project instead:

npm install --save-dev @usebruno/cli
npx bru --version

Keep the CLI version in sync with your desktop app’s major version. A CLI from an older Bru format revision can silently skip newer script blocks, which is a subtle failure mode that looks like a passing test when it actually ran zero assertions.

Step 3: Create your first collection

Open Bruno and click “Create Collection.” Name it something that matches the repo it will live in, for example payments-api-tests. Bruno asks for a location on disk, which should be inside (or alongside) your actual project repository, not in some separate “API tools” folder that never gets committed.

Inside the new collection, create a folder named rest and add a request. Right-click the collection, choose “New Request,” set the method to GET, and point it at a public test API:

GET https://jsonplaceholder.typicode.com/users/1

Click Send. You should see a 200 response with a JSON body containing a user record. Under the hood, Bruno just wrote a .bru file to disk. Open that file in any text editor and you’ll see the whole request as plain text:

meta {
  name: Get User
  type: http
  seq: 1
}

get {
  url: https://jsonplaceholder.typicode.com/users/1
  body: none
  auth: none
}

This is the entire point of Bruno’s design. There’s no binary blob, no proprietary export format, nothing that requires an account to read. A teammate without Bruno installed can still open this file and understand exactly what the request does.

Step 4: Migrate an existing Postman collection

If your team already has a Postman collection with dozens or hundreds of requests, you don’t need to rebuild it by hand. Export the collection from Postman first: open the collection, click the three-dot menu, select Export, and choose Collection v2.1 (recommended) format. This produces a single JSON file.

In Bruno, go to Import Collection and select that JSON file. Bruno parses the Postman format and recreates folders, requests, headers, and body types as Bru files. Review the import carefully afterward, because a few things don’t translate cleanly:

  • Postman’s pm.environment.get() calls in scripts need to become Bruno’s bru.getEnvVar() equivalents
  • Postman collection-level pre-request scripts map to Bruno’s collection script:pre-request block, but variable scoping differs slightly
  • OAuth 2.0 token caching behaves differently between the two tools, so re-test any auth flow after import
  • Postman’s dynamic variables like {{$timestamp}} have no direct Bruno equivalent and need a small script function instead

Budget an afternoon for a collection with heavy scripting, and about 20 minutes for one that’s mostly plain REST calls with static headers.

Step 5: Set up environments and variables

Every real API test suite needs to run against at least two targets: a local or staging environment and production. In Bruno, click the environment dropdown in the top right and select “Configure.” Create two environments, local and staging, each with a baseUrl variable:

# local environment
baseUrl=http://localhost:3000
apiKey=dev-key-not-secret

# staging environment
baseUrl=https://staging-api.example.com
apiKey={{process.env.STAGING_API_KEY}}

Update your request URL to use the variable instead of a hardcoded host:

GET {{baseUrl}}/users/1

Switch environments from the dropdown and re-send the same request. This is the pattern that lets one collection serve your entire team without anyone editing URLs by hand before a demo.

Step 6: Handle secrets without committing them to git

Since collections live in git, any API key typed directly into an environment file gets committed along with it. Bruno solves this with a separate secrets layer that stores sensitive values outside the synced .bru files, in an OS-level secure store on your machine.

Mark a variable as a secret by prefixing it in the environment editor, and Bruno will keep that specific value out of the file that gets committed, replacing it with a reference. For CI environments where there’s no OS keychain, pass secrets as environment variables instead and reference them from scripts:

script:pre-request {
  bru.setVar("apiKey", process.env.STAGING_API_KEY);
}

Never paste a raw production credential into a request header field directly. It’s an easy habit to fall into during a debugging session, and it’s exactly how secrets end up in git history permanently, even after you “remove” them in a later commit.

Step 7: Write pre-request and post-response scripts

Scripts are where Bruno earns comparisons to Postman rather than to simpler tools like curl or HTTPie. A pre-request script runs before the request fires, useful for generating a timestamp, signing a request, or fetching a fresh auth token. Add this to a request’s “Script” tab under “Pre Request”:

script:pre-request {
  const timestamp = Date.now();
  bru.setVar("requestTimestamp", timestamp);
}

Post-response scripts run after the response arrives and are the right place for assertions. Add this under “Post Response”:

script:post-response {
  const body = res.getBody();
  if (res.getStatus() !== 200) {
    throw new Error(`Expected 200, got ${res.getStatus()}`);
  }
  if (!body.id) {
    throw new Error("Response missing expected 'id' field");
  }
  bru.setVar("lastUserId", body.id);
}

Chain requests together by reusing variables set in one request’s script inside the next request’s URL or body. This is how a single collection can simulate a full workflow, like creating a resource, reading it back, and then deleting it, in sequence.

Step 8: Add dedicated assertions and tests

Beyond inline scripts, Bruno has a separate Assert tab for simple checks that don’t need a full script, and a Tests tab for more structured test blocks that report pass/fail individually in the results panel, rather than throwing a single error that stops execution.

tests {
  test("status code is 200", function() {
    expect(res.getStatus()).to.equal(200);
  });

  test("response has email field", function() {
    const body = res.getBody();
    expect(body).to.have.property("email");
  });

  test("response time under 500ms", function() {
    expect(res.getResponseTime()).to.be.below(500);
  });
}

This syntax will look familiar if you’ve written Mocha or Chai-style tests before, since Bruno’s test runner is built on the same assertion patterns. Each test shows up individually in the response panel, which makes it much easier to spot exactly which check failed without reading a stack trace.

Step 9: Build and test a GraphQL request

Create a new request and set its type to GraphQL. Bruno gives you a dedicated query editor plus a variables panel, matching the structure of a real GraphQL call rather than forcing you to hand-assemble a JSON body:

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

In the Variables panel below the query editor:

{
  "id": "1"
}

Point the request at any GraphQL endpoint you have access to. One of the additions in Bruno 4.2.0 is read-only Collection Docs rendering for GraphQL requests, which means once you save this request with a description, teammates browsing the collection see a rendered doc view of the schema and query without needing to open the editor. For a deeper reference on the query language itself, the official GraphQL documentation covers schema design patterns that pair well with this workflow.

Step 10: Build and script a gRPC request (new in 4.2.0)

This is the headline feature of the 4.2.0 release. Create a new request and set its type to gRPC. You’ll need a .proto file describing the service, or a running server with reflection enabled. Point Bruno at the server address and either load the proto file manually or let reflection discover the available methods:

grpc {
  url: localhost:50051
  proto: ./protos/user_service.proto
  method: UserService/GetUser
}

Set the request payload matching the proto message definition:

{
  "user_id": "1"
}

The new part is scripting support across the entire call lifecycle. Before 4.2.0, gRPC requests in Bruno couldn’t run pre-request or post-response scripts at all, which made chaining a gRPC call into a larger workflow impossible. Now the same script blocks you used for REST work here too:

script:post-response {
  const body = res.getBody();
  bru.setVar("grpcUserId", body.id);
  console.log("gRPC call returned user:", body.name);
}

If you don’t have a gRPC server handy to test against, the grpc.io documentation includes a quick-start guide for spinning up a minimal demo server in several languages, which is the fastest way to confirm Bruno’s gRPC support actually works on your machine before pointing it at something real.

Step 11: Organize a multi-request workflow with folders and sequencing

Real API test suites rarely consist of isolated requests. Group related requests into a folder, for example checkout-flow, containing a create-order request, a get-order-status request, and a cancel-order request in that order. Bruno runs folder requests in sequence number order, which you set in each request’s meta block:

meta {
  name: Create Order
  type: http
  seq: 1
}

Right-click the folder and choose “Run” to execute every request inside in order, with variables set by earlier requests available to later ones. This is how you simulate an entire user journey, not just a single endpoint, in one click.

Step 12: Run your collection headlessly with the CLI

With the collection built, run it from the terminal using the CLI installed in Step 2. From inside the collection’s root folder:

bru run --env staging --output results.json

To run just one folder instead of the whole collection:

bru run checkout-flow --env staging

You should see a terminal summary listing each request, its status code, response time, and whether its tests and assertions passed. A failing test exits with a non-zero status code, which is exactly the signal a CI pipeline needs to fail the build.

Example output for a healthy run:

Running Collection: payments-api-tests
Environment: staging

✓ Get User (187ms)
  ✓ status code is 200
  ✓ response has email field
  ✓ response time under 500ms

✓ Create Order (243ms)
✓ Get Order Status (156ms)
✓ Cancel Order (198ms)

4 requests, 4 passed, 0 failed
Total time: 784ms

Step 13: Wire Bruno into GitHub Actions

Add a workflow file that installs the CLI and runs your collection on every pull request. Create .github/workflows/api-tests.yml in your repository:

name: API Tests

on:
  pull_request:
    branches: [main]

jobs:
  bruno-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
      - name: Install Bruno CLI
        run: npm install -g @usebruno/cli
      - name: Run collection
        run: bru run --env staging --output results.json
        env:
          STAGING_API_KEY: ${{ secrets.STAGING_API_KEY }}
      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: bruno-results
          path: results.json

Store the actual API key in your repository’s GitHub secrets, not in the workflow file itself. This is the piece that completes the loop: the same collection you built and debugged visually in the desktop app now gates every pull request automatically, with zero duplication of test logic between local development and CI. For general guidance on securing workflow secrets, GitHub’s own Actions documentation covers the encrypted-secrets model this setup depends on.

Common pitfalls when setting up Bruno

These are the mistakes that come up most often when teams move from Postman to Bruno, based on the behavior differences between the two tools.

  • Committing the wrong environment file. If secrets aren’t marked properly in Step 6, a raw API key ends up in a committed .bru environment file. Check your diff before every commit involving environment changes.
  • Mixing CLI and desktop versions. A CLI still on 4.1.x silently misreads some 4.2.0 script blocks, particularly gRPC lifecycle scripts, and reports false passes. Pin both to 4.2.1.
  • Assuming Postman import is lossless. OAuth flows, dynamic variables, and collection-level scripts from Postman often need manual fixes after import, as covered in Step 4. Always re-test auth-dependent requests after migrating.
  • Forgetting folder sequence numbers. If two requests in a folder share the same seq value, execution order becomes unpredictable, which breaks workflows that depend on one request’s output feeding the next.
  • Running gRPC without reflection or a proto file. Bruno can’t guess a service’s method signatures. Without either a loaded proto file or server-side reflection enabled, the gRPC request editor has nothing to validate against.
  • Treating the Assert tab and Tests tab as interchangeable. Assert checks stop a request’s validation at the first failure; Tests report every check independently. Use Tests when you need to see all failures at once, not just the first one.
  • Not pinning the CLI version in CI. Installing @usebruno/cli without a version pin means a future major release can silently change your pipeline’s behavior. Pin it explicitly, for example @usebruno/[email protected].

Troubleshooting Bruno setup issues

Here are the specific errors and symptoms that come up most often, with what actually fixes them.

  • “Collection not found” when running the CLI. You’re not in the collection’s root directory. The CLI looks for a bruno.json file in the current working directory; cd into the folder containing it first.
  • Environment variables show as literal {{baseUrl}} text in requests. No environment is selected. Check the dropdown in the top right of the Bruno window and confirm an environment is active, not set to “No Environment.”
  • gRPC request fails with a reflection error. The target server doesn’t have server reflection enabled. Either enable reflection on the server or load the .proto file manually in the request’s proto field.
  • Scripts throw “bru is not defined.” You’re running an older Bru file format against the 4.2.x runtime, or testing a script in an unsupported context like a GraphQL subscription. Re-save the request in the current app version to rewrite the file format.
  • CI run passes locally but fails in GitHub Actions. Almost always a missing environment secret. Confirm the secret name in your workflow YAML matches exactly what’s configured in repository settings, including case.
  • Postman import skips some requests entirely. Postman collections using collection-level variables referenced through nested folder scopes sometimes don’t map cleanly. Export smaller sub-collections and import them individually to isolate the broken request.
  • Response time shows as much higher than expected. Check whether a pre-request script is making an additional network call, like fetching a fresh token, before the main request. That round trip counts toward the total.
  • App won’t update past 4.1.x via Snap. Snap’s update channel sometimes lags the official release feed. Run sudo snap refresh bruno manually, or switch to the AppImage distribution for faster access to patch releases like 4.2.1.

Advanced tips once the basics are working

Once your collection is running cleanly in CI, a few extra patterns are worth adopting. First, use Bruno’s vars:pre-request and vars:post-response blocks for simple variable assignment instead of full script blocks when you don’t need conditional logic. They run faster and keep the Bru file more readable for anyone scanning it later.

Second, split large collections into multiple sub-collections by service boundary rather than one giant collection for an entire platform. A 400-request collection for a monolith API is harder to review in a pull request than four 100-request collections split by domain, and folder-level CI runs let you test only the service that actually changed.

Third, for teams running a mix of REST, GraphQL, and gRPC against the same backend, use a shared collection-level pre-request script to normalize auth token fetching across all three protocol types, rather than duplicating the same token-fetch logic in every individual request.

Complete working project: a checkout flow test suite

Here’s how the pieces from this tutorial combine into one real collection. The folder structure on disk looks like this:

payments-api-tests/
├── bruno.json
├── environments/
│   ├── local.bru
│   └── staging.bru
├── checkout-flow/
│   ├── 1-create-order.bru
│   ├── 2-get-order-status.bru
│   └── 3-cancel-order.bru
├── graphql/
│   └── get-user-profile.bru
└── grpc/
    └── get-inventory-count.bru

The bruno.json file at the root defines collection-level settings:

{
  "version": "1",
  "name": "payments-api-tests",
  "type": "collection",
  "scripts": {
    "moduleWhitelist": ["crypto"]
  }
}

The create-order request chains into the next two steps through a post-response script that saves the new order ID:

post {
  url: {{baseUrl}}/orders
  body: json
}

body:json {
  {
    "items": [{"sku": "SKU-1001", "qty": 2}],
    "customerId": "{{testCustomerId}}"
  }
}

script:post-response {
  const body = res.getBody();
  bru.setVar("orderId", body.id);
}

The get-order-status request reuses that saved orderId:

get {
  url: {{baseUrl}}/orders/{{orderId}}
}

tests {
  test("order status is pending", function() {
    const body = res.getBody();
    expect(body.status).to.equal("pending");
  });
}

Running the whole folder with bru run checkout-flow --env staging executes all three requests in sequence, passing the order ID from the first request through to the second and third automatically. That’s the complete pattern: build visually in the desktop app, commit the Bru files to git, and run the identical suite from the CLI in CI, with no translation step between the two.

Keeping Bruno in sync with your OpenAPI spec

Many teams don’t write Bruno requests from scratch. If your backend already publishes an OpenAPI 3.0 or 3.1 document, you can import it directly and let Bruno generate a starting collection with one request per documented endpoint. Open Import Collection, choose OpenAPI as the source format, and point it at your spec file or a live /openapi.json endpoint:

bru import openapi ./api/openapi.yaml --output ./collections/generated

This generates one request per path and method combination, with request bodies pre-filled from the schema’s example values where the spec defines them. It’s a faster starting point than writing every request by hand, but treat it as a first draft rather than a finished suite: generated requests have no scripts, no assertions, and no chained variables, so you still need to go through Steps 7 through 11 for any endpoint that matters enough to test properly.

The practical benefit shows up when your API contract changes. Re-running the import against an updated spec regenerates only the structural parts (URLs, methods, parameter names) while your manually added scripts and tests live in separate files untouched by the regeneration. Keeping the generated requests and the hand-written workflow folders in separate directories, as shown in the project structure in Step 13’s example, avoids an accidental overwrite wiping out scripting work. For teams that haven’t settled on a spec format yet, the OpenAPI Specification reference is the clearest starting point for understanding what a well-documented API contract should contain before you try to generate tests from it.

Security considerations when self-hosting your API test suite

Moving API collections into git solves the secret-sprawl problem of a shared cloud workspace, but it introduces a different set of things to get right. The biggest one is scope: a Bruno collection committed to a monorepo is visible to everyone with read access to that repo, which in a large organization can be a much bigger audience than the handful of people who had access to a private Postman workspace.

Treat environment files the same way you’d treat a .env.example file: commit the structure, never the values. A useful habit is adding a .gitignore entry for any environment file suffixed .local.bru, and keeping production-adjacent credentials out of the repository entirely, fetched instead from your CI provider’s secret store or a dedicated secrets manager at run time. If your organization already runs infrastructure through policy-as-code tooling, treating API test credentials with the same discipline you’d apply to cluster orchestration secrets is a reasonable default rather than overkill.

Second, audit what your pre-request and post-response scripts can actually reach. Bruno scripts run with access to Node’s built-in modules depending on your collection’s moduleWhitelist setting, so a script pulling in a module you didn’t intend to allow is a real supply-chain surface, however small. Keep the whitelist short and reviewed, the same way you’d review a new npm dependency before adding it to a production build.

Bruno CLI command reference

The CLI covers more than the single bru run command used throughout this tutorial. These are the commands worth knowing before you build out a larger pipeline:

CommandWhat it does
bru runExecutes a collection or folder, running every request in sequence order
bru run --env <name>Runs against a specific named environment instead of the default
bru run --output <file>Writes a machine-readable results file, useful for CI artifact uploads
bru run --tests-onlySkips requests that have no test or assertion blocks attached
bru run --bailStops execution on the first failing request instead of running the full suite
bru import openapiGenerates a starting collection from an OpenAPI spec file or URL
bru --versionPrints the installed CLI version, used to confirm it matches the desktop app

The --bail flag is worth calling out specifically for CI use. Without it, a single broken endpoint still lets the rest of the suite run to completion, which is usually what you want for a full report. With it, the pipeline fails fast, which is useful for a pre-merge check where you want the fastest possible signal rather than a complete picture. Pick based on whether the workflow is a thorough nightly run or a quick pull-request gate, similar to how teams choose between fast and exhaustive modes in other build-tool pipelines depending on whether they’re optimizing for iteration speed or full validation.

Bruno vs Postman: what actually changes day to day

AspectBruno 4.2.1Postman
Collection storagePlain text files on disk, git-nativeCloud account by default, local export optional
LicenseMIT, fully open sourceProprietary, free tier with limits
gRPC scriptingFull pre-request and post-response scripting since 4.2.0Supported, tied to workspace sync model
Offline useFully offline-first by designWorks offline but nudges toward cloud sync
Code review of API changesNative, since collections are diffable textRequires export step to review meaningfully
CLI for CI@usebruno/cli, free, matches desktop formatNewman, free, separate maintenance track

Neither tool is strictly better in every category. Postman’s team collaboration features, mock servers, and API monitoring dashboards are more mature simply because the product has existed longer. Bruno’s advantage is specifically for teams that already live in git-centric workflows and don’t want API testing to be the one part of their stack that doesn’t fit that model. If your team already treats infrastructure and configuration as code, treating API collections the same way is a natural extension, similar to how teams standardized on CLI-first developer tooling over GUI-only alternatives this year.

When Bruno isn’t the right fit

Bruno’s git-native model works best for teams where the people writing API tests also have git access and are comfortable resolving merge conflicts in text files. If your organization has a dedicated QA team without engineering-level git workflows, or you need built-in API monitoring and uptime alerting, a hosted platform with those features built in may still be the better fit for that specific use case. Bruno doesn’t try to be a monitoring platform, and bolting that on yourself through CI cron jobs adds maintenance overhead that a purpose-built service already handles.

Database-heavy API testing is another edge case worth a quick note. If your test suite needs to seed and verify data at the database layer in the same workflow as the API calls, pairing Bruno’s HTTP layer with a dedicated query tool, rather than trying to shove raw SQL into a pre-request script, tends to produce more maintainable suites. Teams evaluating that kind of stack split often land on comparisons like Go ORM and query-builder tooling for the database side while keeping Bruno purely on the HTTP and RPC side.

Frequently asked questions

Is Bruno really free, including for commercial use?
Yes. Bruno is released under the MIT license, which permits commercial use, modification, and redistribution without licensing fees. There is no paid tier gating core features on the desktop app or CLI.

Can Bruno fully replace Postman for a team already using Postman workspaces?
For request building, scripting, and CI testing, yes. For team features built around Postman’s cloud account model, like built-in API monitoring dashboards or Postman’s mock server hosting, Bruno doesn’t have a direct equivalent and you’d need a separate tool for those specific features.

Does Bruno support WebSocket testing?
Bruno’s core request types are HTTP (REST), GraphQL, and gRPC. Check the current changelog before assuming WebSocket support, since the project adds protocol support incrementally and the exact feature set shifts between releases.

What happened in the September 30, 2026 security patch?
Bruno 4.2.1 was released as a security-fix release immediately following the 4.2.0 feature release. Teams should update to 4.2.1 rather than staying on 4.2.0, since security patches in desktop tools that execute local scripts are not optional upgrades.

Can I share a Bruno collection with someone who doesn’t have Bruno installed?
Yes, in the sense that the Bru files are plain text and readable in any editor. To actually run requests, they need the Bruno app or the CLI installed, but reviewing the collection’s contents requires nothing beyond a text editor and git access.

Does Bruno work with OpenAPI specifications?
Bruno supports importing from OpenAPI spec files to generate a starting collection, in addition to Postman collection import. This is useful for teams that maintain their API contract in OpenAPI format and want a Bruno collection to stay roughly in sync with it.

How do I keep the Bruno CLI version in sync across a team?
Pin the exact CLI version in your project’s package.json as a dev dependency rather than relying on a global install, and document the required desktop app version in your repository’s README so new team members install matching versions from day one.

Is there a hosted version of Bruno for teams that want cloud sync anyway?
Bruno’s core product is local-first by design, which is the entire reason teams choose it over cloud-default tools. If your team specifically wants hosted sync features, that’s a signal Bruno’s design philosophy may not match your workflow, and a cloud-native alternative would be a more direct fit.

Related Coverage

Sofia Lindström

Sofia Lindström

Editor-in-Chief

Sofia Lindström is the Editor-in-Chief at Tech Insider, where she leads editorial strategy and oversees coverage across AI, cybersecurity, and enterprise technology. With over a decade in Swedish tech journalism, she previously served as technology editor at Dagens Industri and covered the Nordic startup ecosystem for Breakit. Sofia holds an MSc in Media Technology from KTH Royal Institute of Technology and is a frequent speaker at Web Summit and Slush. She is passionate about making complex technology accessible to business leaders.

View all articles