Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ qn agent context
qn agent context -o json
```

**Test:** `qn agent context` with `QN_API_KEY` empty — 370 lines of Markdown titled `# qn — usage guide for agents`, with 9 `##` sections. It succeeds with no key, which is how you confirm it needs no authentication

## Output Formats

Set output with `--format` or `-o`.
Expand All @@ -62,14 +64,32 @@ Set output with `--format` or `-o`.
| `md` | Markdown tables for issues and docs |
| `toon` | Compact LLM-oriented structured output |

Examples:
With `--format` unset the CLI reads `[output] format` from the config file, then defaults to `table` on a TTY and `json` when stdout is piped. `--wide`/`-w` affects only `table` and `md`.

**The piped default is not universal.** `qn rpc list-networks` prints a bare
newline-separated list of slugs even when piped, with no JSON. A script that
pipes it and parses JSON fails. Pass `-o json` explicitly for that command.

```bash
qn endpoint list --format json
qn usage summary --from 7d -o yaml
qn endpoint list --wide
```

**Test:** `qn endpoint list | head -c 1`, `qn chain list | head -c 1`, and `qn kv set list | head -c 1` all print `{`; `qn rpc list-networks | head -c 1` prints `0`, the first character of `0g-galileo`. The piped default holds for three of these four commands and not for the fourth

## Exit Codes

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | Usage error — bad flag, missing argument, unparseable value |
| `2` | API or request error — the server rejected the call (also plan-gated features, and drawdown credit exhaustion) |
| `3` | Indeterminate outcome on a paid call — the request was sent but the response was lost; the wallet may already be charged |
| `4` | No credentials — no `--api-key`, no `--config-file`, no `~/.config/qn/config.toml` |
| `5` | Gated command refused — a destructive command ran in a non-TTY without `--yes`, before any request was sent |
| `130` | Interrupted (SIGINT) |

## Command Groups

### Auth
Expand Down Expand Up @@ -106,24 +126,33 @@ qn auth login
qn rpc call eth_getBlockByNumber '["latest", false]' --network base-mainnet
```

With `-o json` the CLI prints the JSON-RPC `result` **unwrapped**. There is no
`jsonrpc`, `id`, or `result` field to read past.

**Test:** `base-mainnet` · block `latest` — `-o json` prints the block object itself at the top level, keyed `baseFeePerGas` through `withdrawalsRoot`, with no `jsonrpc`, `id`, or `result` wrapper; `transactions` holds hashes, not objects, because the second parameter is `false`

Params: positional (JSON array), by name (JSON object), from a file, or from stdin.

```bash
qn rpc call eth_blockNumber
qn rpc call eth_getBalance '["0xabc...", "latest"]'
qn rpc call eth_getBalance '["0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8", "latest"]'
qn rpc call getSlot --network solana-mainnet
qn rpc call eth_call --params-file params.json
echo '[...]' | qn rpc call eth_call -
echo '[{"to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "data": "0x18160ddd"}, "latest"]' | qn rpc call eth_call -
cat params.json | qn rpc call eth_call -f -
```

**Test:** `ethereum-mainnet` · block `latest` — `eth_getBalance` returns a hex quantity, and the piped `eth_call` returns USDC `totalSupply()` as one 32-byte hex word

`--network <KEY>` selects a network on the multichain endpoint (e.g. `base-mainnet`, `solana-mainnet`, `polygon`, `btc`). Omit it for the default network. `qn rpc list-networks` (alias `ls`) lists available keys — no RPC call made.

```bash
qn rpc list-networks
qn rpc list-networks -o json
```

**Test:** `qn rpc list-networks` — one network key per line, sorted, `0g-galileo` first and `zora-mainnet` last (132 keys on 2026-09-04; the count grows, so do not gate on it). With `-o json` the payload is `{"networks": [...]}`. It is **not** the `data` envelope the other list commands use, so `.data` is `undefined`

`--endpoint-url <URL>` sends the call to a self-authenticating URL instead of the Tooling Access endpoint, overriding `[rpc] endpoint_url` in `~/.config/qn/config.toml`. Mutually exclusive with `--network`.

First-run `qn rpc call` prompts `[y/N]` to enable Tooling Access if it isn't provisioned yet. Pass `-y`/`--yes` to auto-confirm non-interactively.
Expand All @@ -132,6 +161,8 @@ First-run `qn rpc call` prompts `[y/N]` to enable Tooling Access if it isn't pro
qn rpc call eth_blockNumber --network base-mainnet -y
```

