> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trevosdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> Drive proposals and experiments from a terminal, a script, or CI.

`trevo` is the dashboard's `/v1` routes with a workspace API key instead of a browser session.
Anything it does, a person could do by clicking — list what Trevo proposed, approve or dismiss,
send revision instructions, ask for a fresh batch, start and end experiments.

```bash theme={null}
npm install -g @trevosdk/cli
trevo login
trevo proposals list
```

It has no runtime dependencies and needs Node 18 or newer.

## Authentication

Make a key under **Settings → API access**. It is shown once.

```bash theme={null}
trevo login                   # paste the key; stored at ~/.config/trevo/config.json, mode 0600
trevo login --key tsk_api_…   # non-interactive
```

Prefer the prompt on a shared machine: a key passed as `--key` is visible in `ps` and lands in your
shell history. In CI, use `TREVO_API_KEY` and never `trevo login`.

The CLI sends the key only to `https://api.trevosdk.com` unless `TREVO_API_URL` says otherwise, and
it refuses to send it over plain `http` to anything but a local address, to a URL carrying
credentials, or over a scheme that is not http(s). Pointing it anywhere other than the product host
prints a warning on stderr — in CI that line is the difference between a redirected key and a silent
one.

In CI, set `TREVO_API_KEY` instead — the environment wins over the stored file, so a job never
writes one:

```yaml theme={null}
- run: trevo experiments list --status running --json
  env:
    TREVO_API_KEY: ${{ secrets.TREVO_API_KEY }}
```

A key opens **one workspace**, reads everything in it, and writes only what its scopes allow. It
acts as the person who made it. Scopes, refusal codes, and the routes themselves are in the
[API reference](/reference/api).

`tsk_live_` and `tsk_secret_` SDK keys are your site's and are refused here. `TREVO_API_URL`
points the CLI at another host if you run one.

## Commands

```bash theme={null}
trevo proposals list
trevo proposals show <id>
trevo proposals approve <id>              # --acknowledge-overlap to run beside an overlap
trevo proposals dismiss <id> --reason too_risky
trevo proposals revise <id> "Keep the copy, make the CTA green" --wait
trevo generate                            # --replace retires pending drafts when the batch lands
trevo runs                                # what generation is doing

trevo experiments list --status running   # --search text, --limit n
trevo experiments show <id>
trevo experiments create --file experiment.json
trevo experiments start|pause|resume|end|ship <id> [<id> …]
trevo experiments decide <id> --ship treatment
trevo experiments status <id>

trevo funnels list
trevo funnels pull --file funnels.json
trevo funnels apply --file funnels.json

trevo whoami                              # which workspace this key opens, and who it acts as
trevo --version
```

Dismiss reasons, comma-separated for more than one: `not_relevant`, `too_risky`, `tried_before`,
`wrong_metric`, `does_not_match_code`, `not_feasible`, `wrong_area`.

`trevo proposals revise --wait` blocks until the revision lands, up to ten minutes, and exits
non-zero if it fails.

## Declare an experiment yourself

You do not have to wait for a proposal. `create` takes the same body the dashboard's custom-experiment
form sends:

```json theme={null}
{
  "name": "Two-step sample form",
  "mechanism": "Fewer fields per screen should raise completion.",
  "variants": [
    { "displayName": "control", "trafficSplit": 50 },
    { "displayName": "treatment", "trafficSplit": 50 }
  ],
  "conversionTargets": [{ "eventName": "sample_requested" }]
}
```

```bash theme={null}
trevo experiments create --file experiment.json --generate-pr
```

Splits must sum to 100, and the **first** conversion target is the primary metric — the others may
carry `"role": "guardrail"`. An event your site does not send yet is fine: Trevo Bot wires the
`track()` call in the pull request. The `Idempotency-Key` is derived from the body, so a re-run of the same step
within about ten minutes replays the first result instead of opening a second pull request. Pass
`--idempotency-key` to choose your own.

## Keep funnels in your repository

`pull` writes the definitions as a file to commit; `apply` reconciles a workspace to that file.
Funnels are matched **by name**, so renaming one in the file creates a new funnel rather than
renaming the old one.

```bash theme={null}
trevo funnels pull --file funnels.json     # or without --file to print to stdout
trevo funnels apply --file funnels.json --dry-run
trevo funnels apply --file funnels.json
```

`apply` creates what is missing and updates what changed, and is a no-op the second time it runs.
It never deletes unless you pass `--prune` — a partial file must not quietly remove the funnel a
running experiment is judged on.

```json theme={null}
[
  {
    "name": "Signup",
    "steps": [{ "eventName": "visit_pricing" }, { "eventName": "signup" }]
  }
]
```

Step order comes from the order in the file unless a step states its own `stepOrder`.

## Following the slow ones

`generate` and `approve` both answer before the work is done — generation is minutes of model work,
and a pull request means Trevo Bot cloning your repository and writing code. Each has a way to
follow it.

```bash theme={null}
trevo generate --wait                      # blocks until the batch lands; exits 1 if the run failed
trevo runs                                 # or check whenever you like
trevo runs show <run-id>
```

```bash theme={null}
trevo proposals approve <id> --wait        # blocks until the pull request is open
```

`approve --wait` exits `0` with the pull-request URL, and `1` the moment generation ends at
`pr_failed` — it does not sit out the timeout on a failure. Both waits are bounded (`--timeout`,
in seconds; 20 minutes by default for a pull request) and print what to run next if they give up.

Without `--wait` the same answers come from `trevo runs` and
`trevo experiments status <id>`.

## Wait for a status in CI

```bash theme={null}
trevo experiments status exp_123 --wait --until running --timeout 600
```

Exits `0` when the status arrives, `1` when the experiment settles somewhere it will not leave
(`pr_failed`, `rejected`, …) or the timeout passes — `--wait` always ends.

## Output and exit codes

Every command takes `--json` and prints the API response verbatim; without it you get a table or a
line of prose. Exit codes separate the cases a script has to tell apart:

| Code | Meaning |
| - | - |
| `0` | Fine |
| `1` | The API refused. The reason and its `code` are on stderr |
| `2` | Usage — an unknown command, a missing argument, no key configured |
| `3` | Auth — the key is unknown, revoked, or lacks the scope |

So a CI job can tell a revoked key from a proposal that was not approvable:

```bash theme={null}
if ! trevo proposals approve "$ID" --json > result.json; then
  case $? in
    3) echo "::error::TREVO_API_KEY is revoked or lacks proposals.review" ;;
    *) jq -r '.code // .error' result.json ;;
  esac
  exit 1
fi
```

## In a script

The client the CLI uses is exported, for scripts that would rather call the routes than parse
output:

```ts theme={null}
import { TrevoApi } from '@trevosdk/cli';

const api = new TrevoApi({ apiKey: process.env.TREVO_API_KEY! });
const { items } = await api.listExperiments({ status: 'proposed' });
```

Errors throw `ApiError` carrying `status` and the API's `code`, so a caller can branch on
`SURFACE_OVERLAP` rather than on prose.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.