Coax CLI

Run .http files headlessly with inline assertions. Built for CI/CD, smoke tests, contract verification, and synthetic monitoring. The same .http file your team already edits in the desktop app.

Install

The CLI is included with every Coax license. Install from npm:

npm install --global @melodicdev/coax-cli

Requires Node 18+.

Activate

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.).

Quick start

Create a .http file with a request and one or more inline assertions:

### Healthcheck
# @test status == 200
# @test responseTime < 500
GET https://api.example.com/health

Run it:

coax run healthcheck.http

Usage

coax run <file...> [options]
FlagDefaultPurpose
-e, --env <name>noneLoad vars from <name>.env.json next to the .http file
-r, --request <title>noneOnly run requests whose title or # @name matches
-v, --var <key=value>noneOverride a variable; repeatable
-o, --output <reporter>prettypretty (terminal) or junit (XML for CI dashboards)
-t, --timeout <ms>30000Per-request timeout in milliseconds
-k, --insecureoffSkip TLS cert validation (for self-signed dev servers — matches curl -k)
--fail-fastoffStop at the first failed request or assertion
--no-coloroffDisable ANSI colors (auto-disabled on non-TTY)

Exit codes

CodeMeaning
0All requests succeeded, all assertions passed
1At least one assertion failed
2At least one request failed (network, timeout, invalid URL)
3Parse error or CLI usage error
4License missing or invalid

Assertion syntax

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:

FormMeaning
statusHTTP status code
responseTimeResponse time in milliseconds
headers.<name>Response header by name (case-insensitive)
$.path.to.valueJSONPath into the parsed JSON body

Operators:

OpMeaningNotes
==EqualNumeric coercion: 200 == "200" is true
!=Not equal
< <= > >=Numeric comparisonFails cleanly if either side is non-numeric
containsString containsBoth sides must be strings
existsValue is present and non-nullNo right side

Right side: number, quoted string, true/false/null, bare string, or {{var}} reference (resolved before the assertion is parsed).

Environment files

Drop *.env.json files next to your .http file and select one with --env <name> — the same format the Coax desktop app uses, so a workspace round-trips between local editing and CI without changes.

my-workspace/
  api.http
  dev.env.json       ← { "name": "dev",     "vars": [...] }
  staging.env.json   ← { "name": "staging", "vars": [...] }
  prod.env.json      ← { "name": "prod",    "vars": [...] }
coax run api.http --env staging

Variable precedence (highest wins)

  1. --var key=value flags on the command line
  2. Vars from the --env <name> file
  3. @var = value declarations at the top of the .http file

Secrets

Secrets in an env file ({ "key": "apiKey", "isSecret": true, ... }) live in the OS keychain when authored in the desktop. The CLI can't read the keychain, so it looks for an env var:

export COAX_SECRET_APIKEY=sk_live_xxx
coax run api.http --env prod

Missing secrets become warnings, not errors — the run continues with the unsecured vars available.

Response chaining

The CLI threads each response into the resolver context 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

Roadmap

Source & support

Package: @melodicdev/coax-cli · Examples: github.com/MelodicDevelopment/coax · Email: rickhopkins@melodic.dev