The command-line interface for the Treblle Platform.
List the APIs in your workspace, inspect the requests and endpoints Treblle has observed, upload OpenAPI specifications, and score them against Treblle's governance rules — from your terminal or from CI.
$ treblle apis list
┌───────────────┬───────────┬────────────┬────────┬─────────┬───────────────┐
│NAME │SDK │REQUESTS │SCORE │ISSUES │LAST REQUEST │
├───────────────┼───────────┼────────────┼────────┼─────────┼───────────────┤
│Billing API │laravel │1.2M │A │0 │2m ago │
│Identity API │express │410K │C │7 │11m ago │
└───────────────┴───────────┴────────────┴────────┴─────────┴───────────────┘
$ treblle governance design-time --api api_xxx --fail-under 80
Grade: A
Score: 91.5%
Spec version: 2.1.0
Uploaded: 2d ago- Install
- Quickstart
- Command reference
- Using the CLI
- Governance and failing a build
- Running in CI/CD
- Configuration
- Development
go install github.com/Treblle/cli/cmd/treblle@latestRequires Go 1.25 or newer. The binary lands in $(go env GOPATH)/bin — make
sure that directory is on your PATH.
Prebuilt binaries for macOS, Linux and Windows (amd64 and arm64) are attached to every release. No runtime is needed — it is a single static binary.
VERSION=0.1.2 # see the releases page for the latest
OS=darwin # darwin | linux | windows
ARCH=arm64 # amd64 | arm64
curl -sSLO "https://github.com/Treblle/cli/releases/download/v${VERSION}/treblle_${VERSION}_${OS}_${ARCH}.tar.gz"
tar -xzf "treblle_${VERSION}_${OS}_${ARCH}.tar.gz"
sudo mv treblle /usr/local/bin/
treblle --versionEvery release also publishes a checksums.txt if you want to verify the
download. On Windows the archive is a .zip instead of a .tar.gz.
git clone https://github.com/Treblle/cli.git
cd cli
make build # -> ./bin/treblle
make install # -> $GOPATH/bin/treblleThree commands and you are running:
treblle auth login # paste a personal access token
treblle configure # choose the workspace to work in
treblle apis list # confirm it workstreblle configure also runs the login step if you have not authenticated yet,
so it works as a single entry point too.
treblle auth login prints a link to
your Treblle developer settings.
Two details there are easy to miss, and both stop the token from working:
Important
- Set the app to
platform. A token scoped to a different Treblle app authenticates fine but carries no permissions here, so every command fails with403.treblle auth logincatches this and refuses the token rather than saving something that cannot work. - Multi-factor authentication must be enabled on your account, or token creation is silently refused.
Choosing Never for the expiry means you never have to repeat this.
Check where you stand at any time:
treblle auth status| Command | What it does |
|---|---|
treblle auth login |
Authenticate with a personal access token |
treblle auth status |
Show the active profile, user, app scope and token expiry |
treblle configure |
Authenticate if needed, then choose and save a workspace |
treblle apis list |
List the APIs in the active workspace |
treblle apis get <id> |
Show one API |
treblle requests list --api <id> |
List the requests logged for an API |
treblle requests get <id> --api <id> |
Show one logged request |
treblle endpoints list --api <id> |
List the endpoints discovered for an API |
treblle endpoints get <id> --api <id> |
Show one endpoint |
treblle specs list --api <id> |
List the specifications uploaded to an API |
treblle specs get <id> --api <id> |
Show one specification |
treblle specs upload <file> --api <id> |
Upload an OpenAPI specification |
treblle governance design-time --api <id> |
Score the uploaded specification |
treblle governance run-time --api <id> |
Score the observed traffic |
Every command supports --help. Most have short aliases — api, request,
endpoint, spec, gov, and show in place of get.
| Flag | Purpose |
|---|---|
--profile <name> |
Use a named configuration profile (default default) |
--workspace <id> |
Operate on a specific workspace, ignoring the saved one |
-o, --output <format> |
table (default) or json |
--version |
Print the CLI version |
Every list command (apis, requests, endpoints, specs) also takes
--limit and --all.
Note
The API caps a page at 100 results. Use --all to fetch every page rather
than passing a large --limit.
Tip
-o json returns the API response unchanged, which is exactly what you
want for jq and for scripting. Note that it includes fields the tables
deliberately leave out — api_key, ip_address, external_user_id — so
tables stay safe to show on a shared screen.
Everything except apis is scoped to a single API, so those commands need
--api. Get an id from treblle apis list.
treblle apis list
treblle apis list --search billing --sdk laravel
treblle apis list --sort -requests --limit 50
treblle apis list --all -o json | jq '.data[].attributes.name'| Flag | Filter |
|---|---|
--search |
name or URL |
--sdk |
SDK, for example laravel, express, django |
--type |
API type |
--category |
category id |
--environment |
environment id |
Sort by name, score, issues, requests, latency, created, updated
or last-request. Prefix with - for descending.
treblle apis get api_xxx
treblle apis get api_xxx -o json | jq '.data.attributes.api_key'treblle requests list --api api_xxx
treblle requests list --api api_xxx --method POST --status 500,4xx
treblle requests list --api api_xxx --period hour,24 --sort -latency
treblle requests list --api api_xxx --all -o json | jq '.data[].attributes.path'| Flag | Filter |
|---|---|
--search |
request path, at least 3 characters |
--status |
status code — comma-separated, and 4xx-style classes work |
--method |
HTTP method, comma-separated |
--endpoint |
endpoint id, comma-separated |
--period |
time window, as unit,value — minute, hour, day, week, month (e.g. hour,24) |
--user |
external user id |
Sort by created, path, method, endpoint, latency, threat, device
or location. Newest first unless you say otherwise.
treblle requests get req_xxx --api api_xxx
treblle requests get req_xxx --api api_xxx -o json | jq '.data.attributes.request.headers'The detail view prints identity and timing. The captured traffic itself —
headers, bodies, queries, server data — routinely carries Authorization
headers and cookies, so it is left to -o json.
treblle endpoints list --api api_xxx
treblle endpoints list --api api_xxx --zombie
treblle endpoints list --api api_xxx --period day,7 --sort -requests
treblle endpoints get ep_xxx --api api_xxx| Flag | Filter |
|---|---|
--search |
endpoint path |
--method |
HTTP method, comma-separated |
--in-docs |
only endpoints in the docs (--in-docs=false for those excluded) |
--has-problems |
only endpoints with problems (--has-problems=false for those without) |
--zombie |
only zombie endpoints — no traffic in 30 days |
Sort by path, rpm, requests, latency, created or updated.
Warning
endpoints --period changes the numbers, not which rows appear — it sets
the window that request counts and load times are measured over, defaulting to
90 days. It also accepts only a fixed set of windows (minute,1, minute,5,
hour,1, hour,24, hour,48, hour,72, day,1 through day,180, week,1,
week,2, month,1 to month,3); anything else is rejected rather than
silently falling back to 90 days. This is unlike
governance run-time --period, which is free-form.
treblle specs list --api api_xxx
treblle specs list --api api_xxx --sort -score
treblle specs get spec_xxx --api api_xxx
treblle specs upload openapi.yaml --api api_xxx
treblle specs upload openapi.json --api api_xxx --yes # no prompt, for CIUploads accept .json, .yaml and .yml up to 15 MB, and add a version
rather than replacing anything — the API rejects a version it already holds. The
version comes from info.version in the document and must read as 1.0.0 or
v1.
specs list takes --search to filter by file name or version, and sorts by
file, version, created, source, score or uploader. A specification
that is still being generated is not listed until it completes.
Important
specs upload is the one command that writes, so it asks for confirmation.
With no terminal attached it refuses instead of guessing — pass --yes in a
pipeline. Uploading also queues a governance run, so neither the new
version nor its score is readable immediately.
Governance is two separate reports:
design-timescores the OpenAPI specification you uploaded.run-timescores the traffic Treblle has actually observed.
treblle governance design-time --api api_xxx
treblle governance design-time --api api_xxx --tests
treblle governance design-time --api api_xxx --spec-version 1.2.0
treblle governance run-time --api api_xxx
treblle governance run-time --api api_xxx --period day,30 --testsBoth print an overall grade, then a per-category breakdown with a count of tests
that did not pass. --tests lists those findings in full, with the reason and
the remediation attached to each.
Grade: C
Score: 72.5%
Spec version: 1.4.0
Uploaded: 2d ago
┌───────────────┬───────┬───────┬───────┬─────────┐
│CATEGORY │GRADE │SCORE │TESTS │FAILING │
├───────────────┼───────┼───────┼───────┼─────────┤
│Security │D │50% │2 │1 │
│Documentation │A │95% │2 │1 │
└───────────────┴───────┴───────┴───────┴─────────┘
run-time --period takes any unit,value and defaults to day,7.
--fail-under turns either report into a build step. It takes a percentage
against the overall score, and it is under — a score exactly on the threshold
passes.
treblle governance design-time --api api_xxx --fail-under 80 --tests
treblle governance run-time --api api_xxx --fail-under 90 --period day,30The report is printed before the verdict, so a failing gate still leaves the
score and its categories in the log. -o json is unaffected: stdout stays
parseable either way, and the verdict goes to stderr and the exit code.
Warning
An API that was never scored fails the gate, even at --fail-under 0.
Design-time answers an API with no uploaded specification with a zero score and
an F, and run-time answers a window with no governance runs the same way.
Those are exactly the states a pipeline should catch — but note the
consequence: a freshly deployed API with a short --period legitimately has no
data yet, and will fail.
Grades are not accepted as a threshold; the API documents grade as an
unconstrained string, so ordering the letters would be a contract the CLI cannot
actually check.
The CLI is built for pipelines: a single static binary, environment variables instead of config files, and no prompts when there is no terminal.
Environment variables override the config files entirely, so CI needs no
~/.treblle at all:
| Variable | Value |
|---|---|
TREBLLE_TOKEN |
A personal access token scoped to the platform app |
TREBLLE_WORKSPACE |
The workspace id (from treblle configure) |
| Code | Meaning |
|---|---|
0 |
Success |
1 |
The command failed — no credentials, no workspace, a bad flag, a network or API error |
2 |
A governance threshold was not met |
2 exists so a pipeline can tell a verdict about the API from a fault in the
run: a --fail-under breach is a real result and should fail the build, while an
expired token failing the same step means the build never got an answer at all.
Nothing but --fail-under returns 2.
Gate a pull request on the design-time score:
name: API governance
on: pull_request
jobs:
governance:
runs-on: ubuntu-latest
env:
TREBLLE_TOKEN: ${{ secrets.TREBLLE_TOKEN }}
TREBLLE_WORKSPACE: ${{ secrets.TREBLLE_WORKSPACE }}
TREBLLE_API: ${{ vars.TREBLLE_API }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
- name: Install the Treblle CLI
run: go install github.com/Treblle/cli/cmd/treblle@latest
- name: Score the API specification
run: treblle governance design-time --api "$TREBLLE_API" --fail-under 80 --testsPublish the specification once the change is merged:
name: Publish API specification
on:
push:
branches: [main]
paths: ['openapi.yaml']
jobs:
upload:
runs-on: ubuntu-latest
env:
TREBLLE_TOKEN: ${{ secrets.TREBLLE_TOKEN }}
TREBLLE_WORKSPACE: ${{ secrets.TREBLLE_WORKSPACE }}
TREBLLE_API: ${{ vars.TREBLLE_API }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
- run: go install github.com/Treblle/cli/cmd/treblle@latest
- run: treblle specs upload openapi.yaml --api "$TREBLLE_API" --yesImportant
Keep upload and scoring in separate runs. An upload queues a governance run asynchronously, so scoring in the very next step would grade the previous version and report a stale result as if it were the new one.
api-governance:
image: golang:1.25
variables:
TREBLLE_API: api_xxx
before_script:
- go install github.com/Treblle/cli/cmd/treblle@latest
script:
- treblle governance design-time --api "$TREBLLE_API" --fail-under 80 --testsAdd TREBLLE_TOKEN and TREBLLE_WORKSPACE as masked, protected CI/CD variables.
Nothing above is GitHub- or GitLab-specific. Download the release binary and export the two variables:
# Pin whichever version you want from the releases page.
curl -sSL https://github.com/Treblle/cli/releases/download/v0.1.2/treblle_0.1.2_linux_amd64.tar.gz | tar -xz
export TREBLLE_TOKEN="$MY_SECRET_TOKEN"
export TREBLLE_WORKSPACE=wsp_xxx
./treblle governance run-time --api api_xxx --period day,30 --fail-under 90With no TTY attached the CLI never prompts:
echo "$TREBLLE_PAT" | treblle auth login # reads the token from stdin
treblle configure --workspace wsp_xxx # --workspace instead of a picker
treblle specs upload openapi.yaml --api api_xxx --yesSettings live in two files under ~/.treblle, split so the secret can carry a
stricter mode:
# ~/.treblle/config.yaml (0644)
default:
base_url: https://api.treblle.com/v1
identity_url: https://auth.treblle.com
workspace: wsp_xxx
output: table# ~/.treblle/credentials.yaml (0600)
default:
token: "..."
expires_at: null # null means the token never expires
app: platformcredentials.yaml is written 0600, and the CLI refuses to read it if anyone
else can.
Profiles work like the AWS CLI's: a named block in both files, selected with
--profile, TREBLLE_PROFILE, or defaulting to default.
treblle configure --profile staging
treblle apis list --profile stagingThese override the files entirely.
| Variable | Purpose |
|---|---|
TREBLLE_TOKEN |
Personal access token |
TREBLLE_WORKSPACE |
Workspace id |
TREBLLE_BASE_URL |
API base URL |
TREBLLE_PROFILE |
Profile to use |
TREBLLE_CONFIG_DIR |
Config directory (default ~/.treblle) |
make check # fmt, vet, test, verify-spec
make test
make buildContributions are welcome — open an issue or a pull request. CI runs formatting,
go vet, the race-enabled test suite, and the spec check on every pull request.
api/openapi.json is a synced copy of the platform repository's spec.
make verify-spec checks that every route the CLI calls still exists in it, so
an upstream rename fails in CI rather than in someone's terminal. Refresh it with
make sync-spec (set PLATFORM_REPO if your checkout is not at ../platform).
The spec drifts from the live API in a few places, which is why the request and endpoint commands are written against the platform's controllers instead:
- Filter names are
filter[http_codes]andfilter[endpoints], not the singular forms the spec documents. The API ignores an unrecognised filter rather than rejecting it, so the documented names would return an unfiltered list that looks filtered. formatted_load_timeandformatted_response_sizeare typed as strings but arrive as{value, unit, is_good}objects.- The spec lists no filters on the requests collection; the controller accepts the full set the CLI exposes.
Several filters also fail silently on a value they cannot parse — a malformed
--period, a search below the minimum length, an unrecognised metrics window —
so the CLI validates those before sending rather than letting the API return
everything as if it had filtered.
Publishing is driven by the tag. Pushing v1.2.3 runs the tests, builds the
binaries with GoReleaser, attaches them to a GitHub Release, and makes
go install github.com/Treblle/cli/cmd/treblle@v1.2.3 work.
Login goes through a LoginStrategy interface with one implementation today
(paste). A browser-based loopback flow is the intended second one; the seam
exists so adding it changes no commands, no config format, and no client code.