Introduction to Flux
Flux is a local-first, AI-powered desktop API client built on Tauri 2.0 and React 18. It stores everything on disk, uses under 30 MB RAM, and ships zero Electron. Collections are plain YAML you can commit to Git.
Something unclear, or missing? Ask in Discussions — questions about the docs are the fastest way to get them fixed. Something broken goes in Issues, and the contributing guide covers building Flux and opening a pull request.
Why Flux?
| Feature | Flux | Others |
|---|---|---|
| Memory usage | ~28 MB | 200–400 MB (Electron) |
| Storage format | Plain YAML, Git-friendly | Proprietary JSON / cloud-only |
| AI integration | Free tier included, or BYOK for unlimited | Subscription or none |
| gRPC support | Proto files + server reflection | Varies |
| Offline first | Fully offline | Requires cloud sync |
| Protocols | HTTP, gRPC, WebSocket, SSE | Usually HTTP only |
Tech stack
- Frontend: React 18, TypeScript, Vite, Tailwind CSS v4, Zustand, Radix UI
- Backend: Rust + Tauri 2.0, reqwest (HTTP engine), rusqlite (history)
- Code editor: Monaco Editor with local workers, no CDN dependency
- AI: Anthropic Claude API, metered free tier or BYOK with Sonnet, Opus and Haiku selectable
Installation
Flux ships as a single native binary, no runtime, no Electron, no Node.js required on the host machine.
Download
Go to the latest release on GitHub and download the installer for your platform.
| Platform | File | Notes |
|---|---|---|
| Windows | Flux_x64-setup.exe |
x64 NSIS installer |
| macOS (Apple Silicon) | Flux_aarch64.dmg |
M1/M2/M3 |
| macOS (Intel) | Flux_x64.dmg |
Intel Macs |
| Linux | Flux_amd64.deb |
Debian/Ubuntu |
Account
A free account is required to sync collections. Sign up with your email or with GitHub OAuth directly inside the app on first launch. No credit card needed.
System requirements
- Windows: Windows 10 / 11 (x64)
- macOS: 11.0+ (Big Sur or later)
- Linux: Ubuntu 20.04+ or equivalent (GTK 3 required)
- RAM: 30 MB typical, 60 MB peak
- Disk: ~9 MB installer
Quick Start
Send your first request and save it to a collection in under two minutes.
An account is optional
Use Flux without an account on the sign-in screen goes straight in, and the choice is remembered. Everything local works without one:
- Requests, collections, environments and variables
- Pre/post-request scripts, assertions and the Collection Runner
- The mock server, load tests, gRPC, WebSocket and SSE
- The CLI runner, which never needed an account
An account adds cloud sync and the free AI tier, which is metered per account and is why it needs one. The AI also works without an account if you bring your own Claude API key, in Settings → AI & Claude. You can sign in at any time from the avatar menu or from Settings, and nothing on this machine is lost when you do.
https://jsonplaceholder.typicode.com/todos/1, select
GET, then press Enter or click Send. The Response Panel on
the right fills with the JSON response.
What's New
What changed in each release, and what you need to know before updating. The full history lives in CHANGELOG.md.
0.2.0
anything.at.all == null passed against any response, and the CLI exited 0. Those
assertions now fail with unknown path '…'. If a pipeline turns red after updating, it was
green without checking anything. The error message names the path and the roots that are valid.
One assertion language everywhere
The Collection Runner, the Tests screen and the CLI each had their own evaluator, so the same assertion could pass in one and fail in another. They now share a single engine. Both spellings of a body path work, and so do both header forms:
json.token != null // same as body.token != null
headers["Content-Type"] // same as headers.content-type
duration < 500 // now works in all three
Failures also say what they got: expected status == 404, got 200. See
Tests for the full syntax.
AI without an API key
Every signed-in account gets 100 AI actions a month, up to 20 a day, with nothing to configure. Free-tier prompts are relayed through a small Flux proxy, which is how the quota is applied at all; with your own key nothing is relayed and calls go straight from your machine to Anthropic. Privacy spells out what each mode sends.
Settings → AI & Claude shows what you have spent this month and today, and a toggle switches between the free tier and your own key without having to delete the key first. Hitting a limit says which one and when it resets. The model selector is disabled on the free tier, which runs on a fixed model, instead of accepting a choice that was silently ignored. See AI & Claude.
Pre and post-request scripts run in CI
The CLI embeds a JavaScript engine and the same pm shim the app uses. A collection that
authenticates through a pre-request script now runs in CI instead of being skipped with a warning:
flux run collection.yaml // scripts execute, no longer skipped
pm.test() results count as assertions, so a failing Postman-style test fails the build, and
console.log output appears under each request. See
CLI Runner for what it still does not do.
A Content Security Policy for the webview
The webview shipped without one. Everything now loads from the app itself, the only outbound destination
allowed from the frontend is your own Supabase project, and object-src,
frame-ancestors and base-uri are locked down. unsafe-eval is kept on
purpose: pre and post-request scripts run through new Function(), and without it the whole
scripting feature would break in release builds only.
Fonts are served from the bundle instead of a third-party CDN. Flux no longer contacts
fonts.bunny.net on launch, and typography works offline.
Collections carry the whole request
Saving a request used to keep only its method, URL, headers and body. Auth, query params, pre and post-response scripts, variable extractors, GraphQL queries and form bodies were dropped, and assertions were written out empty. All of it is saved and restored now, and the CLI honours it too, so a collection that works in the app works in CI.
{{VAR}} in one click. It matters because collections are meant to be
committed, see GitHub Sync.
Switching requests no longer carries credentials over
Opening a request from a collection, replaying from History, or picking a result in the command palette kept the previously open request's auth, scripts and extractors. A bearer token set for one API could go out to a different host. All three reset the request first now.
Also in 0.2.0
- Collections support folders nested at any depth, and every request keeps a stable id, so deleting or reordering no longer renumbers everything below it.
- Collection
descriptionis no longer dropped on save. - The CLI runs auth (bearer, basic, API key, OAuth 2.0 client credentials), query params, form and GraphQL bodies. It reports what it cannot do instead of running a request that quietly differs.
0.1.7
QUERY method RFC 10008
Safe, idempotent and cacheable like GET, but the query travels in the request body instead of
the URL. The first new general-purpose HTTP method since PATCH. Pick it from the method
selector, see URL Bar & Method.
gRPC streaming
Server-streaming, client-streaming and bidirectional calls, with a live message log, per-message send, end-of-stream and cancel. Previously a streaming method could be selected but not invoked. See gRPC.
Import fixes worth knowing about
- A cURL command with a body containing double quotes, which is most JSON, lost the body entirely and
came in as
GET. Re-import anything you brought in withCopy as cURL. - Flux could not re-import its own exported cURL snippets, because the URL was only found when it came first.
- Variable extractor rules like
$.items[*].idreturned whole JSON objects instead of the field. - An assertion on a field that was absent failed
== null, while an explicitly-null one passed.
URL Bar & Method
The URL bar is the primary entry point for every request. It supports environment variable interpolation, a method picker, and a one-click Send button.
Method selector
Click the method badge to the left of the URL field. Supported methods:
The QUERY method RFC 10008
QUERY is safe, idempotent and cacheable like GET, but the query travels in the
request body instead of the URL. Use it for read-only calls whose parameters are too large or too
structured to fit in a query string: a search with nested filters, a batch lookup by id, a GeoJSON
polygon.
QUERY https://api.example.com/search
Content-Type: application/json
{
"filters": { "status": "active", "tags": ["a", "b"] },
"sort": "created_at"
}
The body works exactly as it does for POST, see
Body. Because the method is safe, a server may cache
the response and a client may retry it, which is the difference from sending the same payload as
POST.
405 Method Not Allowed. Proxies and
gateways in front of your API may reject it before it ever gets there.Environment interpolation
Use {{variableName}} syntax anywhere in the URL. Active environment
variables and global variables are resolved at send time.
https://{{baseUrl}}/api/users/{{userId}}
Keyboard shortcuts
| Action | Shortcut |
|---|---|
| Send request | Enter (while URL focused) |
| Command palette | Ctrl + K |
| New request tab | Ctrl + T |
| Close tab | Ctrl + W |
| Format body | Shift + Alt + F |
| Toggle sidebar | Ctrl + B |
| Go to History | Ctrl + H |
| Go to Collections | Ctrl + L |
| Go to Environments | Ctrl + E |
Params
Query parameters are managed in the Params tab. They are appended to the URL automatically.
Adding parameters
Click + Add param and fill in a key and value. Toggle the checkbox to include or exclude a parameter without deleting it. The URL bar updates live as you type.
Bulk edit
Switch to Bulk Edit mode to paste a raw query string directly:
page=1&limit=20&sort=createdAt&order=desc
{{variable}} interpolation just
like the URL field.Headers
Add, remove, and toggle HTTP request headers. Smart autocomplete surfaces common header names and values as you type.
Common headers
| Header | Example value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer {{accessToken}} |
| Accept | application/json |
| X-Request-ID | {{$uuid}} |
Header inheritance
Headers defined at the collection or folder level are automatically merged into every child request. A request-level header with the same name takes precedence.
Auth
The Auth tab provides structured authentication configuration so you never have to manually compose Authorization headers.
Auth types
| Type | Description |
|---|---|
| None | No authentication header added. |
| Bearer Token | Adds Authorization: Bearer <token>. Supports
{{variables}}.
|
| Basic Auth | Encodes username and password as Base64 in the Authorization header. |
| API Key | Injects a key-value pair into headers or query params. |
| OAuth 2.0 | Authorization code (opens a browser and captures the callback) and client credentials. |
| AWS Sig v4 | Signs the request with AWS credentials for SigV4-protected services. |
accessToken) and
reference it as {{accessToken}} in the Bearer field. Switching
environments automatically swaps the token.Saving auth to a collection
Auth is stored with the request, so reopening it restores the whole configuration. Credential values
should be {{VAR}} references rather than literals, because collection
files are meant to be committed and synced.
If you save a request whose auth holds a literal secret, Flux stops and offers to fix it:
This credential would be written in plain text into the collection file.
Bearer token
[ Move to {{GET_USERS_TOKEN}} ]
Save anyway
Choosing Move to writes the value into the active environment, marks it as a secret so it is masked in the UI, and leaves the reference in the request. If there is no active environment it goes to the globals. Save anyway is there when you know what you are doing, for a throwaway local collection.
Body
The Body tab opens when the selected method supports a request payload (POST, PUT, PATCH, etc.). Monaco Editor powers all text-based body types.
Body types
JSON
Full Monaco Editor with syntax highlighting, error detection, and inline linting. Press Shift + Alt + F to auto-format.
"name": "Alice",
"age": 30,
"admin": false
GraphQL
Split view with a Query editor and a Variables editor side by side. Both use Monaco with GraphQL language support.
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
Form URL-encoded
Key-value table editor, identical to the Params tab. Encoded as
application/x-www-form-urlencoded.
Multipart Form
Supports text fields and file uploads. Each part can be individually named and typed.
Binary
Upload any file from disk. The Content-Type header is inferred from the file extension.
Pre-request Script
JavaScript that runs before each request is sent. Use it to compute dynamic values, set variables, or conditionally modify the request.
Available globals
| Object | Description |
|---|---|
pm.environment.get(key) |
Read an environment or global variable |
pm.environment.set(key, value) |
Write one back, available to this request and the ones after it |
pm.variables |
Alias of pm.environment |
pm.request.headers.upsert(key, value) |
Add or replace a header on the outgoing request. .add() does the same. |
console.log |
Output appears in the Console tab |
{{variables}} for anything the script computes.
Example, sign a request
Compute the value in the script, then reference it from the header or body field:
const secret = pm.environment.get("apiSecret");
const nonce = String(Date.now());
pm.environment.set("nonce", nonce);
pm.request.headers.upsert("X-Nonce", nonce);
pm.request.headers.upsert("Authorization", "Bearer " + secret);
--env.
Post-response Script
JavaScript that runs after a response is received. Ideal for extracting values from responses and writing them to environment variables for use in subsequent requests.
Available globals
| Object | Description |
|---|---|
pm.response.status |
Status code |
pm.response.json() |
Parsed JSON body, or null when the body is not JSON |
pm.response.text() |
Raw body as a string |
pm.response.headers.get(name) |
One header, case-insensitive. .toObject() returns them all. |
pm.response.responseTime |
Round trip in milliseconds |
pm.environment.get / set |
Read and write environment variables. pm.variables is an alias. |
pm.test(name, fn) |
A named check. It fails when the function throws. |
pm.expect(value) |
Chai-style assertions: .to.equal, .to.eql, .to.include,
.to.be.a, .to.be.ok, .to.be.null, .to.be.above,
.to.be.below
|
console.log |
Output to the Console tab |
Example, extract JWT after login
const data = pm.response.json();
if (data?.token) {
pm.environment.set("accessToken", data.token);
console.log("Token saved:", data.token.slice(0, 20) + "...");
}
pm.test("login returns a token", () => {
pm.expect(data.token).to.be.a("string");
});
Extract
The Extract tab provides a no-code UI for pulling values from a response body and storing them in environment variables, no scripting required.
How it works
data.user.id or bracket syntax
items[0].id.
Response Panel
The Response Panel occupies the right third of the main screen and shows everything Flux receives back from the server.
Status bar
At the top of the panel, three numbers are always visible after a request completes:
| Metric | Description |
|---|---|
| Status | HTTP status code with color indicator, green (2xx), amber (3xx/4xx), red (5xx) |
| Time | Total round-trip time in ms or seconds |
| Size | Response body size in bytes / KB / MB |
Tabs
Body
The Body tab renders the response payload with automatic format detection and syntax highlighting.
View modes
| Mode | Description |
|---|---|
| Pretty | Auto-formatted JSON, XML, or HTML with Monaco syntax highlighting. Read-only. |
| Raw | Unformatted response bytes as-is. |
| Preview | HTML responses rendered in an isolated webview. |
| Binary | Hex dump for binary content types (images, PDFs, etc.). |
Copy & Save
Click Copy to put the entire body on your clipboard. Click Save to file to write it to disk, useful for binary responses like file downloads.
Headers & Cookies
Inspect every header and cookie the server returned, with one-click copy for any value.
Headers tab
All response headers are shown in a searchable table. Click any row to copy the value. Common headers like
Content-Type, Cache-Control, and X-Request-Id are highlighted.
Cookies tab
Cookies set by the response (via Set-Cookie) appear here with all attributes: name, value,
domain, path, expiry, Secure, and HttpOnly. They are automatically stored in the Cookie Jar for subsequent requests.
Tests
One assertion per line, no scripting. Tests run automatically after each request and show pass / fail inline, and the same assertions run in the Collection Runner and in CI through the CLI.
Writing tests
An assertion is a path, an operator and a value:
status == 200
duration < 500
json.token != null
json.user.name == "ana"
headers["Content-Type"] == "application/json"
body contains "success"
Paths
| Path | Resolves to |
|---|---|
status |
HTTP status code |
duration |
Round trip in milliseconds |
body |
The whole response body |
json.a.b · body.a.b |
A field in the JSON body. The two spellings are interchangeable. |
headers.name · headers["name"] |
A response header, case-insensitive. Both forms are equivalent. |
Operators
==, !=, <, >, <=,
>=, and contains for substring checks. === and !==
are accepted as aliases.
Values are null, true, false, a number, or a quoted string. A
numeric string in the response matches a number in the assertion, so json.id == 5 passes when
the body has "id": "5".
Variables
{{VAR}} is interpolated in an assertion from the active environment and the global
variables, the same as anywhere else:
json.token == "{{EXPECTED_TOKEN}}"
json.count == {{EXPECTED_COUNT}}
body contains "{{TENANT}}"
Interpolation happens after the operator has been read, so a variable whose value contains
== cannot change how the line is parsed. A variable that is not defined is left as written, so
the assertion fails and the message shows {{VAR}} rather than passing silently.
Reports and history show the assertion as you wrote it, with the {{VAR}} in place, so the
value of a secret variable does not end up in a CI log or in the request history.
Absent fields
A field that is not in the response counts as null, so both of these hold when
token is missing:
json.token == null // passes
json.token != null // fails
unknown path '…' rather than
resolving to null. Without that, a typo like dta.token == null would pass against every
response and the test would look green while checking nothing.Where assertions run
The same lines are evaluated by the same engine in three places: inline under the response, in the Collection Runner, and in the CLI. An assertion means the same thing in all three.
pm API. Scripts run in the app but not in the CLI.
AI-generated tests
Click Generate Tests in the Body tab. Flux sends the response schema to Claude, which returns a set of assertions in the syntax above, ready to run.
Test suite integration
Tests saved per-request are also visible and runnable from the Collection Runner and from the Tests screen in the NavRail.
Timeline
A waterfall view of the request lifecycle, broken into phases so you can identify exactly where time is spent.
Phases
| Phase | Description |
|---|---|
| DNS Lookup | Time to resolve the hostname to an IP address |
| TCP Connect | Time to establish the TCP connection |
| TLS Handshake | Time to negotiate TLS (HTTPS only) |
| Time to First Byte | Time from sending the request to receiving the first byte of the response |
| Download | Time to receive the full response body |
| Total | Sum of all phases |
Console
All console.log calls from Pre-request and Post-response scripts appear
here, along with any script errors.
Log levels
| Method | Color |
|---|---|
console.log() |
White, general output |
console.info() |
Blue, informational |
console.warn() |
Amber, warnings |
console.error() |
Red, errors |
Script errors (syntax, runtime, uncaught exceptions) are always shown in red with a stack trace.
Collections
Collections are folders of requests stored as plain YAML on disk. Every collection maps directly to a directory tree, commit them to Git and collaborate without any Flux-specific tooling.
Collection structure
A collection is one YAML file. Folders live inside that file, not on disk, so moving a request between folders changes one file and shows up as one diff:
~/api-collections/ # a collections folder you opened
users.yaml # one collection
billing.yaml # another one
internal/ # a subfolder groups collections in the sidebar
admin.yaml
Collections folders
Flux can keep several collections folders open at once, so a collection can live inside the repository whose API it tests and be branched, reviewed and cloned with the code:
~/repos/payments-api/api/ # opened as a folder
payments.yaml
~/repos/billing/api/ # and this one too, at the same time
billing.yaml
The folder icon in the Collections header adds one; the list above the search box closes one. Closing a folder only removes it from the sidebar and leaves every file where it is. Each collection remembers which folder it came from, so saving always writes back to its own.
When two folders hold a file with the same name, the second collection is shown with its folder as a
prefix (billing/api). With a single folder nothing changes.
Organising from the sidebar
Right-click a collection, a folder or a request:
| Collection | Folder | Request | |
|---|---|---|---|
| Rename | ✓ | ✓ | ✓ |
| New request / folder inside | ✓ | ✓ | |
| Duplicate | ✓ | ||
| Move to… | ✓ | ||
| Inherited settings | ✓ | ✓ | |
| Delete | ✓ | ✓ | ✓ |
Renaming happens in place. Move to… asks which collection first and then which folder of it, so a request can be moved to another collection, even one in a different collections folder. Deleting asks to confirm and says what goes with it.
Ctrl + Shift + N creates a collection, and the + button in the Collections header does the same.
Inherited settings
Auth, headers and scripts can be set once on a collection or a folder instead of being repeated on every request. Click the shield next to any collection or folder; it lights up when that level defines something. A folder shows what it would inherit before you change anything.
- The nearest level wins. Request over folder, closest folder over outer folder, folder over collection.
- Auth is all or nothing. The nearest definition is used whole, never merged field by field with an outer one, because a half-built credential is worse than none.
- Headers merge, matched case-insensitively, with the closest level overriding.
- Scripts concatenate from the outside in rather than overriding, so a collection-level script can fetch a token and a folder-level one can use it.
- A folder set to
Nonesends no auth even when the collection defines one. Headers and scripts from outer levels still apply.
The same cascade runs when you press Send, in the Collection Runner, on the Tests screen and in the CLI.
Request YAML schema
A collection saves everything the request builder holds, so reopening a request gives back what you
built. Only name is required; anything unset is left out of the file.
name: Create User
id: col-1723800000
kind: http
method: POST
path: {{baseUrl}}/api/users
headers:
Content-Type: application/json
params:
notify: "true"
bodyType: json
body: '{"name":"Alice","email":"alice@example.com"}'
auth:
type: bearer
token: "{{accessToken}}"
scripts:
preRequest: pm.environment.set("nonce", String(Date.now()));
postResponse: pm.environment.set("id", pm.response.json().id);
extractors:
- path: $.data.token
variable: accessToken
tests:
- assert: status == 201
- assert: json.id != null
Fields
| Field | Meaning |
|---|---|
id |
Stable identifier, written on save. Deleting or reordering requests does not change it. |
kind |
http or grpc. Defaults to http when absent. |
path |
Full URL, or a path appended to the collection's baseUrl |
params |
Query string parameters, as a map |
bodyType |
json, form, multipart, binary,
raw or graphql
|
body |
The raw body as a string. For form use the form map instead, and for
graphql use the graphql block.
|
graphql |
query and variables |
auth |
type plus its fields. See Auth.
|
scripts |
preRequest and postResponse. Not run by the CLI. |
extractors |
JSONPath rules that write response values into variables. See Extract. |
tests |
One assert per entry. See Tests. |
{{VAR}} references, never literal tokens. Flux warns when you save a
literal one and offers to move it into the environment for you.
Nested folders
Folders hold requests and further folders, at any depth:
folders:
- name: Admin
requests:
- name: List users
method: GET
path: /admin/users
folders:
- name: Billing
requests: []
Mixed HTTP and gRPC collections
Collections support both HTTP and gRPC requests in the same folder. gRPC requests include an extra
kind: grpc field and a grpc: block. Clicking a gRPC request in the sidebar
automatically navigates to the gRPC screen.
Collection Runner
Select a collection in the sidebar and click Run Collection. The runner executes all requests in sequence, shows pass/fail for each test, and produces a summary report. Currently supports HTTP requests, gRPC runner is on the roadmap.
Import
Flux can import collections from Postman v2.1 and OpenAPI 3.0. Go to File → Import or drag a file onto the sidebar.
What comes across
| From Postman | From OpenAPI |
|---|---|
| Auth: bearer, basic, API key, OAuth 2.0, AWS SigV4. Collection-level auth is inherited by requests that do not define their own. | Auth from securitySchemes: bearer, basic, API key and OAuth 2.0. Credentials come
in empty, since a spec does not carry them. |
Query params from url.query, skipping disabled ones. |
Query and header parameters, with their example as the value. |
Pre-request and test scripts from the event array. |
Path parameters become {{variables}}, so an environment can fill
them. |
| Bodies in raw, JSON, urlencoded, form-data and GraphQL, each with its body type set. | Bodies for JSON, urlencoded and multipart. When the spec has no example, a sample is
built from the schema. |
| Folders at any depth. | Tags become folders; the first servers entry becomes the collection base URL. |
Postman tests arrive as post-response scripts and keep working, in the app and in CI.
Environments
Environments hold key-value variable sets that are injected into requests at send time. Switch between environments instantly without editing any request.
Variable scopes
| Scope | Description | Precedence |
|---|---|---|
| Global | Available in all environments and all collections | Lowest |
| Environment | Active only when the environment is selected in the top bar | Middle |
| Script (runtime) | Set via pm.environment.set() during pre/post scripts, not persisted |
Above both |
| Dynamic built-in | {{$guid}}, {{$timestamp}},
{{$isoTimestamp}}, {{$randomInt}}
|
Highest — a variable of the same name never shadows one |
A global is a default and the environment specialises it: a global
BASE_URL pointing at production is overridden the moment a Local environment
defines its own. A script writes into the active environment, so a token refreshed by a pre-request script
overrides a stale global of the same name.
Substitution is a single pass over the text. A value that itself contains
{{VAR}} is not expanded a second time, and a variable that nothing defines
is left as written, braces included, so the mistake is visible instead of turning into an empty string.
Creating an environment
baseUrl, accessToken, and any other variables
specific to that environment.Secret variables
Mark a variable as Secret (lock icon) to mask its value in the UI. Secret variables are stored in the OS keychain and excluded from exports and GitHub sync.
History
Flux persists every request you send to a local SQLite database. The History screen lets you browse, search, replay, and export your request history.
Searching history
Use the search bar at the top to filter by URL, method, or status code. The search is instant and works fully offline.
Replaying a request
Click any history entry to load its full request (URL, method, headers, body) back into the Request Builder. Click Replay to send it again immediately.
Exporting history
Click Export to download your full request history as a JSON file. Useful for auditing, sharing, or importing into analytics tools.
Retention
Settings → Data & Privacy → History retention keeps the last 7, 30 or 90 days, or everything. Older entries are deleted when you change the setting and again each time Flux starts, locally and in your synced copy.
Clearing history
Click Clear History in the Settings → Data & Privacy section or from the History screen's overflow menu. This deletes the local database and the copy in your Supabase project, so signing in on another device will not bring it back. The action is irreversible.
AI Features
Flux integrates Claude (Anthropic) to accelerate testing, debugging, and body authoring. Free to try during the beta, or bring your own key for unlimited use.
Setup
Nothing to configure. During the beta, every signed-in account gets 100 AI actions a month (up to 20 a day) at no cost. On this free tier your prompts are relayed through Flux servers, which is how the quota is applied, and the model is chosen for you.
For unlimited use, go to Settings → AI & Claude, paste your API key (starts with
sk-ant-), and select a model. Your key is stored only on your device, and AI calls then go
straight from your machine to Anthropic, never through Flux servers. Usage is billed by Anthropic on your
own account.
Available models
Model selection applies when you use your own API key. On the free tier the model is chosen for you and may change over time.
| Model | Best for |
|---|---|
claude-sonnet-4-6 Recommended |
Best balance of speed and quality for all AI features |
claude-opus-4-7 Most capable |
Complex test generation and deeply structured responses |
claude-haiku-4-5 Fastest |
Quick autocomplete and simple debug suggestions |
AI features
Test generation
After any successful response, click Generate Tests. Claude analyzes the response schema and generates a complete test suite covering status codes, field types, required keys, and response time. Generated tests appear directly in the Tests tab, ready to run and save.
AI debug assist
When a request fails (4xx/5xx or connection error), the Debug with AI button appears below the status badge. Clicking it sends the request configuration and error to Claude, which returns a plain-English diagnosis and suggested fixes.
Smart autocomplete
With Smart Autocomplete enabled, Flux suggests completions for header names, path segments, and JSON body fields as you type. Suggestions are ranked by frequency and contextual relevance.
Body editing
Right-click inside any Monaco body editor to access Edit with AI. Type a natural language instruction (e.g., "add a nested address object" or "convert this array to paginated format") and Claude rewrites the body in place.
GitHub Sync
Connect a GitHub repository to push and pull collections automatically. Teams use this to share a single source of truth for API requests without any proprietary cloud backend.
Connecting a repository
Auto-sync
Enable Auto-sync on launch to pull the latest version from GitHub every time Flux starts. Changes you make locally are queued and pushed when you click Sync.
gRPC
Flux has a dedicated gRPC screen with support for proto files, server reflection, unary invocations, metadata, and a persistent proto library.
Getting started
Click the gRPC icon in the NavRail (grid of squares). The screen is split into a left panel (service browser) and a right panel (invocation and response).
Loading a proto
Import .proto file
Click Import Proto and select a .proto file from disk. Flux compiles it into
a FileDescriptorSet using the embedded protoc and lists all services and methods in the left
panel.
Server reflection
Enter the server address and click Reflect. If the server exposes the gRPC Reflection API, Flux fetches all service descriptors automatically, no proto file needed.
Proto Library
Click the Bookmark icon next to a loaded proto to save it to the Proto Library. Saved
protos persist on disk across sessions in {app_data}/protos/ and appear in the
Library section at the bottom of the left panel. Click any saved proto to reload it
instantly.
Invoking a method
Stream types
| Type | Status |
|---|---|
| Unary | Available |
Server streaming ⇉ |
Available |
Client streaming ⇇ |
Available |
Bidirectional streaming ⇄ |
Available |
The glyph next to each method in the service tree tells you which kind it is. Unary methods show
→.
Streaming calls
Selecting a streaming method turns Invoke into Start stream. Messages arrive in a log below the request, newest last, each one numbered and timestamped and collapsible.
| Control | When it appears |
|---|---|
| Start stream | Opens the call. For client-streaming and bidirectional methods the payload is sent as the first message; leave it empty to send none. |
| Send | Client-streaming and bidirectional only. Sends the current payload as another message, so you can edit it between sends. |
| End | Client-streaming and bidirectional only. Signals end-of-stream and waits for the server to finish. |
| Stop | Aborts the call in either direction |
The status bar shows whether the stream is open, how many messages have arrived, and how it ended:
completed when the server closed it, cancelled when you did.
Saving to a collection
Click Save to Collection in the top-right of the gRPC screen. The request is saved as a
YAML entry with kind: grpc. Clicking it in the Collections sidebar navigates back to the gRPC
screen with the request loaded.
WebSocket
The WebSocket screen lets you open persistent bidirectional connections and send and receive messages in real time.
Connecting
ws:// or wss:// URL. Environment variables are
interpolated: wss://{{wsHost}}/events.Authorization are sent during the HTTP
upgrade handshake.Message log
All sent and received messages appear in a scrollable log. Received messages are shown with a left indicator; sent messages with a right indicator. Each entry shows timestamp, direction, and byte count.
Sending messages
Type a message (plain text or JSON) in the composer at the bottom and press Enter or click Send. Click Disconnect to close the connection.
Server-Sent Events
The SSE screen connects to event streams and displays each event in real time, useful for monitoring push notifications, live data feeds, and AI streaming endpoints.
Connecting
Enter an http:// or https:// URL that returns text/event-stream, add
any required headers (e.g., Authorization), and click Connect.
Event log
Each SSE event appears as a row in the log showing its event type, id, and
data. The data field is auto-formatted if it contains JSON.
Filtering
Use the event type filter to show only specific event types (e.g., only update events,
ignoring ping events).
Export
Click Export to download all received events as a newline-delimited JSON file for offline analysis.
Load Test
Fire a configurable number of concurrent requests against any endpoint and see latency distribution, throughput, and error rates in real time.
Configuration
| Parameter | Description | Default |
|---|---|---|
| Total requests | Total number of requests to send | 100 |
| Concurrency | Number of requests in flight simultaneously | 10 |
| Timeout | Per-request timeout in milliseconds | 5000 |
| Method | HTTP method to use | From active request |
| URL | Target URL (environment variables resolved) | From active request |
Results
After the run completes, Flux shows:
- Throughput, requests per second
- Success rate, percentage of 2xx responses
- P50 / P95 / P99 latency, percentile breakdown
- Latency histogram, bucketed bar chart (<50ms, 50–100ms, 100–250ms, 250–500ms, 500ms–1s, >1s)
- Error count, total failed requests with status codes
Mock Server
Run a local HTTP server that returns predefined responses, ideal for frontend development when the real API is unavailable or not yet built.
Creating a mock route
GET /api/users), and status
code.{{$uuid}} and
{{$timestamp}}.
http://localhost:<port> (port configurable, default 3001).
Response configuration
| Option | Description |
|---|---|
| Status code | Any valid HTTP status (200, 201, 400, 404, 500, …) |
| Response headers | Custom headers returned with the mock response |
| Delay | Artificial latency in ms to simulate slow APIs |
| Body | Static JSON, XML, plain text, or dynamic templates |
Request log
Every request the mock server receives appears in the log panel with timestamp, method, path, and matched
route. Unmatched requests return 404 and are logged in red.
Compare
Send the same request against multiple environments simultaneously and compare the responses side by side.
How it works
{{baseUrl}} to reference the base URL of each environment.
Use cases
- Regression check: Verify that staging and production return identical responses after a deploy
- Migration validation: Compare old and new API versions endpoint by endpoint
- Multi-region testing: Hit
us-east,eu-west, andap-southin one shot
CLI
The Flux CLI lets you run collections, single requests, and tests from the terminal, ideal for CI/CD pipelines.
Installation
The CLI is bundled with the desktop app. After installing Flux, add it to your PATH from Settings → CLI Tools → Add to PATH.
Running a collection
Point flux run at a YAML file or a directory of them. Only requests that have
tests are executed.
# One collection file
flux run ./my-api/users.yaml
# Every collection in a directory
flux run ./my-api
# Supply the variables the collection references
flux run ./my-api --env BASE_URL=https://staging.example.com --env API_TOKEN=$TOKEN
# JSON report for CI
flux run ./my-api --reporter json --output results.json
# Stop at the first failure
flux run ./my-api --bail
Options
| Flag | Meaning |
|---|---|
--env KEY=VALUE |
Sets one variable, resolving {{KEY}} anywhere in the collection.
Repeat it for each variable. |
--env-file PATH |
Reads variables from a .env style file: KEY=VALUE per line,
# for comments, optional quotes, a leading export ignored. Repeatable.
|
--folder NAME |
Runs only the requests inside that folder, by name or path (Admin/Users) |
--reporter console|json|junit |
Output format. Defaults to console. |
--output FILE |
Writes the report to a file instead of stdout |
--bail |
Stops the run at the first failing request |
{{VAR}} references, so pass
the values at run time from your CI secret store:
--env API_TOKEN=$API_TOKEN. Nothing sensitive needs to live in the repository.
Chaining requests
What a request captures is available to the ones after it, both through a variable extractor and through
pm.environment.set() in a post-response script:
requests:
- name: Login
method: POST
path: /login
extractors:
- path: $.data.token
variable: TOKEN
- name: Me
method: GET
path: /me
headers:
Authorization: "Bearer {{TOKEN}}"
Requests carrying assertions always run. One without any still runs when it has extractors or scripts, because that is what the login step of a chain looks like and skipping it would leave the rest of the batch without a token. A request with none of the three is skipped.
The same chain behaves identically when you press Send, in the Collection Runner and on the Tests screen.
Where variables come from
The CLI has no named environments and no global variables — those live in the app, on your machine. In CI every value comes from the command line:
flux run ./my-api --env-file ci.env --env API_TOKEN=$API_TOKEN
Later sources win, so --env on the command line overrides a file, and a second
--env-file overrides the first. That ordering is what lets a pipeline keep the shared values in
a committed file and override just one of them for a particular job.
A variable that no source defines is left in the request exactly as written, braces and all, rather than
becoming an empty string. A request that goes out to {{BASE_URL}}/users
fails loudly instead of quietly hitting the wrong host.
Dynamic values
The same built-ins as the app, resolved fresh at each occurrence, so two
{{$guid}} in one request are two different ids:
| Variable | Value |
|---|---|
{{$guid}} |
A v4 UUID. Useful for an idempotency key or a request id. |
{{$timestamp}} |
Milliseconds since the epoch |
{{$isoTimestamp}} |
The current time as ISO‑8601, e.g. 2026-09-09T12:34:56.789Z |
{{$randomInt}} |
An integer from 0 to 999 |
A built-in wins over a variable of the same name, and substitution runs in a single pass: a value that
itself contains {{VAR}} is not expanded again.
Exit codes
| Code | Meaning |
|---|---|
0 |
Every assertion passed and every request completed |
1 |
An assertion failed, or a request could not be sent |
What the CLI supports
A collection saved in the app runs the same way here: auth, query params, form and GraphQL bodies, environment interpolation, folders at any depth, and the same assertion language evaluated by the same engine.
| Auth type | CLI |
|---|---|
| Bearer, Basic, API key (header or query) | Supported |
| OAuth 2.0, client credentials | Supported. The token is fetched before the request. |
| OAuth 2.0, authorization code | Not possible unattended: the grant needs a browser. Use client credentials in CI. |
| AWS SigV4 | Not supported yet. The request fails with a message rather than going out unsigned. |
Scripts
The CLI embeds a JavaScript engine and the same pm shim the app uses, so pre and
post-response scripts run in CI exactly as they do on your machine. A pre-request script that fetches a
token and sets it with pm.environment.set() works before the request goes out, and headers
added with pm.request.headers.upsert() are sent.
Each pm.test() counts as an assertion, so a failing test fails the run and exits
1, and console.log() output appears under its request.
My API 4 requests with tests
running pre/post scripts on 2 request(s)
Create user 214ms
✓ status == 201
✓ body has an id
✗ response time is under 200ms
Expected 214 < 200
› token set to abc123
What it does not do
The CLI announces these at the start of a run instead of quietly running something different:
- gRPC requests are skipped. Only HTTP runs here.
My API 4 requests with tests
skipped 1 gRPC request(s), not supported by the CLI runner
All Settings
Open Settings from the gear icon in the NavRail (or Ctrl + ,). Settings are organized into sections accessible from the sidebar.
General
| Setting | Description | Default |
|---|---|---|
| Default Timeout | Time in ms before a request is cancelled | 30000 |
| Follow Redirects | Automatically follow HTTP 3xx redirects | On |
| SSL Verification | Verify SSL certificates (disable only for local dev) | On |
AI & Claude
| Setting | Description |
|---|---|
| Claude API Key | Your Anthropic API key, stored locally, never sent to Flux servers |
| Model | claude-sonnet-4-6 (recommended), claude-opus-4-7, claude-haiku-4-5 |
| Auto-generate tests | Show "Generate Tests" button after each response |
| AI debug assist | Show "Debug with AI" button on failed requests |
| Smart autocomplete | Autocomplete headers, paths, and body fields using AI |
Appearance
Choose between Dark (default), Light, and System themes.
The accent color is always Flux Purple (#A855F7).
Keyboard Shortcuts
| Action | Shortcut |
|---|---|
| Send request | Ctrl + Enter |
| Command Palette | Ctrl + K |
| New request tab | Ctrl + T |
| Close tab | Ctrl + W |
| Format body | Shift + Alt + F |
| Toggle sidebar | Ctrl + B |
| Go to History | Ctrl + H |
| Go to Environments | Ctrl + E |
| Go to Collections | Ctrl + L |
| Go to Settings | Ctrl + , |
Proxy & Network
Route Flux traffic through an HTTP or SOCKS5 proxy. Useful for corporate environments, VPNs, or traffic inspection tools like Charles or mitmproxy.
Proxy configuration
| Field | Description |
|---|---|
| Proxy URL | Full proxy URL, e.g. http://proxy.corp.com:8080 or socks5://127.0.0.1:1080
|
| No Proxy | Comma-separated list of hostnames to bypass the proxy (e.g. localhost,127.0.0.1) |
| Proxy Auth | Username and password for authenticated proxies |
http://127.0.0.1:8080 and disable SSL
verification to inspect all Flux requests through mitmproxy running locally.Cookie Jar
Flux maintains a persistent cookie jar. Cookies set by responses are automatically sent with subsequent requests to the same domain.
Viewing cookies
Go to Settings → Cookie Jar to see all stored cookies grouped by domain. Each entry shows name, value, path, expiry, Secure, and HttpOnly flags.
Managing cookies
- Click the delete icon on any row to remove a single cookie.
- Click Clear All Cookies to wipe the entire jar.
- Cookies with an expired
Expiresdate are automatically excluded from requests.
Per-request control
To disable cookie sending for a specific request, add the Cookie header manually with an empty
value, or use the Pre-request Script to delete specific cookies before sending.
Data & Privacy
Flux is local first: everything works offline and lives on your machine. Signing in adds optional sync to your own Supabase project. This page explains what is stored, where, and how to delete it.
What Flux stores
| Data | Location | Leaves your machine? |
|---|---|---|
| Collections | Selected directory on disk (YAML files) | Synced to your Supabase project when signed in, and to GitHub if you enable GitHub Sync |
| History | {app_data}/flux.db (SQLite) |
Method, URL, status, duration and environment are synced when signed in. Request and response bodies are never stored or synced. |
| Environments | Browser local storage | Synced when signed in, variable values included. Keep secrets out of committed collections. |
| Settings | Browser local storage (flux-settings) |
Synced when signed in, except your API key and client TLS certificates |
| Cookies | {app_data}/flux.db (SQLite) |
Never |
| Claude API key | Browser local storage (flux_claude_key), unencrypted |
Never synced. Sent only to Anthropic, from your machine. |
| Client TLS certificates | Browser local storage, unencrypted | Never |
| Proto library | {app_data}/protos/ |
Never |
Deleting your data
- Clear history: Settings → Data & Privacy → Clear History
- Clear cookies: Settings → Cookie Jar → Clear All Cookies
- Delete all app data: Uninstall Flux and delete the
{app_data}/fluxdirectory
Telemetry
Flux is an API client: the URLs, tokens and payloads you work with are the most sensitive things on your screen. None of them ever leave your machine. What follows is the complete list of what Flux can send, and it is deliberately short.
Everything here is controlled from Settings → Data & Privacy:
| Toggle | Default | What it sends |
|---|---|---|
| Send usage analytics | Off | Which sections of the app you open, and that a request was sent — its method,
whether the scheme was https, and whether it pointed at localhost. |
| Send crash reports | On | The error message, with URLs, file paths, e-mails and credential-shaped strings stripped out before sending. |
| Share performance metrics | Off | Response time and status code of a request. No URL. |
What is never sent
- Request URLs — not the full URL, not the path, not even the hostname. An internal
host like
api.staging.acme.localidentifies your employer, so it is dropped too. - Headers, request bodies and response bodies.
- API keys, tokens, cookies or any credential.
- Collection names, environment names and variable values.
- Your name, e-mail or the path to your home directory.
How you are counted
When usage analytics is on, Flux sends a random identifier generated on your machine the first time it runs. It identifies a copy of Flux, not a person: it is not derived from your hardware, your account or anything else, and it is not linked to your Flux account even when you are signed in. Clearing the app's local data resets it. Without it there is no way to tell whether a hundred events came from a hundred people or from one, which is the only reason it exists.
fluxapi.dev and github.com in your firewall — Flux will keep
working and simply stop offering updates.
Flux is open source, so none of this has to be taken on trust. The client side is
src/lib/analytics.ts — roughly a hundred lines, with the
redaction rules covered by
tests — and the server side is
supabase/functions/.
Terms of Service
Flux is free and open source under the MIT licence. These terms cover the parts that are not simply the software on your machine: the optional account, cloud sync and the AI free tier.
Last updated: 10 September 2026
The software
Flux is released under the MIT licence. You can use it for anything, including commercially, modify it and redistribute it. That licence governs the software itself, and where it and this page disagree about the software, the licence wins.
As the licence says, Flux is provided as is, without warranty of any kind. You are responsible for the requests you send with it and for having permission to send them.
Accounts
An account is optional: Flux runs without one. If you create one, with an e-mail address or through GitHub, you agree to give accurate details and to keep your credentials to yourself. You are responsible for what happens under your account.
You can delete your account at any time from Settings → Data & Privacy. Deleting it removes what is stored on the server; whatever is already on your machine stays there.
An account may be suspended if it is used to attack the service, to get around the AI limits, or to do something illegal. Where it is reasonable to do so, you will be told why first.
Cloud sync
Sync is opt-in. When it is on, your collections, environments, request history and app settings are stored on the server so that other machines signed into the same account can read them. What is stored, and how to delete it, is set out in Data & Privacy.
Keep your own backups of anything you cannot afford to lose. Collections are plain YAML files on your machine, so a copy or a Git repository is enough. There is no promise that the service will be available at any particular time, and synced data cannot be guaranteed against loss.
Do not put credentials in a collection file. Flux is built so that secrets live in environment variables
and collections reference them as {{VAR}}, and it warns you before saving
one in plain text.
The AI features
Flux can use Claude, from Anthropic, in two ways:
- With your own API key. Requests go from your machine straight to Anthropic. The key never leaves your device and never reaches the Flux servers. Your relationship for that use is with Anthropic, under their terms.
- With the free tier, which needs an account. Prompts are relayed through a Flux proxy, which is what applies the per-account limit, and then on to Anthropic.
The free tier is a beta courtesy with a monthly and a daily limit, both shown in Settings → AI & Claude. Those limits can change, and the free tier can be reduced or withdrawn, without notice. If you depend on the AI features, use your own API key.
Whichever route you use, what you send reaches Anthropic and is subject to their terms and their policies. Do not send anything through the AI features that you are not allowed to share with a third party.
Acceptable use
Do not use Flux or its services to break the law, to attack systems you have no permission to test, or to put someone else's personal data through the AI features without a basis for doing so. Load testing can generate a lot of traffic: only point it at something you are authorised to load test.
Liability
To the extent the law allows, Flux and its author are not liable for loss of data, loss of profit, or any indirect or consequential damage arising from use of the software or the services. Nothing here removes rights that cannot be removed by agreement, including any consumer rights you have where you live.
Changes
These terms can change as Flux changes. Material changes are noted in the changelog, and the date above is updated. Continuing to use the account services after a change means you accept the new terms; if you do not, you can stop using them and keep using Flux locally, or delete your account.
Contact
Questions about these terms, or about an account, go to GitHub issues for anything public, or to the address in Settings → Data & Privacy for anything about your own data.