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.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
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.
| Requirement | Minimum version | Notes |
|---|---|---|
| Bruno desktop app | 4.2.1 (Sept 30, 2026) | Includes the September 30 security patch; don’t stay on 4.1.x |
| Node.js | 18 LTS or newer | Required only if you use the Bruno CLI (bru) for CI runs |
| npm or pnpm | npm 9+ / pnpm 8+ | Used to install the CLI package globally or per-project |
| Git | 2.30+ | Collections are plain folders, so any modern git works |
| A test API | Any REST, GraphQL, or gRPC service | This guide uses a public REST endpoint and a local gRPC demo server |
| Operating system | Windows 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’sbru.getEnvVar()equivalents - Postman collection-level pre-request scripts map to Bruno’s collection
script:pre-requestblock, 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
.bruenvironment 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
seqvalue, 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/cliwithout 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.jsonfile in the current working directory;cdinto 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
.protofile 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 brunomanually, 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:
| Command | What it does |
|---|---|
bru run | Executes 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-only | Skips requests that have no test or assertion blocks attached |
bru run --bail | Stops execution on the first failing request instead of running the full suite |
bru import openapi | Generates a starting collection from an OpenAPI spec file or URL |
bru --version | Prints 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
| Aspect | Bruno 4.2.1 | Postman |
|---|---|---|
| Collection storage | Plain text files on disk, git-native | Cloud account by default, local export optional |
| License | MIT, fully open source | Proprietary, free tier with limits |
| gRPC scripting | Full pre-request and post-response scripting since 4.2.0 | Supported, tied to workspace sync model |
| Offline use | Fully offline-first by design | Works offline but nudges toward cloud sync |
| Code review of API changes | Native, since collections are diffable text | Requires export step to review meaningfully |
| CLI for CI | @usebruno/cli, free, matches desktop format | Newman, 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.