**Test:** `base-mainnet` · block `latest` — returns a bare JSON string such as `"0x308471d"`, not an object. `-y` exits `0` and prompts nothing when Tooling Access is already enabled, so it is safe to pass unconditionally in a script

### Paid RPC (x402 and MPP)

Requires CLI v0.6.0+. `qn rpc call` can pay for a call with stablecoins through the x402 or MPP gateway instead of using the account's API key. This lane requires no Quicknode account, API key, login, Tooling Access, or provisioned endpoint — only a funded local wallet.
Expand Down Expand Up @@ -174,7 +205,9 @@ qn rpc mpp supported-networks
qn rpc mpp supported-payments
```

Use a slug from `supported-networks` as `--network`. Use a row from `supported-payments` — it returns the payment network, asset symbol, and token address — as `--payment-network` and `--payment-asset`. Query these instead of hardcoding a network or asset list.
Use a slug from `supported-networks` as `--network`. Use a row from `supported-payments` as `--payment-network` and `--payment-asset`. Query these instead of hardcoding a network or asset list.

Rows carry `network` and `address`; `asset` is optional. `network` is a slug (`base-sepolia`) or a raw CAIP-2 id (`eip155:1952`). `--payment-asset` accepts a symbol or a contract address/mint.

The query network and the payment network are independent. `--network ethereum-mainnet` with `--payment-network base-sepolia` pays on Base Sepolia for an Ethereum Mainnet read.

Expand Down Expand Up @@ -327,6 +360,8 @@ qn endpoint log-details <endpoint-id> <request-id>
qn endpoint metrics <endpoint-id> --metric method_calls_over_time --period day
```

On plans that do not support logging, they exit `2` reporting `unauthorized. Check your API key`; `-v` shows the details.

Commands that create, update, pause, resume, archive, enable or disable multichain, or change endpoint security/rate limits mutate endpoint state.

```bash
Expand Down Expand Up @@ -439,6 +474,14 @@ qn stream test-filter \
--filter-file filter.js
```

**Test:** `ethereum-mainnet` · block `17811625` — `result` is a JSON string holding the filter's return value, and `logs` is `[]`

`test-filter` prints `{ "result": ..., "logs": [] }`, where `result` is the filter's return value as a JSON-encoded string, not an object.

A filter that calls `console.log` makes the command fail with `Error: unexpected response shape from API.` and exit `1`. The request itself succeeds — the API returns HTTP `201` with each log entry as a `{ "level", "message" }` object, which the client decodes as a string and rejects, so `logs` only ever renders as `[]`. Filters run through the CLI or the SDK must not call `console.log`.

The filter file exports `main(stream)`. For the `block` dataset `stream.data` is an array of blocks, one per batch entry.

Create and lifecycle commands change Stream state and may prompt:

```bash
Expand Down Expand Up @@ -469,9 +512,11 @@ qn webhook create \
--url https://example.com/webhook \
--compression none \
--template evm-wallet \
--wallet 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
--wallet 0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8
```

Pass `--wallet` once per address. `--wallets-list-name <name>` is the alternative: it reads the addresses from a saved Key-Value Store list instead.

Webhook lifecycle and update commands:

```bash
Expand All @@ -496,7 +541,7 @@ qn kv set delete mykey
qn kv set bulk --add threshold=750000 --delete old_threshold
```

Lists store ordered values under one key:
Lists store multiple values under one key. `qn kv list get` returns items sorted lexicographically, not in insertion order:

```bash
qn kv list list
Expand All @@ -509,6 +554,11 @@ qn kv list update allowlist --add 0x456 --remove 0xabc
qn kv list delete allowlist
```

Every `delete` needs `--yes` when no terminal is attached. Without it the command
exits non-zero and deletes nothing.

