> ## Documentation index
> Fetch the complete documentation index at: https://docs.predictefy.com/llms.txt
> Use it to discover every available page before exploring further.

# CLI

> The predictefy terminal client — every command, the output contract, and the trading gates.

`@predictefy/cli` installs the `predictefy` binary, the terminal and AI-agent client for the
Predictefy API. It is a thin wrapper over the [TypeScript SDK](/guides/sdk/): one command shape
for every venue, with `--venue <slug>` choosing the lane. Reads and normalization stay
server-side; signing stays on your machine.

:::note[Beta release]
The package ships on the `1.0.0-beta` line. Pin an exact version while the beta line moves.
:::

## Install & authentication

From this workspace:

```bash
pnpm install
pnpm --filter @predictefy/cli... build
pnpm --filter @predictefy/cli exec predictefy --help
```

Or install the binary globally:

```bash
npm install --global @predictefy/cli
predictefy --help
```

Node `>=20.19 <21 || >=22.12` is required.

Authenticate through the environment:

```bash
export PREDICTEFY_API_KEY='pk_live_…'
predictefy whoami
```

Or through a config file:

```bash
predictefy config init   # writes a commented ~/.predictefy/config.toml
predictefy config show   # prints it with the API key redacted
```

`PREDICTEFY_HOME` relocates the config directory: the CLI then reads
`$PREDICTEFY_HOME/config.toml`. Environment authentication takes precedence over the file.

