Skip to content
Docs / CLI reference

CLI reference

The canonopy command covers the whole workflow from a terminal: build a model from the JSON you send Jev (canonopy build), review how it decides (optional), add past cases, retrain, read reports and advice, decide, and manage versions and keys. It's optional: every command is one plain HTTP call to the API, and coding agents can use the hosted MCP connector with nothing installed.

Install

bash
pip install https://canonopylabs.com/dl/canonopy_cli-0.6.2-py3-none-any.whl

The canonopy-cli package needs Python 3.9 or later and nothing else. pipx install works with the same URL. Set your key from the console:

bash
export CANONOPY_API_KEY=cnp_…
--keyoption
Your API key. Default: $CANONOPY_API_KEY.
--base-urloption
The API. Default: $CANONOPY_BASE_URL, or https://api.canonopylabs.com.
--jsonoption
Print the API's JSON instead of a summary.

Commands

In the order you'd usually use them. A decision model is called a domain in the API.

bash
canonopy build jev-request.json --name refunds --rules "Never approve when amount is over 500."   # your own model from a Jev request
canonopy build status refunds                                       # where the build is (or: a build id)
canonopy build jev-request.json --interview                         # a few questions about your JSON first
canonopy build questions BUILD_ID                                   # the questions (or: a model's name)
canonopy build answer BUILD_ID                                      # answer them on the terminal (or --file answers.json)
canonopy playtest my-bot@latest --cmd "python my_game.py" --episodes 20   # your game plays with the model, locally
canonopy shadow start refunds                                       # shadow mode: beside your current decisions
canonopy shadow send refunds requests.jsonl                         # real requests with your current decision
canonopy shadow review refunds                                      # the disagreements (--pick-model, --pick-current, --skip)
canonopy spot-checks refunds                                        # this week's optional card (--set off|light|thorough)
canonopy init refunds --describe "We handle refund requests. Actions: approve, escalate, decline. …"
canonopy describe refunds "Card vs payment: these go to cards"     # the set-up conversation (- reads stdin)
canonopy examples refunds                                           # optional: how it decides on about 20 cases
canonopy signoff refunds --correct ex_03:action=escalate --retrain  # optional: correct any, retrain with them
canonopy upload refunds past_cases.csv                              # CSV with a header row, JSONL or JSON
canonopy upload refunds better_cases.csv --replace                  # these replace every uploaded case
canonopy uploads refunds                                            # each upload: id, date, file, cases
canonopy uploads delete refunds UPLOAD_ID                           # one upload's cases, deleted permanently
canonopy data clear --confirm refunds                               # every uploaded case (--signoff, --outcomes)
canonopy train refunds                                              # --no-wait, --promote auto|always|never
canonopy advice refunds                                             # what would improve it most
canonopy report refunds                                             # or: canonopy report refunds 3
canonopy decide refunds@latest --state '{"message": "refund please", "amount": 40}'
canonopy options refunds action store-credit --description "offer store credit" --when "tier is pro"
canonopy outcomes DECISION_ID --action approve                      # what really happened
canonopy outcomes --unsure refunds                                  # the unsure queue
canonopy outcomes DECISION_ID --review refunds --answer action=escalate
canonopy progress refunds                                           # what's waiting for the next retrain, and every version
canonopy versions refunds
canonopy promote refunds 3                                          # what refunds@latest serves
canonopy download refunds@latest -o refunds.zip                     # to run yourself
canonopy fallback refunds --provider typesafe --key-env TYPESAFE_API_KEY
canonopy keys [list | create NAME | revoke KEY_ID]
canonopy decisions refunds --route review --since 2026-09-01       # the decision log (--json for JSON)
canonopy decisions show refunds DECISION_ID                        # one decision in full
canonopy decisions export refunds --format csv > decisions.csv     # or --format jsonl, -o FILE
canonopy decisions delete refunds DECISION_ID                      # or --before 2026-06-01 --confirm refunds
canonopy decisions wrong DECISION_ID --answer escalate              # mark a decision wrong (--note "…")
canonopy retention [30_days | 90_days | 365_days | until_deleted]   # how long decisions are kept
canonopy sync                                                       # cases a downloaded model saved offline
canonopy sync --status                                              # where they are, and how many wait
canonopy mcp                                                        # the local MCP server
canonopy skill install                                              # the agent skill for Claude Code (--project, --dir, --force)