**Test:** `qn kv set list` — `data` is an **array** of `{key, value}`; `qn kv list list` — `data` is an **object** with a `keys` array. The two commands do not share a response shape

### SQL Explorer

```bash
Expand All @@ -519,6 +569,8 @@ qn sql query --file query.sql --cluster-id hyperliquid-core-mainnet
qn sql query --file - --cluster-id hyperliquid-core-mainnet
```

`qn sql query` costs API credits on every call; `qn sql schema` is free.

### Account Operations

```bash
Expand All @@ -536,7 +588,16 @@ qn billing payments
qn completions zsh
```

`qn chain credits <chain>` returns the per-method API credit costs for that chain, using the same chain slugs returned by `qn chain list`. Use it when an agent needs account-aware credit costs instead of hardcoding method multipliers.
`qn chain credits <chain>` returns the per-method API credit costs for that chain. Use it when an agent needs account-aware credit costs.

The CLI uses three chain vocabularies:

| Where | Vocabulary | Examples |
|-------|-----------|----------|
| `qn chain list` | short slugs | `eth`, `matic`, `sol`, `btc` |
| `qn chain credits <chain>` | accepts either the short slug or the long name | `eth` or `ethereum`, `sol` or `solana` |
| `qn endpoint create --chain` | long names | `ethereum`, `solana` |
| `qn rpc call --network` / `qn rpc list-networks` | network keys | `ethereum-mainnet`, `base-mainnet`, `solana-mainnet`, `polygon`, `btc` |