API keys are never accepted as command-line arguments. Argv is readable by other processes and
lands in shell history, so the key comes from the environment or the config file only. The same
rule covers the signing key and every [venue credential](#venue-credentials).

`status` is public and keyless. Every other remote command uses the configured key.

## Global flags

| Flag             | Default  | What it does                                                  |
| ---------------- | -------- | ------------------------------------------------------------- |
| `--json`         | off      | Writes the API response bytes unchanged.                      |
| `--venue <slug>` | `router` | Selects the venue. There are no per-venue command namespaces. |

`--json` does not rewrap, rename, or pretty-print fields, so it is offered only on commands
backed by exactly one API request. The machine contract is the API envelope itself:

```json
{ "success": true, "data": [] }
```

An API error envelope is written unchanged and exits 1:

```json
{ "success": false, "error": { "code": "NOT_SUPPORTED", "message": "…", "retryable": false } }
```

Exit codes:

- `0` — success.
- `1` — API, transport, config, or doctor-check failure.
- `2` — command usage error.

Errors go to stderr, except a `--json` envelope, which goes to stdout.

Composite and local commands reject `--json` because there is no single envelope to pass through
honestly: `venues`, `doctor`, `config`, `skill`, and `watch`. Two surfaces stream instead:
`webhooks listen` and `watch --raw` emit NDJSON, one JSON object per line.

`predictefy <command> --help` lists the exact options a command accepts.

## Command reference

Venue-aware commands take `--venue <slug>`; the default `router` is the cross-venue aggregate.
List verbs paginate with `--limit` plus either `--offset` or `--cursor`, depending on which
pagination the route serves.

### Platform

Service checks, identity, capability summaries, and local setup.

```bash
predictefy status                        # public service snapshot, no key required
predictefy whoami                        # prove the configured key works
predictefy venues                        # every venue with its honest capability summary
predictefy venues metrics --venue polymarket
predictefy doctor                        # sequenced connectivity, key, and catalog checks
predictefy config init
predictefy config show
predictefy skill install --agents        # write the predictefy-cli agent skill
```

The API does not expose identity, scopes, plan, or credits, so `whoami` says so and uses an
authenticated capability request as its proof. `skill install` also accepts `--claude`,
`--read-only`, and `--dry-run`.

### Markets

Search, read, and page the normalized market catalog.

```bash
predictefy markets search "central bank" --venue router --limit 5
predictefy markets get 0xabc --venue polymarket
predictefy markets list --venue kalshi --status active --sort liquidity --limit 20
predictefy markets page --venue router --limit 100
predictefy markets categories
predictefy markets tags --category politics
predictefy markets has --venue kalshi
predictefy markets orderbooks <outcomeId> <outcomeId>
predictefy markets execution-price <outcomeId> --side buy --size 250
predictefy markets execution-price <outcomeId> --side buy --size 250 --detailed
predictefy markets filter '<criteriaJson>' --markets '<marketsJson>'
```

`markets search` accepts `--search-in title|description|both` and
`--mode lexical|semantic|hybrid`; semantic and hybrid stay subject to server capability flags.
`markets page` is the cursor-paginated form, which freezes the catalog snapshot from page one.
`markets has` prints one venue's capability map.

`markets execution-price` is a stateless VWAP calculation over the venue's order book: it
estimates what a fill of that size would average. It places, routes, and prepares nothing. `--side`
and `--size` are required, and `--detailed` returns the per-level breakdown. `markets filter` is a
pure stateless filter over a market list: the positional argument is the criteria JSON and the
required `--markets` is the JSON array to filter. That route is feature-flagged, so a deployment
without it returns the server's honest error.

### Events

Event-shaped listings, event metadata, and series.

```bash
predictefy events --venue router --status active --sort volume --limit 20
predictefy events get <eventId> --venue polymarket
predictefy events page --venue router --limit 100
predictefy events metadata <eventId> --venue kalshi
predictefy events series --venue kalshi
predictefy events filter '<criteriaJson>' --events '<eventsJson>'
```

`events --sort` accepts `volume`, `newest`, `liquidity`, or `closeDate`. `events filter` mirrors
`markets filter`: criteria as the positional argument, the required `--events` array as the input.

### Market data

Live books, the public trades tape, and candles.

```bash
predictefy orderbook <marketId> --venue polymarket --limit 20
predictefy trades <marketId> --venue polymarket --limit 20
predictefy candles <marketId> --venue polymarket --timeframe 1h --limit 100
```

These three verbs are outcome-keyed. The positional argument keeps the `<marketId>` spelling, but
the value must be the venue's outcome or CLOB token identifier. A venue without a public trades
tape returns the server's honest `NOT_SUPPORTED` error rather than an empty list.

### Cross-venue intelligence

Matched clusters, matched pairs, indicative price discrepancies, and the router's executable
assessment.

```bash
predictefy clusters --sort similarity --limit 20
predictefy clusters list --has-discrepancy --limit 20
predictefy clusters get <clusterId>
predictefy clusters markets --limit 20
predictefy clusters events --limit 20
predictefy discrepancies --live --limit 20
predictefy discrepancies qualify <clusterId> --size 100
predictefy arbitrage --venue router --contracts 100 --executable-only --limit 20
predictefy matches list --market-id polymarket:0xabc --limit 20
predictefy matches browse --category politics --limit 20
predictefy matches markets --min-difference 0.05 --sort priceDifference --limit 20
predictefy matches prices --min-similarity 0.9 --limit 20
predictefy matches compare --market-id polymarket:0xabc --live --limit 20
predictefy matches hedges --market-id polymarket:0xabc --limit 20
predictefy matches related --slug <marketSlug> --limit 20
predictefy matches events --event-id polymarket:<eventId> --limit 20
```

Cross-venue price gaps are **indicative price discrepancies** — observed mid-price gaps, not
executable opportunities. `discrepancies qualify` runs the server's qualification checks against
one cluster at a size, and `arbitrage` applies the server's live-ask, open-market, depth,
fee/gas, and resolution-equivalence gates. The CLI neither weakens nor recreates those gates; it
prints what the server decided, including the refusal reasons.

`matches compare`, `matches hedges`, and `matches related` are anchored on one market with
`--market-id` (the canonical `{venue}:{marketId}` spelling) or `--slug`, and `matches events` takes
an `--event-id` anchor instead. `--sort` accepts `priceDifference` or `similarity`. Hedge and
related rows are candidates, not instructions.

### Trader Intelligence

Wallet-attributed tapes, holders, leaderboards, and scored trades.

```bash
predictefy traders leaderboard --window all --limit 20
predictefy traders leaderboard --venue polymarket --window month --limit 20
predictefy traders wallet <address> --venue polymarket
predictefy traders smart-money --venue kalshi --limit 20
predictefy traders market-trades <marketId> --venue polymarket --limit 50
predictefy traders holders <marketId> --venue polymarket --limit 50
predictefy traders wallet-trades <address> --venue polymarket --limit 50
```

Trader identity is capability-qualified per venue. Without `--venue`, `traders leaderboard`
serves the cross-venue score ranking. Unsupported venue and verb combinations return the
server's honest error; scores are informational signals, not advice.

### Portfolio

```bash
predictefy portfolio <address> --venues polymarket,limitless,opinion
```

One public address across the venues you name.

### Accounts

Hosted account reads. Every verb addresses one account on one venue, so all six require an
explicit `--venue`: the `router` default is a catalog aggregate, never an account.

```bash
predictefy accounts capabilities --venue hyperliquid
predictefy accounts snapshot <accountId> --venue hyperliquid
predictefy accounts balances <accountId> --venue hyperliquid --limit 50
predictefy accounts positions <accountId> --venue hyperliquid --limit 50
predictefy accounts open-orders <accountId> --venue hyperliquid --limit 50
predictefy accounts fills <accountId> --venue hyperliquid --cursor <cursor>
```

Start with `accounts capabilities`: it reports which of these resources the venue actually
serves. See [Accounts & funding](/guides/accounts/) for the per-venue account model.

### Funding & bridge

Funding prerequisites, ordered funding steps, and cross-chain bridge sessions.

```bash
predictefy funding requirements --venue polymarket
predictefy funding steps --venue polymarket --owner 0xabc --target-amount 250
predictefy funding transfer-plan --from-venue polymarket --to-venue hyperliquid \
  --amount 250 --owner 0xabc
predictefy bridge quote --from-chain 8453 --from-token USDC --from-amount 250000000 \
  --from-address 0xabc --to-venue hyperliquid
predictefy bridge session create --from-chain 8453 --from-token USDC \
  --from-amount 250000000 --from-address 0xabc --to-venue hyperliquid \
  --payment-currency <caip19>
predictefy bridge session get <sessionId>
predictefy bridge session payment <sessionId> --tx-hash 0xdef
predictefy bridge status --tx-hash 0xdef --from-chain 8453 --to-chain 42161
```

`funding requirements` and `funding steps` address one venue and require an explicit `--venue`;
`funding transfer-plan` names both ends itself with `--from-venue` and `--to-venue`. `bridge quote`
also accepts `--to-chain`, `--to-token`, `--to-address`, and `--slippage`.

Every artifact these commands return is **unsigned**. The CLI prints it; you sign and broadcast
it yourself, then report the payment hash back with `bridge session payment`. Step bodies are
venue-shaped, so `funding steps` takes repeatable `--field key=value` values that the server
stays authoritative over — additive fields only, under the same rule as
[trade `--field`](#trading-gates).

### Data feeds

Reference price feeds, separate from the prediction-market venues.

```bash
predictefy feeds list
predictefy feeds markets <feed>
predictefy feeds ticker <feed> <symbol>
predictefy feeds tickers <feed> --symbols <symbol>,<symbol>
predictefy feeds candles <feed> <symbol> --timeframe 1h --limit 100
predictefy feeds orderbook <feed> --symbol <symbol>
predictefy feeds oracle-round <feed> <pair>
predictefy feeds oracle-history <feed> <pair> --limit 20
predictefy feeds history <feed> <symbol> --from <epochSeconds> --until <epochSeconds> --order asc
```

A feed is addressed by feed id and symbol, not by `--venue`. `feeds candles` also takes `--since`
as an epoch-ms lower bound, and `feeds history` takes `--max-size` plus `--order asc|desc`.

`feeds orderbook` is a permanent capability gap, kept visible rather than hidden: reference feeds
publish prices, not depth, so the route always returns a typed `NOT_SUPPORTED` error. Use
`predictefy orderbook` for prediction-market depth.

### Billing

```bash
predictefy billing checkout --pack <id>
predictefy billing subscribe --plan <id>
predictefy billing portal
```

Each command prints a hosted Stripe URL for you to open. The CLI never collects card or payment
data. See [Credits & billing](/guides/credits/) for packs, plans, and metering.

### SQL & mappings

```bash
predictefy sql "select venue, count(*) from markets group by venue"
predictefy mappings polymarket:0xabc kalshi:KXFED-26MAR-T4.00
```

`sql` requires an API key with the `sql` scope. A key without that scope gets an authorization
error from the server — the scope is not granted by default and cannot be set from the CLI.
`mappings` resolves `venue:marketId` pairs to their cross-venue identity.

### Webhooks

```bash
predictefy webhooks create --url https://hooks.example.com/predictefy \
  --events ingest.run.completed,execution.status.changed
predictefy webhooks list
predictefy webhooks delete <id>
predictefy webhooks listen --endpoint <id>
predictefy webhooks listen --endpoint <id> --forward http://localhost:3000/hooks
```

`webhooks listen` polls deliveries until `SIGINT`. Without `--endpoint` it creates a temporary
endpoint, subscribes, polls, and deletes the endpoint on exit; a failed cleanup prints the
endpoint id. Forwarding accepts only `localhost`, `127.0.0.1`, or `::1`, and POSTs the payload
with an `X-Predictefy-Event` header. `--interval <seconds>` changes the poll interval, which
defaults to 2. With `--json`, each delivery is one NDJSON line rather than an API envelope.

### Trading

```bash
predictefy trade buy <marketId> <outcome> <size> --venue hyperliquid --price 0.62
predictefy trade sell <marketId> <outcome> <size> --venue hyperliquid --price 0.62 --preview
predictefy trade submit <executionId> --venue hyperliquid --side buy --artifact-digest <digest> --yes
predictefy trade cancel <executionId> --venue hyperliquid
predictefy trade modify <executionId> --venue hyperliquid --price 0.64
predictefy trade status <executionId> --venue hyperliquid
predictefy trade status <executionId> --venue hyperliquid --refresh
```

Every `trade` verb requires an explicit `--venue`: the `router` default is a catalog aggregate,
never an execution lane. `--price` is required on `buy` and `sell` and lies in `(0, 1]`.
`trade modify` needs at least one of `--price`, `--size`, or `--field`. `trade status --refresh`
re-reads status from the venue, using the venue credentials described in
[Venue credentials](#venue-credentials) — never command arguments.

`--tif` accepts `Alo`, `Ioc`, or `Gtc` and applies to `hyperliquid` only, which is the one
builder that takes the field. It has no default: omit it and each venue applies its own. Passing
it for another venue is refused locally, before any network call, because most builders reject
the key outright and `pascal` rejects the value — its time-in-force vocabulary is uppercase
`GTC | GTT | IOC`, which this flag deliberately does not translate. Translating would submit an
order you did not describe.

`trade cancel` and `trade modify` sign locally, and only Hyperliquid's cancel/modify artifact is
signable by the local EVM key today. Both verbs refuse another venue by name before signing; use
the REST API or the SDK for those.

**`polymarket_us` is a capability-qualified exception.** `trade buy` and `trade sell` refuse it
by name. The CLI derives one generic order body — `outcome`, `outcomeSide`, `isBuy`, `price`,
`size`, `owner` — and the `polymarket_us` builder takes a different vocabulary entirely
(`marketId`, `outcomeId`, `side`, `type`, `amount`), so every field would be rejected. Use the
REST API or the SDK for `polymarket_us` orders. Its other verbs are unaffected.

These verbs are gated. Read [Trading gates](#trading-gates) before running any of them, and
[Trading & execution](/guides/trading/) for the full non-custodial model.

### Execution lifecycle

Reads over the isolated execution service: the armed lane registry and your recorded orders,
trades, positions, and balance.

```bash
predictefy exec venues
predictefy exec orders --venue hyperliquid --status open --limit 50
predictefy exec trades --venue hyperliquid --limit 50
predictefy exec positions --venue hyperliquid
predictefy exec balance --venue hyperliquid
```

`exec venues` is the registry the trading gates below consult: one row per venue the execution
service arms, with a flag per verb and the gating that applies. The table prints the `build`,
`submit`, `cancel`, `modify`, and `redeem` flags plus the geo-restriction gate; `--json` returns
the row verbatim. Run it first when a trade is refused. `exec orders --status` accepts `open`,
`closed`, or `all` and defaults to `open`. Every `exec` verb except `venues` addresses one venue
and requires an explicit `--venue`.

These verbs read; they never sign. They therefore take exactly one requirement —
`PREDICTEFY_EXEC_BASE_URL`, because there is no other origin to read from — and neither the
`[trading]` opt-in nor a signing key. `trade status` follows the same rule, so no read is gated
more tightly than its neighbour.

### Streaming

```bash
predictefy watch orderbook <marketId> --venue polymarket
predictefy watch trades <marketId> --venue polymarket
predictefy watch price <marketId> --venue rain --market-address 0xabc
predictefy watch feed-ticker <feed> <symbol>
predictefy watch arbitrage
```

See [Streaming behavior](#streaming-behavior) for the socket lifecycle, `--raw`, and
entitlements.

## Trading gates

Trading is deliberately gated by three independent controls. None of them substitutes for
another, and each one covers a different set of verbs.

1. `[trading] enabled = true` in `~/.predictefy/config.toml` — the local opt-in. Required by
   `trade buy`, `trade sell`, `trade submit`, `trade cancel`, and `trade modify`.
2. `PREDICTEFY_EXEC_BASE_URL` — the isolated execution origin. It has no default, so the reads
   API can never become an execution proxy. Required by every command that talks to the
   execution service, reads included: all of `exec *` and `trade status`.
3. `PREDICTEFY_EVM_PRIVATE_KEY` — required by `trade buy`, `trade sell`, `trade submit`,
   `trade cancel`, and `trade modify`, **including an unsigned preview**. A preview is built _for_ your account, so
   the CLI reads that key to derive its address and puts the address in the build body. Nothing
   is signed and nothing is sent. `exec *` and `trade status` never read the key at all.

```toml
[trading]
enabled = true
```

```bash
export PREDICTEFY_EXEC_BASE_URL='https://execution.example.com'
export PREDICTEFY_EVM_PRIVATE_KEY='0x…'
```

The signing key is environment-only. It is never accepted in argv or `config.toml`, never
printed, and never sent to Predictefy. Every byte the CLI writes — human output, `--json`
envelopes, warnings, and error text — passes through one redaction seam that removes the API
key, the signing key, and any configured venue credential, matching case-insensitively and with
or without a `0x` prefix. Signing happens locally, in-process.

**Venue support is not a hardcoded CLI list.** `trade` queries `GET /v1/exec/venues` — the
server's lane registry — at runtime, and allows any venue whose row arms the requested verb. When
a venue or verb is not armed, the CLI refuses and names the venues that are armed, reading both
answers out of the registry rather than out of a list baked into the client. A preview needs
`build`; a confirmed persisted execution needs `submit`. Before signing, the CLI also honors the
gating the same row publishes: a geo-restricted lane, or a lane armed without artifact bounds,
is refused before a signature exists rather than after the venue rejects the submit.

**Signing routes on your `--venue`, and the envelope is pinned.** Structural validity is not
authorization: a token `Permit`, a Permit2 batch and a marketplace listing are all valid EIP-712
typed data, so an execution service that returned one instead of an order could otherwise have
it signed. Two rules prevent that:

- The signing path is chosen by the venue **you** typed, never by the shape or the self-declared
  venue of the artifact. An artifact stamped for a different venue than `--venue` is refused by
  name.
- Before signing, the envelope must match that venue's pinned exchange domain — EIP-712 domain
  name, version, `chainId`, and a `verifyingContract` from the venue's known-exchange allowlist —
  the exact struct of its deployed exchange (field names, types, count and order, not just the
  struct name), and your own intent: the struct's signer field must be your signing address, and
  its side must be the buy or sell you asked for. Where the struct routes value or names a
  counterparty (`beneficiary` on XO, `taker` on Opinion, PredictStreet, and PRED), that field must
  be the zero address or your own account. Any mismatch is refused naming the field that failed,
  and no signature bytes are produced.

Hyperliquid's native L1 path is pinned the same way. The signer canonicalizes the visible action,
recomputes its official msgpack `connectionId` with the nonce and expiry, and requires the network
source to match `Mainnet` or `Testnet`. It also requires the real `Exchange` domain (chainId 1337,
zero `verifyingContract`) and refuses any action outside `order`, `cancel`, `cancelByCloid`, and
`modify`. A different digest or fund-moving action is refused before signing.

A venue with no pinned envelope and no native path — one authenticating with venue-issued API
credentials, or signing with a non-EVM key — is refused with a message naming what _is_
supported. The CLI never signs an artifact it cannot account for.

**Always preview first.** The default builds and prints the unsigned artifact plus its notional
and fee bounds. It signs nothing and submits nothing:

```bash
predictefy trade buy <marketId> <outcome> <size> --venue hyperliquid --price 0.62
```

The preview prints a canonical artifact digest and the exact second-step command. After a human
confirms that artifact, submit the persisted execution rather than rebuilding it:

```bash
predictefy trade submit <executionId> --venue hyperliquid --side buy \
  --artifact-digest <digest-from-preview> --yes
```

`trade submit` reloads the named execution, recomputes its digest, and refuses before signing when
it differs from `--artifact-digest`. Under `--json`, the preview's confirmation command and the
confirmed artifact go to **stderr**, so stdout remains one clean API envelope. The older one-shot
`trade buy|sell --yes` path no longer exists.

**Read that artifact — it is your verification surface for the values the pins cannot cover.**
The order amounts (`makerAmount`/`takerAmount`) and the outcome `tokenId` are _not_ pinned. Amounts
are each venue's own tick- and decimal-scaled derivation of price × size, and the token is resolved
from your market through the venue's catalog, so the CLI cannot re-derive either without
duplicating every venue's rounding rules — which would refuse legitimate orders. The honest
consequence: an execution origin that has been compromised can put different values in the
preview, so check its amounts and token. Once its digest is confirmed, however, the origin cannot
swap those values before signing: any change produces a different digest and is refused.

Additive venue-shaped build fields go through repeatable `--field key=value` values, which the
server — not the CLI — is authoritative over. The per-venue build schemas in
[Trading & execution](/guides/trading/) name the fields each venue expects:

```bash
predictefy trade buy <marketId> <outcome> <size> --venue polymarket \
  --field <key>=<value> --field <key>=<value>
```

A field value parses as JSON when it can, so numbers, booleans, and objects survive; anything else
stays a literal string. `trade modify` takes the same escape hatch for venue-shaped modify bodies,
and `funding steps` takes it for venue-shaped step bodies.

`--field` is additive only. It cannot set `venue`, `owner`, `signer`, `funder`, `isBuy`, `side`,
`price`, `size`, `outcome`, `outcomeSide`, `asset`, `targetAmount`, `dryRun`, or
`idempotencyKey`: those come from the command's own arguments, `--venue`, its dedicated flags,
and the signing key, and the safety gates already ran against them. Setting one would move the
request to a different venue, account, side, price, or size than the one you typed — past the
gate that checked it. Passing one is a usage error (exit 2) before any network call.

## Venue credentials

Some execution lanes require credentials the venue issued to _you_ — an API key, secret, and
passphrase, or an access/refresh token pair. Predictefy never holds them, so the CLI forwards
them from your environment on the calls that need them: `trade submit` and `trade status --refresh`.

They are accepted from the environment only, never from command arguments, where they would land
in the process list, shell history, and CI logs. One variable per field:

```bash
export PREDICTEFY_VENUE_CRED_XO_API_KEY='…'
export PREDICTEFY_VENUE_CRED_XO_API_SECRET='…'
export PREDICTEFY_VENUE_CRED_XO_API_PASSPHRASE='…'
```

The name is `PREDICTEFY_VENUE_CRED_<VENUE>_<FIELD>`. `<VENUE>` is the venue slug upper-cased with
separators removed, so `polymarket_us` becomes `POLYMARKETUS` and can never be confused with
`POLYMARKET`. `<FIELD>` is the request field in `SCREAMING_SNAKE_CASE`, so `ACCESS_TOKEN` becomes
`accessToken`. Any value set this way is redacted from CLI output like the other secrets.

## Streaming behavior

`watch` opens the hosted WebSocket, prints frames until you press Ctrl-C, then unsubscribes and
closes the socket cleanly.

```bash
predictefy watch orderbook <marketId> --venue polymarket
predictefy watch orderbook <marketId> --venue polymarket --raw
```

`--raw` emits each frame as one line of JSON (NDJSON) instead of the human summary, which is the
form to pipe into another process. `watch` itself rejects `--json`: a stream has no single
envelope.

`watch orderbook`, `watch trades`, and `watch price` each address one market on one venue, so they
require an explicit `--venue`; `watch price` also requires `--market-address`, the on-chain market
contract address.

`watch arbitrage` requires a plan entitling the `arbitrage` feature. The entitlement is
re-checked while the socket is open, so a denial — or a mid-stream revocation — arrives as a
`PLAN_UPGRADE_REQUIRED` error frame on a socket that stays open. The CLI prints those error
frames to stderr rather than exiting silently, so a revoked stream never looks like a quiet
market. A venue that does not serve a subscription answers `NOT_SUPPORTED` the same way, on
every verb — order books, trades, prices, and feed tickers included.

Ctrl-C is the only clean ending. If the socket closes on its own — a server restart, an auth
rejection, a dropped connection — the CLI prints the close code and reason to stderr and exits
non-zero, so a dead stream is never mistaken for a quiet one.

The hosted stream requires an API key, so `watch` fails fast with a named error when none is
configured, before opening a socket the server would immediately close.

See [Streaming](/guides/streaming/) for venue coverage and the
[WebSocket API](/reference/streaming/) for the frame shapes.
