Coax documentation
Coax is a polished, file-based HTTP client. Your workspace is just a folder of
.http files — edit them in the desktop app, then run the same files headlessly
in CI with the CLI. This guide covers both.
Getting started
- Install & launch. Coax runs on macOS, Windows, and Linux. Get a license to download the app, then install and launch it.
-
Open a workspace. On first launch, pick Open a workspace folder
and choose any folder — Coax treats every
.httpfile inside it as a collection. New to Coax? Choose Try with examples to start from a ready-made sample workspace. - Open a request. Click any request in the sidebar; it opens in a new tab.
-
Set variable values. If your file has
@baseUrl = …lines, those become an environment that's activated automatically. Edit values on the request's Vars tab or from the Settings → environments manager. - Send. Hit the green Send button. The response renders in the lower pane.
Workspaces
A workspace is a folder on disk — nothing more. The .http files
inside it are the source of truth, so a workspace lives happily in a Git repo right next to the
code it tests, and your teammates get the exact same requests when they clone.
- Sub-folders inside the workspace become folders in the sidebar tree.
- Environment files (
*.env.json) sit next to the.httpfile they belong to. - Coax keeps a fast per-machine cache of the folder, but it never replaces your files — move or rename the folder and Coax just rebuilds the cache from the files on disk.
Open another workspace any time from the application menu; the active workspace is shown in the status bar.
Importing
Use the Import menu in the header. Coax can import from three sources:
| Source | What you get |
|---|---|
| Import .http… | A .http file (VS Code REST Client / JetBrains HTTP Client format) becomes a collection. |
| Import Swagger from URL… | Point at a live OpenAPI/Swagger document URL; each operation becomes a request. |
| Import Swagger from file… | Same, from a local OpenAPI/Swagger JSON or YAML file. |
When importing a .http file, Coax:
- Turns
@var = valuelines into an environment scoped to the new collection, and activates it so{{var}}references resolve immediately. - Creates one request per
### Titleblock, preserving headers and body verbatim. - Treats
############-style comment dividers as folder names. - Reads
# @name fooas a request's chain name.
Collections & requests
The sidebar is a single tree:
- Collection — one
.httpfile (folder icon). - Folder — a sub-grouping inside a collection.
- Request — method + URL + headers + body + optional auth + chain name, shown with a colored method badge.
Right-click any collection, folder, or request for a context menu — add a new request, rename, or delete. Method badge colors: GET = blue, POST = green, PUT = orange, PATCH = purple, DELETE = red, HEAD = cyan, OPTIONS = indigo.
Editing a request
Open a request to edit it. The tab is organized into sub-tabs:
| Tab | What it edits |
|---|---|
| Method & URL | The top row. Method dropdown (GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS) + URL. The URL supports {{var}} references, resolved at send time. |
| Params | The ?key=value query string, as a key/value editor that stays in sync with the URL. |
| Headers | Request headers, same key/value editor. |
| Body | Pick a kind — none, text, json, form, multipart, or graphql — and edit it in the Monaco editor with syntax highlighting. |
| Auth | Capture credentials for None / Bearer / Basic / API Key. See the note below. |
| Vars | Shows the resolved value of every {{reference}} in the request, so you can confirm a value (or a chain) before sending. |
| cURL | The equivalent curl command for the request. |
Edits autosave shortly after you stop typing — there's no Save button. Above the method row is an optional Chain name field; set it to make this request's response referenceable from others (see Response chaining).
Note on Auth: the Auth tab stores credentials with the request, but Coax does not yet
auto-inject them into the outgoing request. For now, add an explicit header on the
Headers tab — e.g. Authorization: Bearer {{token}}.
Variables & environments
- A variable is a
name → valuemapping. Reference it anywhere with{{name}}. - An environment is a named bag of variables, scoped either global (visible everywhere) or to a specific collection.
- One environment per scope is active at a time. Switch the active environment from the header dropdown; create and edit environments from Settings → environments.
Resolution order, highest precedence first:
- Request-local vars (
@name = valueinside the request) - Active collection environment
- Active global environment
- Built-ins:
{{$timestamp}},{{$isoTimestamp}},{{$guid}},{{$randomInt min max}}
Secrets
Mark any variable as secret (in the environment manager, or via the lock icon on the Vars tab). Secret values are encrypted at rest using your operating system's secure storage and masked in the UI until you reveal them. When you export a collection, secrets are written as placeholders rather than real values, so the file is safe to share.
In CI the CLI can't read your machine's secure storage, so it reads each secret from an environment variable instead — see Environments & secrets below.
Response chaining
Thread one request's response into another:
- Set the source request's Chain name (e.g.
getToken). - Send it — the response is saved.
- Reference it elsewhere:
{{getToken.response.body.$.access_token}}.
JSONPath is supported ($.field, $.items[0], $..nested). For a
response header, use {{getToken.response.headers.x-request-id}} (header names lowercased).
The Vars tab shows the resolved value so you can confirm a chain works before sending.
Sending & reading responses
Click Send. While in flight the button shows a spinner; click it again to cancel. When the response arrives you get a status pill colored by class (2xx green, 3xx blue, 4xx orange, 5xx / network error red) plus the time and size. Network, TLS, timeout, and invalid-URL failures render with a labeled error pill and the underlying message.
The response pane has three tabs:
- Body — pretty-printed JSON / XML / HTML / text (read-only).
- Headers — the response headers.
- Raw — the full HTTP transcript: the request that was sent, then the response received.
Each open request is a tab in the strip at the top; tabs persist across restarts. Cycle the theme (light → dark → system) with the ☾ button, and drag the dividers to resize the sidebar and the request/response split — both are remembered.
Exporting
Click Export collection and choose where to save the .http file.
Variable definitions are written as @name = value at the top; secret
variables become placeholders instead of real values. If a request carries an Authorization
header with a literal token (not a {{var}}), Coax warns you so you can scrub it before
sharing. The result opens cleanly in VS Code REST Client and the JetBrains HTTP Client.
Where your data lives
Your workspace folder is the source of truth — the .http and
*.env.json files you can see, edit, and commit. Coax also keeps a per-machine cache to
make opening large workspaces fast:
macOS ~/Library/Application Support/Coax/workspaces/<hash>/cache.sqlite
Windows %APPDATA%\Coax\workspaces\<hash>\cache.sqlite
Linux ~/.config/Coax/workspaces/<hash>/cache.sqlite
The cache is derived data — deleting it is safe; Coax rebuilds it from your files the next time you open the workspace. Secret values are never written to disk in the clear; they live in your OS secure storage.
Command-line (CLI)
Run the same .http files headlessly, with inline assertions — built for CI/CD, smoke
tests, contract verification, and synthetic monitoring. The file your team edits in the desktop app
is the file CI runs, unchanged.
Install & activate
The CLI is included with every Coax license. Install from npm (requires Node 18+):
npm install --global @melodicdev/coax-cli
Set your license key as an environment variable:
export COAX_LICENSE_KEY=COAX-XXXX-XXXX-XXXX-XXXX
In CI, store the key as a secret (GitHub Actions: secrets.COAX_LICENSE_KEY;
GitLab: $COAX_LICENSE_KEY protected variable; etc.).
Usage & flags
coax run <file...> [options]
| Flag | Default | Purpose |
|---|---|---|
-e, --env <name> | none | Load vars from the *.env.json sibling whose name matches |
-r, --request <title> | none | Only run requests whose title or # @name matches |
-v, --var <key=value> | none | Override a variable; repeatable |
-o, --output <reporter> | pretty | pretty (terminal) or junit (XML for CI dashboards) |
-t, --timeout <ms> | 30000 | Per-request timeout in milliseconds |
-k, --insecure | off | Skip TLS cert validation (for self-signed dev servers — like curl -k) |
--fail-fast | off | Stop at the first failed request or assertion |
--no-color | off | Disable ANSI colors (auto-disabled on non-TTY) |
Exit codes
| Code | Meaning |
|---|---|
0 | All requests succeeded, all assertions passed |
1 | At least one assertion failed |
2 | At least one request failed (network, timeout, invalid URL) |
3 | Parse error or CLI usage error |
4 | License missing or invalid |
Assertions
Inline assertions live above the request line, alongside # @name:
### Get user
# @name getUser
# @test status == 200
# @test $.user.email exists
# @test $.user.id == 42
# @test responseTime < 500
# @test headers.content-type contains "application/json"
GET https://api.example.test/users/42
Grammar: <left> <operator> [<right>]
| Left side | Meaning |
|---|---|
status | HTTP status code |
responseTime | Response time in milliseconds |
headers.<name> | Response header by name (case-insensitive) |
$.path.to.value | JSONPath into the parsed JSON body |
| Operator | Meaning | Notes |
|---|---|---|
== | Equal | Numeric coercion: 200 == "200" is true |
!= | Not equal | |
< <= > >= | Numeric comparison | Fails cleanly if either side is non-numeric |
contains | String contains | Both sides must be strings |
exists | Value is present and non-null | No right side |
Right side: number, quoted string, true/false/null, bare string, or {{var}} reference (resolved before the assertion is parsed).
Environments & secrets
Drop *.env.json files next to your .http file and select one with
--env <name> — the same format the desktop app uses, so a workspace round-trips
between local editing and CI without changes.
my-workspace/
api.http
api.dev.env.json ← { "name": "dev", "vars": [...] }
api.staging.env.json ← { "name": "staging", "vars": [...] }
api.prod.env.json ← { "name": "prod", "vars": [...] }
coax run api.http --env staging
Variable precedence (highest wins):
--var key=valueflags on the command line- Vars from the
--env <name>file @var = valuedeclarations at the top of the.httpfile
Secrets in an env file ({ "key": "apiKey", "isSecret": true, … }) live in your OS secure
storage when authored in the desktop. The CLI can't read that, so it looks for an environment variable
named COAX_SECRET_<KEY> (the key uppercased):
export COAX_SECRET_APIKEY=sk_live_xxx
coax run api.http --env prod
A missing secret becomes a warning, not an error — the run continues with the non-secret vars available.
Response chaining
The CLI threads each response into the resolver, so chain references work end to end:
### Login
# @name login
POST {{baseUrl}}/login
Content-Type: application/json
{ "user": "rick" }
### Get my profile
# @test status == 200
GET {{baseUrl}}/users/{{login.response.body.$.userId}}
Authorization: Bearer {{login.response.body.$.token}}
Requests run in file order — put dependencies above their consumers.
CI integration
GitHub Actions:
jobs:
smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Install Coax CLI
run: npm install --global @melodicdev/coax-cli
- name: Run API smoke tests
env:
COAX_LICENSE_KEY: ${{ secrets.COAX_LICENSE_KEY }}
run: coax run tests/smoke.http --output junit > coax-results.xml
- name: Upload JUnit results
if: always()
uses: actions/upload-artifact@v4
with:
name: coax-results
path: coax-results.xml
Questions?
CLI package: @melodicdev/coax-cli. Email support@melodic.dev — or buy a license if you haven't yet.