Axon is a local-first desktop API client for HTTP, GraphQL, gRPC, WebSocket, Socket.IO, SOAP, SSE, MQTT and MCP. These guides cover every part of it — from your first request to running collections in CI and syncing with your team without a cloud account.
Axon is a native desktop app for macOS, Windows and Linux. There is no account to create and nothing to sign in to — download it, open it, and send a request.
Every release is published on GitHub with an installer for each platform: a .dmg for macOS, an .msi or .exe for Windows, and .deb, .rpm or .AppImage packages for Linux.
On first launch Axon creates a local workspace and a cryptographic identity for this device. Both stay on your machine. If you are moving from another API client, press ⌘O (Ctrl+O) to import your collections — see Collections.
Releases are signed. When Axon starts it asks GitHub whether a newer signed release exists and installs it. Turn this off in Settings → Update; you can still check by hand from the same page.
You need Rust (stable, 1.94+), Node.js 22+ and pnpm 10, plus the platform toolchain: Xcode Command Line Tools on macOS, webkit2gtk-4.1, libsoup-3.0 and librsvg2 on Linux, and WebView2 with the MSVC build tools on Windows.
pnpm --dir frontend install
cd frontend
./node_modules/.bin/tauri build
The installers land in target/release/bundle/<platform>/.
Welcome to Axon. It's an API client that runs natively on your machine and keeps everything offline.
If you've used Postman or Insomnia, you already know the shape of this: you write a request, you send it, you read the response. Axon adds scripting, tests, a collection runner, mock servers, monitors, an AI assistant, and live team sync over your LAN — and it speaks a lot more than REST: GraphQL, WebSocket, Socket.IO, gRPC, SOAP, SSE, MQTT and MCP.
One thing to settle up front, because it changes how you use everything else: there is no account and no server. Your collections, environments, history and secrets live on your own disk — a local SQLite database, with secrets in your OS keyring. Nothing you work on is uploaded. You never sign in.
Five regions, and you'll use all of them within your first hour.
Left rail — the narrow strip of icons. Each one swaps the sidebar beside it: Collections, Environments, Protocols, Services, History. Click the icon you're already on to collapse the pane and win back some width.
Center — the tab strip and whatever tab is active. Tabs aren't only requests; the runner, settings, docs and other full-page surfaces all open as tabs too.
Right sidebar — companion panels for the AI assistant and the variable editor. Open them from the top bar.
Top bar — environment switcher, Team sheet, and the AI and Variables toggles.
Footer — Globals, Tools, and the terminal drawer where live protocol traffic streams.
Let's send something real. This takes about thirty seconds.
Press ⌘K (Ctrl+K) to open the command palette — or just click + on the tab strip for a blank tab.
Pick a method from the dropdown and type a URL. Try https://httpbin.org/get if you don't have one handy.
Press ⌘Enter to send. Watch the Send button: while the request is in flight it turns into a red Cancel, so a hung request is always escapable.
The response docks underneath. Drag its top edge to give it more room.
Press ⌘S to save the request into a collection.
That last step is the one worth building a habit around. An unsaved request isn't lost — the History rail keeps every response you've ever sent — but a saved one can be run, shared, exported and monitored.
Anywhere you can type a value, you can type {{a_variable}} instead. Axon highlights the names it recognises as you type. More on that in Variables.
Worth knowing before you trust it with anything important.
Axon keeps one SQLite database in your OS user-data directory, holding every workspace (each row is tagged with its workspace), plus whatever YAML/JSON exports you write elsewhere. Secrets go to the OS keyring.
Your requests only go where you send them. Apart from that, Axon makes exactly two kinds of network call on its own:
Update check — on launch it asks GitHub for a newer signed release and installs it. Turn this off in Settings → Update; you can still check by hand there.
Licence check — only if you have activated a paid licence: it is revalidated with the store about once a day. A free install never contacts the store.
Team sync is the one feature that moves your data between machines, and even then it goes directly between paired devices on your network — never through a third party.
The request tab is where you'll spend most of your time. It has an address bar on top, seven sub-tabs beneath it, and the response docked below.
Look for the small dot on a sub-tab — that means it has content. It's how you tell at a glance that a request carries a body or three headers without clicking through all seven tabs to check.
The URL accepts {{variables}} and highlights the names it recognises as you type. If a name stays unhighlighted, it isn't defined in any scope yet — that visual cue catches typos before you send.
The caret beside Send offers Copy as cURL. Save (⌘S) writes back to the collection; Share copies the axon://request/{id} deep link.
Markdown notes for this request, with a view/edit toggle.
These aren't just for you. Export → Markdown / HTML docs turns them into published API documentation, so a sentence written here becomes a sentence in the docs your team reads later.
Query parameters and path variables as an enable/disable checklist.
The useful part: editing a param rewrites the URL, and editing the URL rewrites the params. They stay in sync, so you can work in whichever one is more convenient. Unchecking a row keeps it in the list but leaves it out of the wire request — which beats deleting a param you'll want back in five minutes.
Inherit auth from parent — the default, and usually the right answer. Uses whatever the parent folder or collection defines.
No Auth
API Key — in header or query.
Basic
Bearer Token
JWT (HS\*) — signs an HMAC JWT from a secret and claims.
Digest
AWS SigV4
OAuth 2.0 — token, refresh token, expiry.
Every field takes {{variables}}. Please use that: put the secret in the vault and reference it here, rather than typing a live token into a request that might get exported or synced.
Your manual headers sit on top. Below them is a read-only auto-generated headers preview showing what Axon adds on your behalf — content type, content length, host, accept-encoding, auth, cookies.
Expand that preview when a signature mismatch has you stuck. It's exactly what goes out on the wire, which makes it the fastest way to spot the header you assumed was there and isn't.
The response panel docks under the request — drag its top edge to resize. Its header carries status, elapsed time and size, and it has these tabs:
Body — pick a rendering: JSON, XML, HTML, YAML, JavaScript, Markdown, Raw, Hex, or Base64. Hex and Base64 exist so a binary payload is still inspectable instead of a wall of nonsense.
Cookies — cookies this response set, with a count badge.
Headers — response headers, with a count badge.
Request — exactly what was sent, after interpolation and auto-headers. When a request misbehaves, start here: it shows what actually left, not what you meant.
Tests — pass/fail rows from the post-response script.
A collection is a tree of folders and requests. It's the unit Axon runs, exports, syncs and shares — so once a request lives in a collection, every other feature in the app can reach it.
If you're just poking at an API, you don't need one. The moment you want to run a suite, hand it to a teammate, or commit it next to your code, you do.
New collection — the + button at the top of the sidebar.
Add a request — hover a collection row and click the + that appears, or use ⋯ → Add request.
Add a folder — ⋯ → Add folder nests a sub-collection.
Reorder — drag rows around the tree.
Every row has a ⋯ menu carrying Run, Copy link, Duplicate, Export, Rename and Delete.
Deleting a collection makes you type its name first, because there is no undo. That prompt is the only safety net, so read it rather than clicking through it.
Click a collection's name and it opens as a tab with five sub-tabs. This page is where you set things once instead of on every request inside it.
Overview — description and notes.
Authorization — auth for every request set to inherit. Configure it here and your fifty requests stop each carrying their own copy of the same bearer token.
Scripts — pre-request and post-response scripts wrapping every request in the collection.
Variables — collection-scope variables, which outrank environment and global scope.
Runs — history of runner executions for this collection.
The filter box at the top of the Collections sidebar does double duty.
Typed text filters the tree, and at the same time those terms are scored against a full-text index — BM25 over request names, URLs, bodies, tags and collection names. When there are hits, the ranked list replaces the tree and your matched terms are highlighted in place. Click any hit to open that request. Clear the box to get the tree back.
So you can search for a header value you half-remember, not just a request name.
File → Import… (⌘O) opens the import modal. It reads:
Postman Collection v2.1
OpenAPI 3.x / Swagger 2
HAR 1.2
Insomnia v4 export
Bruno (.bru)
cURL is handled separately — paste a command into the cURL import dialog and it becomes a request with method, URL, headers and body already filled in. This is the fastest way to move something out of your browser's network tab and into Axon.
One honest caveat about Postman: scripts do not always translate one-to-one. Rather than let them fail silently, run axon convert postman. It rewrites a v2.1 collection so its scripts run under Axon, comments out any call with no equivalent, and writes a per-request diagnostics report telling you exactly what it couldn't carry over. See Runner & CLI.
Export → JSON or YAML — the full collection, round-trip safe. YAML is the git-friendly one; commit it beside your code and your API suite gets reviewed like everything else.
Export → Markdown or HTML docs — readable API documentation generated from your requests and their Docs tabs.
That second option is why filling in a request's Docs tab pays off later — it's the source those published docs are built from.
Share on the request panel copies a deep link shaped like axon://request/{id}. A teammate on the same synced workspace pastes it and lands directly on that request.
A variable lets you write a request once and point it at staging, then production, without editing it. Reference one anywhere — URL, params, headers, body, auth fields — with {{name}}.
Axon highlights names it can resolve as you type, so an unhighlighted {{name}} is your early warning that you spelled it wrong.
Four scopes hold variables, and the same name can live in several. Highest tier wins:
Runtime — set by a script (pm.variables.set) or by the runner during a run.
Collection — the collection's Variables tab. Nested sub-collections all feed this tier, and a closer collection masks a farther ancestor.
Environment — whichever environment is currently active.
Global — always in scope, whatever the environment.
The practical read: a script can always override an environment, and an environment can always override a global. If a value isn't what you expect, something above it is shadowing it — check runtime first and work down.
An environment is a named bag of variables you swap as a set — one for local, one for staging, one for prod.
Open the Environments rail to manage them. + New environment creates one, the radio control on each row activates it, and each row supports inline rename, edit-variables and delete. The Environment button in the top bar is the quick picker for the same thing.
Press ⌘/ to float the variable editor over whatever request you're on. It's the fastest way to check or nudge a value without navigating away and losing your place.
The vault holds secrets encrypted at rest behind a passphrase. It's where tokens and API keys belong.
First use — you set a passphrase, which derives the encryption key.
Locked — enter the passphrase to unlock for the session.
Unlocked — a key/value list with add and delete, plus a Lock button.
Why bother instead of an ordinary variable? Because a vault reference keeps the secret out of the exported YAML. You can commit a collection to a shared repo, and the token stays on your disk.
The passphrase never leaves the frontend except as a one-shot argument. The derived key stays in memory in the backend process and is never written to disk.
Identity, contact, address and internet values come out realistic (en locale) rather than as random noise, so a seeded test account reads like a real one. Numbers, dates and tokens honour the ranges you pass.
One ordering detail worth knowing: dynamic variables resolve after your own. So defining a variable named $timestamp shadows the built-in — occasionally handy, occasionally baffling if you did it by accident.
The rule to remember: pm.variables reads the merged view across all scopes, while the scope-named accessors (pm.environment, pm.globals, pm.collectionVariables) read and write one specific tier.
So read through pm.variables when you just want the value, and write through a named scope when you care where it lands. More in Scripting & tests.
Everything that isn't a plain HTTP call lives in the Protocols rail.
The pattern is the same whichever one you pick: click +, choose a kind, and you get a saved connection — a row you can reopen later, not a throwaway session. Click a saved row to open it as a tab. For the connection-oriented protocols, live traffic streams into the footer terminal drawer while you work.
Two ways in: set the body type to GraphQL on any request, or create a saved GraphQL connection.
Point at an endpoint and Axon runs the standard introspection query, then uses the returned schema for field autocomplete on types and root operations. You get completion for the API you're actually talking to, without configuring anything.
Queries and mutations post a well-formed {query, variables, operationName} body.
Subscriptions run over graphql-transport-ws and stream results into the tab.
Event-oriented rather than frame-oriented. You emit a named event with a payload and subscribe to the events you care about, instead of pushing raw frames.
Normally gRPC means a codegen step before you can call anything. Not here — Axon works in two layers:
Introspection — load a .proto file, or use server reflection, to build a catalog of services, methods, messages and fields. The sidebar lists every callable method.
Invocation — pick service.method, supply JSON input, and Axon encodes it dynamically against the proto, then decodes the response back to JSON.
Unary and streaming calls both work, over plaintext or TLS. You write JSON and read JSON; the protobuf part stays out of your way.
Builds a SOAP 1.1 or 1.2 envelope around your body payload, with the right Content-Type and SOAPAction.
Parsing a WSDL populates a sidebar of <service> / <port> / <binding> / <operation> entries, so you call operations by name instead of hand-writing envelopes.
A <soap:Fault> response is parsed and shown as a fault, rather than dumped as raw XML for you to squint at.
Not there yet: full XSD validation and dynamic schema binding.
Scripts are how a request stops being a single call and starts being part of a workflow — grabbing a token, seeding an id, asserting the response is actually correct.
Every request has a Scripts tab with two editors: a pre-request script that runs before the call goes out, and a post-response script that runs after it comes back. Collections have the same pair on their Scripts tab, wrapping every request inside them.
Scripts run in a sandboxed JavaScript engine (Boa) inside Axon. No Node, no filesystem, and no network access except through pm.sendRequest. The API is a documented subset of Postman's pm.*, so most existing Postman scripts port over directly.
New to this? The Snippets picker beside the editor inserts working examples for every supported call, grouped the way Postman groups them. It's a faster way in than reading this page top to bottom.
Each scope object supports get, set and unset. Note that pm.variables.set writes to runtime scope, which outranks everything else — handy, but it means a value you set in a script will shadow the environment for the rest of the run. See Variables for the full precedence chain.
pm.test(name, fn) records one pass/fail row. Assertions throw when they fail, which fails that test — but the other tests keep running, so one broken assertion doesn't hide the rest of your results.
pm.test("status is 200", () => {
pm.response.to.have.status(200);
});
pm.test("returns the right user", () => {
const body = pm.response.json();
pm.expect(body).to.have.property("name", "axon");
pm.expect(body.roles).to.have.lengthOf(3);
pm.expect(body.email).to.match(/@example\.com$/);
});
pm.test("not a 404", () => {
pm.response.to.not.have.status(404);
});
Results land in the response panel's Tests tab, and in the runner's report when the request runs as part of a collection.
These words are chainable no-ops that exist purely so assertions read like English: to, be, is, has, have, with, that, and, of, at, same, does, but, still. Add not to invert whatever follows.
pm.sendRequest fires an extra HTTP call from inside a script and hands the result to a callback. The classic use is fetching a token before the real request goes out.
Writing an assertion per field gets old fast. Axon can validate a response body against a JSON Schema instead, so contract drift fails the run without you enumerating every property by hand.
Open it with ⌘⇧R, from File → New Runner Tab, or from a collection's ⋯ → Run.
The runner is a full page split in two. The left column is the run sequence: one row per request, with a checkbox, a folder breadcrumb, a method badge and the request name. Drag rows to reorder, uncheck rows to skip them. The right column is the configuration.
Switch the right column to Performance to drive the same collection as a load test, in the app or via the CLI. You pick a load profile, a number of virtual users, and a test duration.
Results stream into a table as the run proceeds, one row per request per iteration: iteration number, method, request name, status, duration, test results and any error. A collection's Runs tab keeps the history of past runs.
The axon command (built from the axon-cli crate) is the headless runner. It shares the request engine and script sandbox with the app, so a run in CI behaves exactly like a run in the window.
axon run <collection.json|yaml> [options]
axon run --collection-id <uuid> [--workspace-db <path>] [options]
Point it either at an exported collection file or at a stored collection by UUID. A stored collection is read from --workspace-db, else --db / AXON_DB, else this app's own database, and runs with its folder scripts, inherited auth and the workspace's active environment, the same as the in-app Runner.
Options:
-e, --env <file> — environment file: a JSON or YAML object of key/value pairs.
-d, --data <file> — data file: CSV or a JSON array of objects. One iteration per row.
-o, --out <file> — report path. The format follows the extension: .xml for JUnit, plus .html, .md, .sarif and .json.
--bail — stop on the first failure.
--iterations <n> — iteration count when no data file is supplied. Default 1.
--retry <n> — per-request retries on failure. Default 0.
--delay <ms> — delay between requests. Default 0.
--parallel <n> — parallel requests within one iteration. Default 1.
--iteration-concurrency <n> — iterations to run at once. Default 1, which preserves dispatch order; raise it for load tests.
--pre-script <file> — runs once before any iteration; can seed variables every request then sees.
--post-script <file> — runs once after the last iteration, with the run summary exposed as pm.response.json().
--db <path> — workspace database. Also settable via the AXON_DB environment variable. Defaults to the user data directory.
Other commands:
axon version — version and build info.
axon convert postman <file> [--out <file>] [--report <file>] — rewrite a Postman v2.1 collection so its scripts run under Axon. Calls with no Axon equivalent are commented out and reported in a per-request diagnostics file.
The Services rail bundles the workspace surfaces that run in the background: mock servers, monitors and webhook receivers. All three sections are always open, each with its own Create button.
A local HTTP server that answers with responses you define — useful for building a client before the real API exists, or for reproducing an error your backend will not produce on demand.
A mock is defined by a spec:
bind — the address and port to listen on. Use port 0 to auto-assign.
label — the name shown in the panel and on the log tab.
routes — matched in declaration order; the first matching (method, path) wins. Path matching is exact by default, and a trailing * makes the rest of the path a wildcard.
auth — optional token endpoints backing the stateful layer below.
resources — optional stateful CRUD collections.
api_key — when set, every request must carry a matching x-api-key header or it is refused with 401 before any route is considered.
delay_ms — artificial latency on every response. Per-route delay_ms still applies; the longer of the two wins rather than stacking.
allow_external — whether anything outside localhost may reach it.
Each route carries a method, path, status, headers, body and an optional delay_ms for simulating a slow endpoint. A route can hold several named examples; which one is served is chosen at request time by a header or query selector, and example bodies can interpolate values out of the incoming request. That is the mechanism for error injection — add a 500 example behind a selector and flip your client into it without restarting anything.
Mock servers opens as a full page (command palette, or Create in the Services rail), with room for the form and, once a server runs, the server itself.
The form is one card, and the JSON spec at the bottom of it is the thing Create actually submits — the fields above are an overlay on that JSON:
Blueprint — which kind of API to start. Picking one rewrites the spec editor:
Blueprint
What it serves
CRUD + auth API
The full demo surface: register / login / me plus /api/posts and /api/todos. The only one that can seed a collection.
Public read-only catalog
One open resource with seeded records, search and filtering, no tokens.
Error injection
GET /api/flaky?case=500|429|422|401 answers with that failure; no case is a 200.
Slow / timing out
Every response waits 1.5s and /api/hang waits 15s — for spinners and client timeouts.
Long paginated list
23 orders at 5 per page, for ?page=, ?limit=, ?sort= and ?status=.
Request echo
POST /api/echo returns your body; GET /api/whoami returns your headers.
Environment — where the server's URL is saved as mockUrl, so requests can point at {{mockUrl}}. Pick an existing environment, ask for a new one, or leave it at No environment and nothing is written.
Network delay — applied to every response. A blueprint's own delay comes selected.
Create a collection for this server — off by default; tick it to save a collection that exercises every endpoint. Only the CRUD + auth blueprint has one; it is disabled for the others, whose paths the seeder doesn't know.
Make mock server private — generates an x-api-key, shown once the server starts and copyable from the card. It is never persisted, so it dies with the session.
Spec — the raw JSON, exactly as axon_mock::MockServerSpec deserializes it. Edit it for anything the blueprints don't cover — extra routes, more resources, a different auth base path — and Reset puts the blueprint back.
One mock server runs at a time. While it does, the form is replaced by the server's card: status, URL with a copy button, the collection and environment the create wrote, the x-api-key for a private one, and Logs / Stop. Stop it and the form comes back.
Logs on a running server opens that server's request log as its own tab in the bottom terminal drawer — one line per request with method, path, status and the matched route or example. It keeps streaming while you work, and the tab closes on its own. It lives there rather than on the page so it stays visible once you switch tabs to go send something at the mock.
The server keeps its last 50 requests. Watching starts from the present rather than replaying that buffer, and the terminal says so when requests roll off it faster than it polls.
Static routes answer with a canned body — enough for one stubbed endpoint, not enough to exercise a client against something that behaves like a real API. A spec can also declare auth and resources, which are backed by an in-memory store that lives as long as the server does.
auth mounts, under base_path (default /auth):
Method
Path
Behaviour
POST
/register
Creates an account. 409 if the username is taken, 422 if a field is missing, 403 when allow_register is false.
POST
/login
Returns access_token, token_type, expires_in. 401 on bad credentials — the same body whether the username or the password was wrong.
GET
/me
The token's user.
POST
/refresh
Issues a new token and revokes the presented one.
POST
/logout
Revokes the token, 204.
GET
/users
All accounts, hashes never included.
Seed accounts with users: [{ "username": …, "password": …, "role": … }], and set token_ttl_secs (0 never expires). Passwords are salted-hashed per server so demo credentials do not sit in cleartext, but this is a test double — never put a real secret in a spec.
Each entry in resources serves a full CRUD surface over its path:
Method
Path
Behaviour
GET
{path}
{ items, total, limit, offset }. Accepts limit, offset, q (substring search over search_fields, or every string field), sort, order=asc|desc, and exact field=value filters.
POST
{path}
201 with a Location header. The id is server-assigned; one you send is ignored.
GET
{path}/{id}
One record, or 404.
PUT
{path}/{id}
Full replace. id, created_at and created_by survive.
PATCH
{path}/{id}
Shallow merge; a null value drops the field.
DELETE
{path}/{id}
204, or 404 if it was already gone.
Per resource: auth is "none", "write" (default — public reads, authenticated writes) or "all"; required_fields drives a 422 with a fields map; seed pre-loads records; owner_scoped hides each record from every account but its creator, reads included.
A static route always wins over the stateful layer for the exact (method, path) it declares, so you can override one endpoint of an otherwise-live resource — pin a 500 on GET /api/posts while create and delete keep working.
Ticking Create a collection for this server saves a collection wired to that server's port: a folder each for auth, a public-read posts resource and an owner-scoped todos one, nineteen requests including the deliberate 401 and 422 cases. baseUrl is a collection variable, and the login request's test script stores the token into token, so the rest of the collection sends authenticated without any copy-paste. Send 2. Login first.
Marking it private also writes the key into the collection as the secret variable apiKey and adds an x-api-key: {{apiKey}} header to every request, so the collection still works end to end.
alert rules — any of: status is not the expected code, latency over a threshold in milliseconds, body missing an expected string, or any test failed.
enabled — pause without deleting.
Each run records status, duration, body, failed test count and any triggered alerts. Recent runs are listed under the monitor so you can see the pattern rather than only the last failure.
Monitors opens as a full page (command palette, or Create in the Services rail), built like the mock server page: the form while nothing is configured, then one card for the monitor itself — schedule, target, environment and alert rules, with Pause / Resume, Refresh runs and Delete. One monitor at a time; delete it and the form comes back.
An inbound webhook capture. Start a receiver bound to a local port, point an external service at its URL, and every request it receives is captured with method, path, headers and body. The list polls while the page is open.
This is how you inspect what a third party actually sends you — Stripe, GitHub, a payment provider — instead of reading their docs and guessing.
Webhook receivers opens as a full page, in the same shape as the other two. The form asks for the bind address, a label and the status code to reply with; a bind other than 127.0.0.1 also needs Expose on the network, since the receiver has no auth. Once it listens the form is replaced by the receiver's card — its URL with a copy button, Clear captures and Stop — and every capture below it, click one for headers and body. One receiver at a time: you point a service at exactly one URL.
Axon keeps an in-memory tantivy index over your requests and collections, rebuilt on startup from the workspace database. It scores over request name, URL, body text, tags and collection name using BM25.
Type in the Collections sidebar filter box to use it: matching hits replace the tree, ranked by score, with the matched terms highlighted in place and the collection name shown as a breadcrumb. Clear the box to get the tree back.
Axon has a chat-style assistant that can read the request you are on and propose concrete edits to it. Open it with the AI button in the top bar — it docks into the right sidebar.
Axon never stores API keys. The default backend drives an AI CLI that is already installed on your machine (the Claude Code CLI) as a subprocess, so it uses whatever you are already authenticated with. Ollama is supported for fully local models, and a direct Anthropic HTTP backend exists for callers that want to supply their own connection.
Available providers and models are detected at startup. Settings → AI shows what was found and lets you set your default; the panel's own model picker overrides it per conversation.
Header — the conversation title, a new chat button and a history button.
Composer — the prompt box, a model picker popover (an Auto option that lets the CLI pick its default model, a Thinking toggle for extended thinking, and a quick model list; all three are remembered), a shortcut into AI settings, and the send button.
Your provider and model choice persists across reloads. Conversation history lives in memory for the lifetime of the tab — it is not written to disk.
The assistant does not answer only in prose. It can propose changes, which appear as one reviewable list under its reply. A single Apply runs the whole list in order, and nothing changes until you press it; Reject drops it.
The complete set of actions it can propose:
create_collection — a collection, or a folder inside one (by id or name).
create_request — a new request with name, method, URL and target collection.
set_url, set_method
set_headers — replacing or merging the current set.
set_body — any body kind except binary: JSON, XML, text, HTML, JavaScript, URL-encoded, form-data (text fields) and GraphQL. set_body_json is the JSON shorthand.
set_auth — No Auth, Inherit, Basic, Bearer or API Key. Axon does the Basic encoding itself.
set_variable — an environment (the active one), collection or global variable, applied right away. Tokens and keys can be marked secret.
set_pre_request_script, set_test_script — e.g. a test script that saves a token from the response with pm.environment.set.
run_command — runs one Axon command: anything in the command palette, plus send and save the current request. It can carry the answers to the dialogs the command opens (a name, a yes/no, a choice); without them the dialog waits for you. Unused answers are dropped right away, so they never fill a dialog you open later.
update_request — changes any saved request by id (name, method, URL, params, headers, body, auth, scripts, docs), open or not.
move_request, move_collection (a collection or folder under another one, or to the top level), rename_collection
delete_request, delete_collection — shown with a red warning; a collection delete must name its target.
set_collection_auth, set_collection_scripts — auth and scripts on a collection or folder, inherited by its requests.
create_environment — with its variables, optionally activated.
read_request, read_collection — look at saved settings. These only read, so they run without Apply.
create_connection, update_connection, delete_connection, read_connection — WebSocket, Socket.IO, MQTT, SSE, gRPC, SOAP, MCP and GraphQL connections. Each kind has a fixed list of settings the assistant may set. MCP is HTTP-only: the assistant can never set a STDIO command line, because that would run a program on your machine.
run_connection — opens the connection's tab and presses its main button (Connect, Send, Invoke, Subscribe). Not for STDIO MCP; the output shows in that tab.
start_mock, stop_mock, read_mock — a local mock server from fixed routes and in-memory CRUD resources. Its URL comes back to the assistant and can be saved as {{mockUrl}} in an environment; read_mock shows the requests it answered.
create_monitor, update_monitor, delete_monitor, read_monitor — run a request or collection on a schedule (every minute or slower, or cron) with alerts; pause, change, remove, and read the last runs.
The assistant sees your whole workspace tree (collections, folders, and each request's name, method and URL), your protocol connections, running mock servers and monitors, the active environment, and the conversation so far.
The assistant can work through a task over several steps. When a step reads something, sends a request, or fails, the results (the read data, the response status, body and test results, or the error) go back to it, and it proposes the next step: fix the test, retry, read before editing. Steps that only read carry on by themselves; every step that changes something still waits for Apply. The results each step sent back are listed, collapsed, in the chat.
A task stops when the assistant says it is done, after 8 steps (say "continue" to keep going), or when you press Stop.
Steps in one reply build on each other: "make a Users collection with a login request using basic auth, then send it" creates the collection, puts the request in it, sets its auth on the new request, and sends that one.
The assistant also knows the random values ({{$randomUUID}}, {{$randomEmail}}, {{$randomInt(1,100)}} and the rest) and uses them for sample data.
That list is the whole surface, and it is deliberately closed. There is no shell tool, no file tool, no arbitrary network tool; run_command only reaches Axon's own commands. Even a model that is confused, or a response crafted by a hostile API you are testing, cannot make the assistant do anything outside those in-app actions.
The prompt, and whatever request or response context you attach, go to the provider you selected. With Ollama or a local CLI that stays on your machine; with a hosted model it does not. Nothing is sent implicitly — the assistant only sees what a message includes.
Axon syncs a workspace live between teammates' machines over your LAN — or over a VPN address for people who are not on it. There is no server, no account, and no third party in the middle. Open it with the Team button (the radio icon) in the top bar; the sheet slides in from the right edge.
Each device keeps its SQLite database as the source of truth for what you see. A team workspace is additionally mirrored into a CRDT document (automerge), and it is that document peers exchange over a TLS WebSocket. Merges are conflict-free — two people editing different fields of the same request both land, with no manual resolution prompt.
The topology is a full mesh: every active member connects to every other. That is fine at team scale and means there is no leader to elect and no host whose absence stops everyone else.
Click Host. Nearby devices running Axon can now discover you.
When a teammate joins, you both see a Confirm pairing code prompt with a short code derived from the handshake transcript. Read it aloud to each other. If it matches on both screens, click They match; otherwise They differ — cancel.
Click Sync to start exchanging edits live.
The pairing code is what makes this safe on a network you do not fully trust. It defeats a man-in-the-middle and stops random devices on the same Wi-Fi from joining. If the codes differ, something is wrong — cancel.
mDNS cannot cross a WAN, so someone off the LAN will never appear in discovery. Use the Join a remote host field with an address instead — TLS pinning and pairing are address-agnostic, so everything else works the same.
Reachability has to come from a VPN overlay. Do not port-forward the pairing port to the public internet.
The members list shows each person's role and status.
Rotate key issues a new team encryption key. Remaining members must re-pair to receive it.
The shield icon revokes a member, cutting them out of future syncs.
Auth secrets travel inside the synced document encrypted with a shared 32-byte team key (ChaCha20-Poly1305, per-field nonce), decrypted locally by each member.
One thing to be clear about: revoking a member cannot un-share data that already reached their disk. Rotating the key only protects future secrets. If someone leaves under bad circumstances, rotate the key and rotate the credentials themselves upstream.
Each device has its own cryptographic identity, stored only on that machine. There is no server-side account to fall back on — lose it and you re-pair from scratch.
Back up identity (cloud-download icon) in the Team panel saves a passphrase-protected backup file. Do this before wiping a machine or switching computers.
Restore identity (cloud-upload icon) on the new device brings the same identity back. Restoring overwrites whatever identity is currently on that device, so use it before you have paired anything, or right after a fresh install.
Open Work with Git from the left rail and pick a working directory — usually the root of a git repository. Axon mirrors the workspace's collections and environments into <folder>/axon/ as YAML files.
Every file keeps a stable id, so a rename or an edit shows up as a small diff instead of a delete and an add.
Axon only deletes files it wrote itself. It tracks them in a manifest, so files you added to the folder by hand are never touched.
Secrets are never written to these files. Vault entries and secret variables stay on your device.
Once linked, the pane shows the folder, its branch, how far you are ahead of or behind the remote, and the synced files that changed — each marked as added, modified, renamed, deleted or untracked.
Commit, Pull and Push run through the git installed on your system, so your existing credentials, SSH keys and hooks apply. Pulling brings teammates' changes back into Axon.
This is the asynchronous alternative to Team sync: changes are reviewed in pull requests instead of streaming live between machines.
A workspace is a separate space: its own collections, environments, history and team. (They all live in Axon's one local database, kept apart by workspace.) Use one per client, per product, or to keep a personal scratch space away from work.
Open the Workspaces page from the Home menu. It lists every workspace with name, type, access, whether it is active, and when it was created, and supports create, activate, rename and delete. Switching workspaces swaps everything the window shows.
The Workspace switcher in the header is the quick picker for the same thing.
Follow redirects — auto-follow 3xx Location headers, up to 10 hops.
SSL certificate verification — validate the peer's TLS chain against the system trust store. Turning this off is the right call against a staging box with a self-signed cert, and the wrong call everywhere else.
Always ask when closing unsaved tabs
Always open sidebar item in new tab
Two-pane view — show the response beside the request instead of below it.
Show icons with tab names — the method badge on request tabs and the protocol badge on protocol tabs.
Variable autocomplete — suggest variable names after you type {{ in the URL bar.
Editor behaviour: font and font size, auto close brackets, auto close quotes, indentation type (space or tab) and indentation count.
Light, Dark or System, plus seven named themes — Nebula, Abyss, Solar, Carbon, Ember, Verdigris and Rose. Alongside them: font family (a preset or your own), font size, and a font scale of Compact / Default / Comfortable.
Every command with its current chord. Click a row and press a new combination to rebind it; conflicting chords are flagged. Only changed bindings are stored, so defaults keep tracking the app. Reset appears on rows you have changed, and there is a reset-all.
Backup & restore. Backup writes the whole workspace out to a file; restore reads one back in. Take a backup before an upgrade, before a risky import, or before wiping a machine.
Check for updates on launch (on by default) looks for a newer signed release on GitHub when Axon starts and installs it. Turn it off and Axon makes no update requests; Check for updates checks once, and offers Install and restart when there is a newer version.
Axon → Plans… opens the plans page. Purchases happen in your system browser; the app carries no payment code. A license key unlocks the corresponding features in place.
These live in the command registry and can be reassigned in Settings → Shortcuts.
General
Mod+K — Command palette
Mod+Alt+P — Ask AI
Request
Mod+Enter — Send request
Mod+S — Save request
Mod+/ — Edit variables (floating editor)
Mod+Shift+R — Open runner (owned by the menu's New Runner Tab, so it isn't rebindable)
Edit
Mod+C — Copy (context-aware)
Mod+C yields to the browser's native copy when any text is selected (in a field or in rendered text such as a response body) or focus is inside a Monaco editor. Otherwise it copies whatever the focused pane advertises — a response body, a header table, a hit row.
Protocol tabs (WebSocket, gRPC, MQTT, …) use the same Send and Save chords while focus is inside the tab.
Open Settings → Shortcuts, click a row, and press the combination you want. A chord already claimed by another command is moved to the new one (and flagged); a chord the app menu owns is refused. Only bindings you change are stored — everything else follows the app's defaults, so upgrades never leave you with a stale map. Rows you have changed grow a Reset, and there is a reset-all at the bottom.