--state and --questions take JSON, plain text, or @file.json. Errors print the API's message and exit with status 1.

canonopy sync sends what a downloaded model saved while it couldn't reach us (it needs the canonopy-runtime package installed alongside; --dir picks another folder). Each case leaves the local file once it's stored; the held ones join your unsure queue.

canonopy decisions filters with --since, --until, --source, --route, --answer action:refund (repeatable), --version, --has-outcome or --no-outcome, and --search (words in the case's text, or a decision id); --limit and --cursor page through. Export takes the same filters. See The decision log.

upload --replace, uploads delete and data clear delete cases permanently; examples you corrected and outcomes stay unless data clear gets --signoff or --outcomes, and versions already trained are kept. Train again afterwards for the next version of the same model. See Replace or remove cases.

Build from a Jev request

For models made from the JSON you send Jev (Start with no data), in canonopy-cli 0.4.0 and later:

bash
canonopy build jev-request.json                                     # the body you send Jev, as is
canonopy build jev-request.json --name refunds --description "Refund requests" \
  --rules "Never approve when amount is over 500. Always escalate when fraud_flag is true."
canonopy build jev-request.json --name refunds --examples more-states.jsonl \
  --field "order.amount=the refund asked for, in USD (0-5,000)"     # 5-20 more real states, notes on fields
canonopy build jev-request.json --name refunds --history past_refunds.csv --wait
canonopy build status refunds                                       # the latest build of a model, or a build id
canonopy examples refunds                                           # optional, once it's ready: how it decides
canonopy signoff refunds --correct ex_03:action=escalate --retrain  # optional: correct any, retrain with them
canonopy rules found refunds                                        # rules found in your history
canonopy rules found refunds --confirm fr_3 --hard fr_1 --reject fr_2
canonopy history flagged refunds --status pending                   # past cases that contradict your rules
canonopy history flagged refunds --follow-rule h1042 --keep h1180 --drop h2210
canonopy history flagged refunds --all follow_rule                  # or keep, or drop
canonopy advice refunds                                             # what would improve it most
canonopy train refunds                                              # a targeted retrain: prints the build id
canonopy rules change refunds "Always escalate when amount is over 300."   # retrains now; --replace for all your rules
canonopy decisions wrong DECISION_ID --answer escalate              # or --answer action=escalate, --note "…"
build FILEcommand
FILE holds {"state", "questions"}, and optionally examples, fields, rules and name (a pasted Jev request works: model is ignored). --examples FILE (5-20 more real states, like state: one per line, or a JSON list; up to 50; no past decisions or answers needed, a few examples of what you send Jev make it better), --fields FILE (notes on fields: a JSON object keyed by path or name) and --field "path=note" (repeatable), --rules "…" or --rules-file F (plain words), --name, --description, --history FILE (past cases: CSV, JSONL or JSON), --wait (until the model is ready, needs attention or failed; nothing waits for you in between), --json.
build … --interviewcommand
The build asks a few questions about your JSON first and waits for your answers up to --answer-timeout seconds (default 600), then goes on with what it has. With --wait, it returns when the questions are ready.
build questionscommand
A build id or a model's name (its latest build): the questions, most useful first, and what each answer holds. --round 2 for the follow-up.
build answercommand
Asks each question on the terminal (Enter skips it), or sends --file answers.json ({"answers": [{"id": "q01", "text": "…"}]}). --more: more answers follow, the build keeps waiting.
build statuscommand
A build id (bld_…) or a model's name: where the build is, what it read, the quality check, and what to do next.
rules foundcommand
Lists the rules found in your history with their support, agreement and what confirming each changes. --confirm, --reject and --hard take rule ids (fr_1 …); --hard confirms a rule and enforces it on every decision, like your own rules. A build id works in place of the model's name.
history flaggedcommand
Lists flagged past cases (--status pending or reviewed, --limit, --cursor). --follow-rule, --keep and --drop take case ids; --all decides for every pending case.
rules changecommand
New rules in plain words (or - to read them from stdin), added to yours (or --replace). Your model is retrained with them straight away, and the build says what changed; the new version serves only if it passes the quality check. --wait waits for the build to finish. Its examples (the changed situations) are optional to review with canonopy examples.
decisions wrongcommand
The right answer for a decision: --answer escalate (the decision question) or --answer question=answer (once per question), and --note. canonopy decisions wrong MODEL DECISION_ID works too. The next retrain learns it.
advicecommand
What would improve the model most, in plain words.
examplescommand
Optional, any time: how your model decides on about 20 situations, with the answer for each and why.
signoffcommand
Optional: --correct EXAMPLE_ID:question=value for each example you correct (repeatable), and --retrain to retrain with them now. Without --retrain, they apply at the next retrain. --all-ok also marks every other example right.

On a model made with canonopy build, canonopy train starts a targeted retrain and prints its build id; canonopy build status shows the before and after. See Rules found in your history and Improve your model.

Try it on real work

bash
canonopy playtest my-bot@latest --cmd "python my_game.py" --episodes 20 [--once] [--keep-situations 200]
canonopy shadow start|stop|status refunds
canonopy shadow send refunds requests.jsonl
canonopy shadow review refunds [--pick-model ID … --pick-current ID … --skip ID …]
canonopy spot-checks refunds  |  canonopy spot-checks --set thorough
playtestcommand
Serves the model (a local .zip, or name@version downloaded from your workspace) at a local endpoint on 127.0.0.1, runs --cmd once per episode (or once for all with --once), reads one JSON line of numbers per episode, and sends only those numbers (--no-send to keep them). Needs canonopy-runtime. See Playtest your model.
shadowcommand
start, stop, status; send MODEL FILE (JSON lines of {"state", "current", "source", "confident"}); review lists the disagreements, and --pick-model, --pick-current, --skip record your review. See Shadow mode.
spot-checkscommand
This week's optional card for a model, or --set off|light|thorough for the workspace.

The local MCP server

canonopy mcp runs an MCP server on stdio so coding agents can use every step above, plus a docs tool. It reads your key from CANONOPY_API_KEY and never prints it.

bash
claude mcp add canonopy --env CANONOPY_API_KEY=cnp_… -- canonopy mcp

Most agents don't need it: the hosted MCP connector has the same tools with nothing installed. Use the local server to upload files straight from disk, save downloaded models to disk, or read a day-one backend's key from an environment variable. Setup for Cursor, Codex and other clients: Use with coding agents.

The agent skill

canonopy skill install copies the Canonopy Decisions skill to Claude Code's personal skills folder, ~/.claude/skills/canonopy-decisions/, and prints where it went. --project installs it in the current project's .claude/skills/ instead (or --project DIR), and --dir PATH in any agent's skills folder (as PATH/canonopy-decisions/). It never replaces an installed copy unless you add --force. canonopy skill show prints SKILL.md.

The runtime CLI

The runtime that runs a downloaded model offline has a command line of its own, in the canonopy-runtime package:

bash
pip install "canonopy-runtime[text] @ https://canonopylabs.com/dl/canonopy_runtime-0.6.3-py3-none-any.whl"
python -m canonopy_runtime bank-support@3.zip request.json
canonopy-runtime sync            # send the cases it saved while offline (reads CANONOPY_API_KEY)
canonopy-runtime queue           # where they are, and how many wait
canonopy-runtime playtest my-bot@3.zip --cmd "python my_game.py" --episodes 20   # your game plays with it; prints the results
canonopy-runtime reader list     # the text readers downloaded to this machine, and their size
canonopy-runtime reader remove   # delete them to free the space (fetched again when a model needs one)

request.json is a /v1/decide body. The answer is printed in the same format as the hosted endpoint. sync and queue take --dir FOLDER (default: CANONOPY_OFFLINE_DIR or your user data folder); sync exits with status 1 when it couldn't send everything (the cases stay in the file). See Running it yourself.

Or with curl

bash
export CANONOPY_API_KEY=cnp_…
API=https://api.canonopylabs.com

curl -s $API/v1/domains -H "Authorization: Bearer $CANONOPY_API_KEY"                                  # list domains
curl -s $API/v1/domains/refunds/data -H "Authorization: Bearer $CANONOPY_API_KEY" -F file=@cases.csv  # upload
curl -s -X POST $API/v1/domains/refunds/train -H "Authorization: Bearer $CANONOPY_API_KEY"             # train
curl -s $API/v1/domains/refunds/versions -H "Authorization: Bearer $CANONOPY_API_KEY"                 # versions