CLI
Reference for @apitraffic/cli: pull and check pinned offline definitions in CI, with authentication, flags, exit codes, JSON output and troubleshooting.
@apitraffic/cli keeps the offline definitions committed to your
repository in step with the configuration in your ApiTraffic account.
npx @apitraffic/cli definitions pull # write .apitraffic/definitions/sdk.json
npx @apitraffic/cli definitions check # fail when the committed file has drifted
Info: No install step and no dependencies. The package has zero runtime dependencies, so
npxfetches and runs it in about a second - which is the point, because its main home is a CI job. Node 20 or newer.
Install
You do not need to. npx is the intended way to run it, in CI and locally alike.
If you would rather pin a version - sensible for a CI gate, so a release cannot change your build’s behavior without a commit:
npx (recommended)
npx @apitraffic/[email protected] definitions check
npm
npm install --save-dev @apitraffic/cli
{
"scripts": {
"definitions:pull": "apitraffic definitions pull",
"definitions:check": "apitraffic definitions check"
}
}
pnpm
pnpm add -D @apitraffic/cli
Authentication
The CLI uses the same ingestion key your SDK already has. No new credential, no new permission, no extra setup - the snapshot endpoint carries the same permission as the settings endpoint the SDK calls on every boot.
| Variable | Meaning | Required |
|---|---|---|
API_TRAFFIC_TOKEN |
Your ingestion key. APITRAFFIC_TOKEN is read too. |
yes, or --token-stdin |
API_TRAFFIC_BUCKET |
The bucket ID, e.g. k3m9p2x |
yes |
API_TRAFFIC_API_HOST |
Defaults to api.apitraffic.io |
no |
API_TRAFFIC_DEFINITIONS_PATH |
Overrides where the file lives | no |
export API_TRAFFIC_TOKEN="your-ingestion-key"
export API_TRAFFIC_BUCKET="k3m9p2x"
For a one-off run, pass them as flags instead. This is the form the Offline Definitions dialog on the API Tokens page gives you (choose Definitions for an environment), with your bucket already filled in:
npx @apitraffic/cli definitions pull -b k3m9p2x -t YOUR_API_TOKEN
Warning: Use the flag for a one-off, the environment variable in CI. A token passed as an argument lands in your shell history, in
psoutput, and in the logs of any CI runner that echoes the commands it runs - which is fine for something you type once at your own terminal, and not fine for a pipeline.
definitions pull
Fetches your current configuration and writes it where the SDK will look for it.
npx @apitraffic/cli definitions pull
✔ wrote .apitraffic/definitions/sdk.json
! This file contains your WAF policy and redaction rules.
Both describe what you block and what you consider sensitive. Commit it to a
private repository; do not publish it.
bucket k3m9p2x
hash 679c2b8ce73c
generated 2026-08-14T12:00:00.000Z (today)
rules 12 redactions, 3 exclusions
waf block
Commit the change so your next cold start ships these rules.
Then commit it:
git add .apitraffic/definitions/sdk.json
git commit -m "chore: refresh ApiTraffic definitions"
What it will and will not do
- Rewrites an existing file wherever it already lives, walking up from the current directory. It will not create a second file lower down that the first would shadow - a “refresh” that silently changed nothing would be the worst possible outcome for a fallback.
- Refuses to overwrite a file pinned to a different bucket. Silently replacing one service’s redaction
rules with another’s is a data-protection incident dressed up as a convenience. Use
--forceto override, or--bucket-qualifiedto keep both. - Writes nothing if the API answers with something unusable. A bad response must not replace a file that currently works.
- Writes atomically, via a temporary file and a rename, so an interrupted pull cannot leave truncated
JSON for the next cold start to read. The temporary file has an unpredictable name, is created only if
nothing is already there, and is removed if any step fails. A new file is
0644; a rewrite keeps the permissions the existing file had. - Stays inside your repository. The walk up from the working directory stops at the repository root
(the first directory containing
.git), so a stray file above your checkout is never rewritten. - Formats for review: pretty-printed, with
configkey-sorted, so your commit diff shows which rule changed rather than one reflowed line.
definitions check
Compares the committed file against your live configuration. This is the command that makes the whole feature work, because it puts staleness in front of the person doing the deploy, at the moment of the deploy.
npx @apitraffic/cli definitions check
In sync
✔ .apitraffic/definitions/sdk.json is current
bucket k3m9p2x
hash 679c2b8ce73c
generated 2026-08-14T12:00:00.000Z (today)
Drifted
✖ .apitraffic/definitions/sdk.json is out of date
pinned 0b4187423da1 generated 74 days ago
current 679c2b8ce73c
Your running servers are unaffected - they hold live config.
This is about the next cold start that cannot reach ApiTraffic:
it would apply these rules instead of the current ones.
Refresh and commit: npx @apitraffic/cli definitions pull
Edited by hand
✖ .apitraffic/definitions/sdk.json has been edited by hand
states 679c2b8ce73c
contains 0b4187423da1 (matches your live config)
The config is right, but the file's own configHash disagrees with it.
Regenerate it so the two match: npx @apitraffic/cli definitions pull
Exit codes
| Code | Meaning | What it usually means |
|---|---|---|
0 |
In sync | Nothing to do |
1 |
Drifted, edited by hand, missing, or pinned to another bucket | Run definitions pull and commit |
2 |
Could not determine | Bad token, network failure, or a bad response |
Note:
1and2are deliberately different. Collapsing them into “non-zero” would mean an ApiTraffic blip breaks your build - which is the exact failure this feature exists to remove, reintroduced inside your pipeline. Choose which one you want to be strict about with the flags below.
Warning: By default, exit code
2fails your CI build whenever ApiTraffic is unreachable. In a pipeline, rundefinitions check --ignore-unreachable: drift and a rejected token still fail the build, but our outage does not block your deploy. See If ApiTraffic is unreachable.
npx @apitraffic/cli definitions check --ignore-unreachable
Local checks
A check in your local checkout answers a slightly different question from a CI one: it tells you the
account moved on since you last pulled, but blocking your commit on somebody else’s dashboard edit is
usually the wrong trade.
npx @apitraffic/cli definitions check --warn-only --quiet || true
Options
| Flag | Applies to | |
|---|---|---|
--bucket, -b |
both | Override API_TRAFFIC_BUCKET |
--token-stdin |
both | Read the token from standard input (one line, surrounding whitespace trimmed), e.g. printf %s "$TOKEN" | apitraffic definitions pull --token-stdin. Run in a terminal, it prompts on stderr and does not echo what you type |
--token, -t |
both | Override API_TRAFFIC_TOKEN. Prints a warning: an argument is visible in ps, shell history and CI logs - prefer the env var (API_TRAFFIC_TOKEN, or APITRAFFIC_TOKEN) or --token-stdin |
--api-host |
both | Override API_TRAFFIC_API_HOST |
--insecure |
both | Allow an http:// API host that is not localhost (sends the token unencrypted) |
--out, -o |
both | The file to write or check |
--redact-at |
both | client (default) or server |
--timeout |
both | Per attempt, default 15000 |
--json |
both | Machine-readable output on stdout |
--quiet, -q |
both | Suppress the human-readable output |
--bucket-qualified |
pull |
Write sdk.{bucketSid}.json |
--force |
pull |
Overwrite a file pinned to a different bucket |
--warn-only |
check |
Report drift, still exit 0 |
--ignore-unreachable |
check |
Exit 0 when ApiTraffic cannot be reached |
--help, -h |
||
--version, -v |
JSON output
--json prints a single object on stdout and nothing else, so a script can act on the result. Errors
still go to stderr - a CI log showing only {"ok":false} costs somebody twenty minutes.
check (drifted)
{
"ok": false,
"command": "check",
"status": "stale",
"filePath": "/repo/.apitraffic/definitions/sdk.json",
"configHash": "sha256:0b4187423da1...",
"remoteConfigHash": "sha256:679c2b8ce73c...",
"generatedAt": "2026-06-01T09:12:44.000Z",
"ageDays": 74,
"handEdited": false
}
status is one of current, stale, modified, missing, unreadable, bucket-mismatch or
undetermined.
pull
{
"ok": true,
"command": "pull",
"filePath": "/repo/.apitraffic/definitions/sdk.json",
"created": false,
"changed": true,
"bucketSid": "k3m9p2x",
"configHash": "sha256:679c2b8ce73c...",
"previousConfigHash": "sha256:0b4187423da1...",
"generatedAt": "2026-08-14T12:00:00.000Z"
}
Where the file lives
The CLI and the SDK resolve the path identically - if they disagreed, every command would report success while the SDK read a different file.
API_TRAFFIC_DEFINITIONS_PATH, if set. It is an override, not a preference: nothing else is checked..apitraffic/definitions/sdk.{bucketSid}.json.apitraffic/definitions/sdk.json
Both walk up from the working directory, so starting your process from a subdirectory of the repository
still finds the file at the root. The CLI stops at the repository root - the first directory containing
.git - because it manages the file your repository commits; inside the repository it sees exactly what
the SDK sees, in the same order.
Several buckets in one repository
API_TRAFFIC_BUCKET=p4n8w2k npx @apitraffic/cli definitions pull --bucket-qualified
API_TRAFFIC_BUCKET=s7q3m9d npx @apitraffic/cli definitions pull --bucket-qualified
Each SDK prefers the file matching its own bucket and falls back to a shared sdk.json. A file whose
bucketSid does not match is refused rather than best-guessed.
Reliability
The CLI is built on the assumption that it runs in a pipeline that must not break because of us.
- Transient failures are retried - three attempts with backoff - before anything is reported. A one second hiccup does not fail your build.
- Authentication failures are not retried. Repeating a rejected credential only slows down a failure whose cause will not change.
--ignore-unreachableconverts “we could not be reached” into a pass, while still failing on real drift and on a rejected token.
Troubleshooting
Missing API_TRAFFIC_TOKEN and API_TRAFFIC_BUCKET
Both missing values are reported together, on purpose - finding out about the second only after fixing the first costs a whole CI round trip.
The token is your ingestion key, the same one your SDK uses. Create or manage it on the API Tokens page in your account settings; see API tokens.
The API rejected the token (HTTP 401/403)
The token is invalid, or it does not grant at:DescribeSdkSettings on this bucket. An ingestion key
scoped to a different environment will produce this against a bucket it cannot see.
Bucket … was not found
The bucket sid is wrong, or the token cannot see it. Check API_TRAFFIC_BUCKET against the sid on the
bucket’s settings page.
check says the file is missing, but it is right there
The CLI looked where the SDK looks. Two things usually explain it: API_TRAFFIC_DEFINITIONS_PATH is
set in one environment but not the other, or the file is bucket-qualified for a different bucket than
API_TRAFFIC_BUCKET names.
Run with --json - filePath tells you exactly which file it read.
check keeps reporting ‘edited by hand’
The file’s own configHash field disagrees with a hash recomputed from its config. Something edited
the file after it was pulled - a formatter, a merge resolution, or a person.
It matters because the SDK reports the recomputed hash, so the Servers page would show drift that
the file’s own contents cannot explain. definitions pull regenerates both consistently.
Refusing to send the API token over plain http://
The API host is http:// and not this machine. The token is your ingestion key, and over plain HTTP
anyone on the network path can read it from the first request. Use https://, or a bare host name
(which means https://).
http://localhost, http://127.0.0.1 and http://[::1] are allowed without a flag, for local
development. For any other host (a compose service such as http://api:8081, an internal staging box),
pass --insecure if you trust that network.
‘…’ is not a bucket sid
API_TRAFFIC_BUCKET (or --bucket) holds something other than a bucket sid: a sid is letters, digits,
_ and - only, e.g. k3m9p2x. The value becomes part of the file name sdk.{bucketSid}.json, so
anything else - a path separator, .., a stray space or newline from a copied secret - is refused.
–token needs a value
The argument after --token (or -t) was another flag. A token that genuinely starts with - is
accepted as-is unless it is also one of this tool’s flags; --token= always works.
Could not write the file
A filesystem problem rather than an API one: a read-only checkout, or a directory the CI user cannot create. The CLI never partially writes - the existing file is untouched.
Related
-
Offline definitions: What the file is for and how the fallback works.
-
If ApiTraffic is unreachable: What the SDK and this CLI do during an outage.
-
Buckets API: The endpoint this CLI wraps.
-
Servers: See which servers carry a stale definitions file.