## Confirmation Behavior

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ if (!status.enabled) {

// params defaults to []; pass an array (positional) or object (by name).
const blockNumber = await qn.rpc.call("eth_blockNumber");
const balance = await qn.rpc.call("eth_getBalance", ["0xabc...", "latest"]);
const balance = await qn.rpc.call("eth_getBalance", [
"0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8",
"latest",
]);

// Multichain: seed the network map from admin.getEndpointUrls, then select
// a network by its multichain_urls key as the 3rd argument.
Expand Down Expand Up @@ -124,6 +127,8 @@ try {
// await qn.admin.disableToolingAccess();
```

**Test:** `ethereum-mainnet` · block `latest` — `eth_getBalance` on that address returns a hex quantity; passing the truncated placeholder `0xabc...` instead throws `RpcError` with `code === -32602`

### Account Info and API Credits

```typescript
Expand All @@ -140,7 +145,9 @@ for (const row of credits.data.slice(0, 5)) {
}
```

`accountInfo()` calls the Admin API account-info endpoint and returns details such as account id, name, creation timestamp, billing version, and current subscription. `getApiCredits(chain)` calls the per-chain API credits endpoint and returns `{ method, credits }` rows for the supplied chain slug. Use `listChains()` to discover valid chain slugs.
`accountInfo()` calls the Admin API account-info endpoint and returns details such as account id, name, creation timestamp, billing version, and current subscription. `getApiCredits(chain)` calls the per-chain API credits endpoint and returns `{ method, credits }` rows for the supplied chain.

`listChains()` returns short slugs (`eth`, `matic`, `sol`). `getApiCredits` accepts either the slug or the long name. The `rpc` client uses a third vocabulary, network keys such as `ethereum-mainnet`.

### Streams

Expand All @@ -165,9 +172,21 @@ const test = await qn.streams.testFilter({
filterFunction,
});

console.log({ streams: streams.pageInfo.total, enabled: enabled.total, result: test.result });
// test.result is a JSON-encoded string.
const filtered = JSON.parse(test.result);

console.log({
streams: streams.pageInfo.total,
enabled: enabled.total,
filtered,
logs: test.logs,
});
```

**Test:** `ethereum-mainnet` · block `17811625` — `typeof test.result === "string"` and `test.logs` is `[]`; a filter calling `console.log` throws `DecodeError` instead

`testFilter` returns `{ result: string, logs: string[] }`, where `result` is the filter's output serialized as JSON. A filter that calls `console.log` makes `testFilter` throw `DecodeError`: the API returns each log entry as an object and the client decodes `logs` as strings, so `logs` only ever resolves to `[]`.

### Webhooks

```typescript
Expand Down Expand Up @@ -199,6 +218,8 @@ if (sets.data.length > 0) {
console.log({ sets: sets.data.length, lists: lists.data.keys.length });
```

`getSets()` returns `{ data: [{ key, value }], cursor }` with `data` as an array; `getLists()` returns `{ data: { keys: [...] }, cursor }` with `data` as an object wrapping `keys`. `getSet(key)` returns `{ value }` as a string. List items come back sorted lexicographically, not in insertion order.

### SQL Explorer

```typescript
Expand All @@ -214,9 +235,12 @@ const result = await qn.sql.query(
const schema = await qn.sql.getSchema("hyperliquid-core-mainnet");

console.log(`${result.rows} rows, ${result.credits} credits`);
console.log(schema);
console.log(result.data); // the rows themselves
console.log(schema.tables.map((t) => t.name));
```

`query` returns `{ meta, data, rows, rowsBeforeLimitAtLeast, statistics, credits }`. `rows` is a count; the row objects are in `data`, keyed by the selected columns. `rowsBeforeLimitAtLeast` is the match count before `LIMIT`. `query` costs API credits on every call; `getSchema` is free.

## RPC Micropayments

Node 3.8.0+, other languages 0.8.0+. The `rpc` client can pay for calls with stablecoins through the x402 or MPP gateway instead of an API key. Set a payment wallet and the SDK handles the HTTP `402 Payment Required` challenge, signs the payment, and resends the request. This lane needs no Quicknode account, API key, or provisioned endpoint — construct the SDK without an API key.
Expand Down Expand Up @@ -287,12 +311,14 @@ quicknode-sdk = { version = "0.8", features = ["payments", "payments-svm", "paym
```typescript
import { generatePaymentWallet } from "@quicknode/sdk";

const wallet = generatePaymentWallet("evm"); // "evm" or "svm"
const wallet = generatePaymentWallet("evm"); // "evm" | "svm" | "tempo"
console.log("Fund this address:", wallet.address);
// Persist wallet.key now. It cannot be recovered later.
```

Use `evm` for x402 on EVM and for MPP on Tempo; use `svm` for x402 on Solana. Other bindings expose it as `generate_payment_wallet`.
**Test:** offline, no network — `evm` and `tempo` return a 42-character `0x` address, `svm` a base58 address; any other value throws `ConfigError` naming all three

`chain` is typed `"evm" | "svm" | "tempo"`. Use `evm` for x402 on EVM, `svm` for x402 on Solana, and `evm` or `tempo` for MPP on Tempo — both return a secp256k1 hex key. Other bindings expose it as `generate_payment_wallet`.

### x402 Credit Drawdown

Expand Down Expand Up @@ -349,6 +375,22 @@ The gateway has no read-only channel endpoint, so it cannot reconstruct a lost l
| `PaymentRejectedError` | The gateway refused the payment; nothing settled |
| `PaymentIndeterminateError` | Outcome unknown — the payment may have settled. Check the wallet before retrying; never blind-retry a paid call |

## Errors

Every SDK call throws from one typed hierarchy rooted at `QuicknodeError`. All of these are named exports.

| Error | Extra fields | Raised when |
|-------|--------------|-------------|
| `QuicknodeError` | — | Base class for every SDK error |
| `ConfigError` | — | Bad or missing configuration — an unknown network key before `setNetworks`, an unknown payment chain |
| `HttpError` | — | Transport failure |
| `TimeoutError` | — | Extends `HttpError`. Request exceeded `QN_SDK__HTTP__TIMEOUT_SECS` |
| `ConnectionError` | — | Extends `HttpError`. Could not reach the host |
| `ApiError` | `status`, `body` | The API returned a non-2xx response |
| `DecodeError` | `body` | The response could not be decoded into the expected shape |
| `RpcError` | `code` | The JSON-RPC response carried an `error` member |
| `PaymentError` | — | Base class for the payment lane (see the table above) |

## Other Languages

Python, Rust, and Ruby expose the same product-client model through language-native method names:
Expand Down