Status: Implemented (schema_version = "1.0") as of 2026-02-26
Scope: Native CLI command contract for both human users and AI agents.
This document defines the active v1 command contract. It is additive with current CLI JSON outputs so existing integrations can migrate safely.
- One wallet engine and one command model for both humans and agents.
- Stable machine-readable responses in
--agentmode. - Strict, typed error taxonomy for automation reliability.
- Backward-compatible rollout from current
SCHEMAS.mdbehavior.
- Human profile
- Default output mode (
ZINC_CLI_OUTPUT=human). - Output is a curated, styled, user-friendly presentation.
- Agent profile
- CLI called with
--agentorZINC_CLI_OUTPUT=agent. - Exactly one JSON object on
stdoutper invocation. - Non-JSON noise must not be printed to
stdout.
--agent, --yes, --password, --password-env, --password-stdin, --reveal, --data-dir, --profile, --network, --scheme, --esplora-url, --ord-url, --ascii, --no-images, --thumb, --no-thumb, --correlation-id, --log-json, --idempotency-key, --network-timeout-secs, --network-retries, --policy-mode
Global flags are supported both before and after command tokens.
Password defaults:
- If
--password-envis omitted, the CLI reads password fromZINC_WALLET_PASSWORD.
Reliability defaults:
--network-timeout-secsdefaults to30.--network-retriesdefaults to0.--policy-modedefaults towarn.
Human-output defaults:
- Thumbnails are enabled by default in human mode.
--no-thumbor--no-imagesdisables thumbnails.- In
--agentmode thumbnails are disabled by default unless--thumbis set.
{
"ok": true,
"schema_version": "1.0",
"correlation_id": "zinc-...",
"command": "wallet info",
"meta": {
"started_at_unix_ms": 1710000000000,
"duration_ms": 42
},
"...": "command-specific fields"
}{
"ok": false,
"schema_version": "1.0",
"correlation_id": "zinc-...",
"command": "wallet info",
"meta": {
"started_at_unix_ms": 1710000000000,
"duration_ms": 42
},
"error": {
"type": "invalid|config|auth|network|insufficient_funds|policy|not_found|internal",
"message": "human-readable",
"exit_code": 1
}
}Notes:
schema_versionandcommandare target-required for v1 implementation.correlation_idis stable for one invocation and should be used by agents for tracing.- Clients must ignore unknown fields for forward compatibility.
Idempotency for mutating commands:
- When
--idempotency-key <key>is provided on a mutating command, successful results are cached. - Repeating the same mutating command payload with the same key replays the cached success result.
- Reusing the same key for a different mutating payload returns
error.type=invalid. - Mutating success payloads include additive field:
idempotency: { key: string, replayed: bool, recorded_at_unix_ms: u128 }
| error.type | exit_code | Meaning |
|---|---|---|
invalid |
2 | invalid command, flags, or values |
config |
10 | local profile/config/storage issue |
auth |
11 | password/decryption/auth issue |
network |
12 | remote API/network/connectivity issue |
insufficient_funds |
13 | balance insufficient for requested spend/fees |
policy |
14 | ordinals/policy/security guard blocked operation |
not_found |
15 | requested profile/resource/tx not found |
internal |
1 | unexpected internal error |
| Type | Values / Shape |
|---|---|
Network |
bitcoin | signet | testnet | regtest |
Scheme |
unified | dual |
SatsBreakdown |
{ immature: u64, trusted_pending: u64, untrusted_pending: u64, confirmed: u64 } |
BalanceResponse |
{ total: SatsBreakdown, spendable: SatsBreakdown, inscribed_sats: u64 } |
TxItem |
{ txid: string, amount_sats: i64, fee_sats: u64, confirmation_time: u64 | null, tx_type: "send" | "receive", inscriptions: InscriptionDetails[], parent_txids: string[], index: usize } |
InscriptionDetails |
{ id: string, number: i64, content_type: string | null } |
Account |
{ index: u32, label: string, taprootAddress: string, taprootPublicKey: string, paymentAddress: string | null, paymentPublicKey: string | null } |
u64 and i64 are JSON numbers in v1.
All commands below describe --agent response payloads.
Command:
wallet init [--words 12|24] [--network ...] [--scheme ...] [--esplora-url <url>] [--ord-url <url>] [--overwrite]
Success fields:
profile, version, network, scheme, account_index, esplora_url, ord_url, bitcoin_cli, bitcoin_cli_args, phrase
Notes:
phraseis redacted by default and only shows the real mnemonic when--revealis set.wordsis emitted only when--revealis set.
Command:
wallet import --mnemonic <phrase> [--network ...] [--scheme ...] [--esplora-url <url>] [--ord-url <url>] [--overwrite]
Success fields:
profile, network, scheme, account_index, imported
Optional fields:
phrase (only when --reveal is set)
Command:
wallet info
Success fields:
profile, version, network, scheme, account_index, esplora_url, ord_url, bitcoin_cli, bitcoin_cli_args, has_persistence, has_inscriptions, updated_at_unix
Command:
sync chain
Success fields:
events as string[]
Command:
sync ordinals
Success fields:
inscriptions as u64 count
Command:
address taproot [--index N] [--new]
Success fields:
type, address
Where type = "taproot".
Command:
address payment [--index N] [--new]
Success fields:
type, address
Where type = "payment".
Command:
balance
Success fields:
total, spendable, inscribed_sats
Command:
tx list [--limit N]
Success fields:
transactions as TxItem[]
Command:
psbt create --to <addr> --amount-sats <n> --fee-rate <n> [--out-file <path>]
Success fields:
psbt
Command:
psbt analyze [--psbt <base64> | --psbt-file <path> | --psbt-stdin]
Success fields:
analysis
Expected analysis object:
warning_level, inscriptions_burned, inscription_destinations, fee_sats, warnings, inputs, outputs
Additive policy fields:
safe_to_send, inscription_risk, policy_reasons, policy
policy shape:
{ safe_to_send: bool, inscription_risk: "none" | "low" | "medium" | "high" | "unknown", reasons: string[] }
Command:
psbt sign [--psbt <base64> | --psbt-file <path> | --psbt-stdin] [--sign-inputs 0,1] [--sighash N] [--finalize] [--out-file <path>]
Success fields:
psbt
Additive policy fields:
safe_to_send, inscription_risk, policy_reasons, analysis
Command:
psbt broadcast [--psbt <base64> | --psbt-file <path> | --psbt-stdin]
Success fields:
txid
Additive policy fields:
safe_to_send, inscription_risk, policy_reasons, analysis
Command:
account list [--count N]
Success fields:
accounts as Account[]
Command:
account use --index N
Success fields:
previous_account_index, account_index, taproot_address, payment_address
Command:
wait tx-confirmed --txid <id> [--timeout-secs N] [--poll-secs N]
Success fields:
txid, confirmation_time
Additive fields:
confirmed, waited_secs
Command:
wait balance --confirmed-at-least <n> [--timeout-secs N] [--poll-secs N]
Success fields:
confirmed
Additive fields:
confirmed_balance, target, waited_secs
Command:
snapshot save --name <name> [--overwrite]
Success fields:
snapshot
Command:
snapshot restore --name <name>
Success fields:
restored
Command:
snapshot list
Success fields:
snapshots as string[]
Command:
lock info
Success fields:
profile, lock_path, locked, owner_pid, created_at_unix, age_secs
Command:
lock clear
Success fields:
profile, lock_path, cleared
Command:
scenario mine [--blocks N] [--address <addr>]
Success fields:
blocks, address, raw_output
Command:
scenario fund [--amount-btc <decimal>] [--address <addr>] [--mine-blocks N]
Success fields:
address, amount_btc, txid, mine_blocks, mine_address, generated_blocks
Command:
scenario reset [--remove-profile] [--remove-snapshots]
Success fields:
removed
Command:
doctor
Success fields:
healthy, esplora_url, esplora_reachable, ord_url, ord_reachable, ord_indexing_height, ord_error
Command:
offer create --inscription <id> --amount <u64> --fee-rate <u64> [--expires-in-secs <u64>] [--created-at-unix <unix>] [--nonce <u64>] [--seller-payout-address <addr>] [--publisher-pubkey-hex <xonly-hex>] [--submit-ord] [--offer-out-file <path>] [--psbt-out-file <path>]
Notes:
--amounthas alias--ask-sats.--ord-urlmust be configured or provided as a global override.--seller-payout-addressoverrides payout destination output while preserving seller input metadata from ord inscription output.
Success fields:
inscription, ask_sats, fee_rate_sat_vb, seller_address, seller_outpoint, seller_pubkey_hex, expires_at_unix, thumbnail_lines?, hide_inscription_ids, raw_response
Command:
offer publish [--offer-json <json> | --offer-file <path> | --offer-stdin] --secret-key-hex <hex> --relay <url>... [--created-at-unix <unix>] [--timeout-ms N]
Success fields:
event_id, accepted_relays, total_relays, publish_results, raw_response
Command:
offer discover --relay <url>... [--limit N] [--timeout-ms N]
Success fields:
event_count, offer_count, offers, thumbnail_lines?, hide_inscription_ids, raw_response
Command:
offer submit-ord [--psbt <base64> | --psbt-file <path> | --psbt-stdin]
Success fields:
ord_url, submitted, raw_response
Command:
offer list-ord
Success fields:
ord_url, count, offers, raw_response
Command:
offer accept [--offer-json <json> | --offer-file <path> | --offer-stdin] [--expect-inscription <id>] [--expect-ask-sats <u64>] [--dry-run]
Success fields:
inscription, ask_sats, txid, dry_run, inscription_risk, thumbnail_lines?, hide_inscription_ids, raw_response
For psbt analyze, psbt sign, psbt broadcast, and offer submit-ord exactly one PSBT input source must be present:
--psbt <base64>--psbt-file <path>--psbt-stdin
If zero or multiple are provided, return invalid.
--password-stdin must not be combined with --psbt-stdin in the same invocation.
For offer publish and offer accept, exactly one offer source must be present:
--offer-json <json>--offer-file <path>--offer-stdin
If zero or multiple are provided, return invalid.
For offer publish and offer discover, at least one --relay <url> is required.
offer create requires --ord-url and a resolvable inscription on that ord indexer.
When --policy-mode strict is set, psbt sign, psbt broadcast, and offer accept fail closed with error.type="policy" for unsafe, medium/high, or unknown inscription-risk outcomes.
- v1 changes must be additive by default.
- Existing field names must not be renamed in v1.
- Any planned removal requires:
- deprecation notice in docs
- one minor cycle with compatibility output
- explicit migration note
- Agents should always use
--agent. - Agents should prefer password env variables over plaintext flags.
- Policy/ordinals failures must be surfaced as
error.type = "policy"with actionable messages. - Commands that mutate wallet state should remain explicit and single-purpose.
- Agents should set
--idempotency-keyon mutating commands and tune--network-timeout-secs/--network-retriesfor reliability.
When --log-json is provided, the CLI emits JSON lines to stderr with:
event:command_start|command_finish|command_errorcorrelation_idcommandts_unix_ms
These logs are additive and do not affect stdout contract shape.
- Every JSON response includes
ok. - Every error response includes
error.type,error.message,error.exit_code. - Each command returns only documented required fields plus optional additive fields.
- Unknown command/flag/value paths map to
invalidwith exit code2. - Machine contract tests enforce envelope and representative command/error shape invariants.