CLI

Reference for @apitraffic/cli: pull and check pinned offline definitions in CI, with authentication, flags, exit codes, JSON output and troubleshooting.

This page as Markdown

@apitraffic/cli keeps the offline definitions committed to your repository in step with the configuration in your ApiTraffic account.

bash
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 npx fetches 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)

bash
npx @apitraffic/[email protected] definitions check

npm

bash
npm install --save-dev @apitraffic/cli
json
{
  "scripts": {
    "definitions:pull": "apitraffic definitions pull",
    "definitions:check": "apitraffic definitions check"
  }
}

pnpm

bash
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
bash
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:

bash
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 ps output, 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.

bash
npx @apitraffic/cli definitions pull
text
✔ 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:

bash
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 --force to override, or --bucket-qualified to 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 config key-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.

bash
npx @apitraffic/cli definitions check

In sync

text
✔ .apitraffic/definitions/sdk.json is current

  bucket     k3m9p2x
  hash       679c2b8ce73c
  generated  2026-08-14T12:00:00.000Z (today)

Drifted

text
✖ .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

text
✖ .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: 1 and 2 are 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 2 fails your CI build whenever ApiTraffic is unreachable. In a pipeline, run definitions 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.

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

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

json
{
  "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

json
{
  "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.

  1. API_TRAFFIC_DEFINITIONS_PATH, if set. It is an override, not a preference: nothing else is checked.
  2. .apitraffic/definitions/sdk.{bucketSid}.json
  3. .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

bash
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-unreachable converts “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.