This is the full command and flag reference for @schemaclient/cli 0.2, built for
scanning — every flag below is spelled exactly as the CLI spells it. For the narrative
version of why the CLI exists and how collections and schemas fit together, read the
CI runner guide; this page is for when you already know what you want.
Install
npm install -g @schemaclient/cli
Requires Node.js 20+. Two equivalent binaries are installed: schemaclient and the
short alias scli. It is scli rather than sc because on Windows sc is already
the built-in Service Control tool, and shadowing a system utility is not a trade worth
making.
Global flags
These work on every command:
| Flag | Effect |
| --- | --- |
| -V, --version | print the version and exit |
| -v, --verbose | extra diagnostics and full stack traces |
| -s, --silent | suppress diagnostics; reporter output still goes to stdout |
| --no-color | disable ANSI colour |
| --ci | force non-interactive output (auto-detected in most CI environments) |
run — the collection runner
schemaclient run <target>
<target> is a collection file, or a single URL. Everything below applies to run.
Variables and secrets
| Flag | Effect |
| --- | --- |
| --var <key=value> | inline variable (repeatable) |
| --secret-var <key=value> | like --var, but masked to •••••• in all output |
| -e, --env <file> | JSON file of environment variables (collection mode) |
| --env-passthrough | expose every environment variable to the collection, not just SCHEMACLIENT_VAR_* ones — see environment variables |
Precedence, lowest to highest: process environment → config file → suite → folder →
--env file → data row → --var → captures → script writes. The practical version:
--var pins a value for the whole run, and a value written in the collection beats one
that happens to be exported in your shell.
Data-driven runs
| Flag | Effect |
| --- | --- |
| -n, --iterations <count> | run the collection this many times |
| -d, --data <file> | CSV or JSON data file — one iteration per row |
| --parallel <n> | run this many iterations concurrently |
| --seed <n> | make generated variables ({{$uuid}}, {{$randomInt}}, …) deterministic |
CSV headers may declare a type so values are not all sent as strings:
id:number,active:boolean,meta:json
1,true,{"tier":"pro"}
Under --parallel, iterations are isolated — a variable written in one iteration is
never visible to a sibling — and --seed derives values per iteration, so a parallel
run reproduces a sequential one.
Request control
| Flag | Effect |
| --- | --- |
| --bail [mode] | stop the run early on failure; mode is failure (default) or folder |
| --timeout <ms> | per-request timeout |
| --delay <ms> | pause between requests, for rate-limited APIs |
| --max-redirects <n> | how many redirects to follow (0 disables following) |
| -k, --insecure | accept self-signed or mismatched TLS certificates |
| --proxy <url> | route requests through an HTTP proxy |
Sidecar assertions
| Flag | Effect |
| --- | --- |
| --tests <file> | assertions sidecar (default: <collection>.tests.json) |
| --no-tests | ignore the sidecar assertions file |
| --collection <name> | which collection to run from a multi-collection export |
Collections exported from the desktop app do not carry assertions, so assertions live in the sidecar: the app owns the requests, the sidecar owns the CI contract.
Debugging
| Flag | Effect |
| --- | --- |
| --dry-run | resolve and print every request without sending anything |
| --print-vars | with --dry-run, show the resolved variables and their scopes |
| --unresolved <policy> | undefined {{var}} handling: error (default), warn, ignore |
An unresolved variable fails the request by default instead of sending {{baseUrl}}
into a URL as literal text — a typo should name itself, not surface as a DNS error.
--unresolved=warn restores the looser behavior if an old collection depends on it.
Output
| Flag | Effect |
| --- | --- |
| --reporter <name> | cli (default), junit, json, html, tap, or github |
| --out <file> | write the report to a file instead of stdout |
| --suppress-exit-code | always exit 0, even when assertions fail |
github is the cli output plus ::error annotations in the PR checks UI; json
is machine-readable with secrets redacted; html is a single self-contained file;
junit is the XML most CI dashboards render natively.
Schema and scripts
| Flag | Effect |
| --- | --- |
| --schema-strict | fail on response fields the schema does not declare (default: warn) |
| --no-scripts | skip all pre-request and test scripts |
Scripts run in a node:vm sandbox with no require or process access — but that
sandbox is isolation, not a hardened security boundary. Running a collection someone
else wrote? Use --no-scripts.
send — one ad-hoc request
schemaclient send GET http://127.0.0.1:8009/api/products/1/
schemaclient send POST https://api.example.com/graphql \
-H "Content-Type: application/json" -d '{"query":"{ products { id } }"}'
curl-shaped, with validation bolted on:
| Flag | Effect |
| --- | --- |
| -H, --header <"Key: Value"> | header (repeatable) |
| -d, --data <body> | request body, sent as-is |
| --schema <file> | validate the response against a schema file |
| --expect-status <code> | assert the response status code |
| --timeout <ms> | timeout, default 30000 |
A bare host works: localhost, 127.0.0.1, 0.0.0.0 and [::1] get http://,
everything else gets https://.
validate — offline, no network
schemaclient validate response.json --schema schemas/product.sc
Checks a JSON file against a schema without sending anything. --schema is required.
schema — work with .sc files
| Command | Purpose |
| --- | --- |
| schema lint <file.sc> | compile the schema and report diagnostics |
| schema mock <file.sc> | generate mock data (--name picks a schema in the file, --seed for deterministic output) |
| schema infer <response.json> | infer a schema from a JSON response body |
infer is the fast way to bootstrap a contract from a live endpoint:
curl -s https://api.example.com/products/1 > response.json
schemaclient schema infer response.json --method GET --path "/products/{id}"
--method and --path produce an @http response block (--status sets its status
code, default 200); --name infers a bare named type instead. The engine behind all
three subcommands is the same one the desktop app uses for
schema validation, so a contract behaves
identically in both.
init — scaffold a suite
schemaclient init my-api-suite
Creates a starter suite and an example schema in the directory (default: the current
one). --force overwrites existing files.
Exit codes
The exit codes are a stable contract — script against them:
| Code | Meaning |
| --- | --- |
| 0 | everything passed |
| 1 | at least one assertion or test failed |
| 2 | usage error — bad flag or missing argument |
| 3 | load error — a file was missing or malformed |
| 4 | internal error — a bug; please report it |
--suppress-exit-code forces 0 after test failures. It does not mask a 2: a
mistyped flag is still a mistyped flag.
Environment variables
Only variables named SCHEMACLIENT_VAR_<NAME> are visible to a collection, as
{{NAME}} — and they arrive pre-masked as secrets:
SCHEMACLIENT_VAR_TOKEN=abc123 schemaclient run suite.scoll.json # provides {{TOKEN}}
The restriction is deliberate. A collection is executable input, and you will run
collections other people wrote — if the whole environment were readable, any shared
collection could exfiltrate {{AWS_SECRET_ACCESS_KEY}} just by interpolating it into
a URL. The prefix makes exposure a decision instead of a default. --env-passthrough
opts into the full environment for when you wrote the collection yourself and want the
convenience.
A minimal CI job
# .github/workflows/api-tests.yml
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npx @schemaclient/cli run ./suite.scoll.json --reporter junit --out reports/junit.xml
env:
SCHEMACLIENT_VAR_TOKEN: ${{ secrets.API_TOKEN }}
For the collection format, sidecar files, and the schema language itself, continue with the CI runner guide.