CLI reference
Every sats command, flag, and setting. Run sats <command> --help for the
exact options in your installed version.
Commands
| Command | Does |
|---|---|
sats init [--words 12|24] [--restore] | Create a wallet, or restore one from a mnemonic backup |
sats balance [--offline] | Sync and show confirmed and pending balance |
sats receive | Reveal and save the next receive address |
sats send <address> <amount> | Prepare, confirm, sign, save, and broadcast a payment |
sats status [TXID] [--offline] | Show saved transactions and their chain status |
sats history [--offline] | List the wallet's transactions, newest first |
sats psbt inspect <FILE> | Decode a PSBT offline: outputs, fee, signing state |
sats psbt sign <FILE> [--out FILE] | Sign a PSBT file |
sats tx broadcast <FILE|TXID> | Broadcast a raw hex file or a saved transaction |
sats agent grant <name> | Give an agent a budget to propose sends |
sats agent mode <name> <ask|observe> | Switch a grant's mode |
sats agent allow <name> <address> | Add a recipient to a grant's allowlist |
sats agent disallow <name> <address> | Remove a recipient from a grant's allowlist |
sats agent list | List active grants and remaining budgets |
sats agent revoke <name> | Delete a grant immediately |
sats agent requests [--all|--watch] | List requests that need attention, or stream new ones |
sats agent approve [ID] [--yes] | Review, authorize, sign, and broadcast one request |
sats agent dismiss <ID> | Decline a request |
sats agent log [--limit N] [--request ID] | Show the agent event log |
sats agent serve <name> | Run the MCP server for an agent (MCP) |
sats providers [list] | Show the network's provider |
sats providers add <subfrost|esplora> | Add a provider, asking for its key, and use it |
sats providers use <mempool|subfrost|esplora> | Switch the network's provider |
sats providers remove <subfrost|esplora> | Remove a provider and its stored key |
Global options
| Option | Meaning |
|---|---|
--network <NET> | signet, testnet4, or regtest. Overrides the config. Mainnet is refused. |
--provider <KIND=URL> | Replace the network's provider for this run. See Providers. |
--json | Machine-readable output, where supported |
| Variable | Meaning |
|---|---|
SATS_DIR | Put all configuration and data under one directory. Useful for test wallets. |
Amounts are whole satoshis. 25000, 25k, and 1.5m (1,500,000) all
work. Suffixes are case-insensitive, and shorthand must resolve to a whole
sat.
Setting up
sats init # asks: create a new wallet, or restore one
sats init --words 24 # create, with a 24-word mnemonic
sats init --restore # restore from a mnemonic backup
sats --network testnet4 init # add another test network
init shows a new mnemonic once. Write it down before you continue. One seed
serves every network: on a machine that already has a seed, init with
another --network adds that network's wallet. If a wallet already exists,
use sats balance or sats receive. For a separate test wallet, point
SATS_DIR at an empty directory.
Restoring
sats init --restore reads the phrase with hidden input, never as an
argument. It validates the BIP-39 checksum before writing anything, names the
mistyped word when it can, seals the seed with a new password, and rescans the
chain on first sync.
- Restore needs an empty data directory.
- sats derives BIP-86 Taproot addresses only (
bc1p…,tb1p…). Coins on other address types won't appear, but they aren't lost. - Restore only a seed made for testing. sats refuses mainnet, and a seed it has held is a development key that must never hold mainnet funds.
- Grants, saved transactions, and agent requests are local records. They don't come back with the seed; on-chain history does.
- Restore needs a terminal for both the phrase and the new password.
Sending
sats send <address> <amount> [--fee-rate <SAT_VB>] [--allow-dust]
[--yes | --dry-run | --export-psbt <FILE>]
Every send, human or agent, takes the same path:
- Validate the address for the selected network.
- Sync the wallet. An empty wallet stops here with funding guidance.
- Exclude 546- and 330-sat outputs, which may carry inscriptions, unless
you pass
--allow-dust. If every output is excluded, stop and say so. - Estimate the fee for the configured target (2 blocks by default), unless
you pass
--fee-rate. - Build the unsigned PSBT in memory.
A sync failure stops the send. --allow-dust applies only to that one command
and is never available to agents.
| Mode | Behavior |
|---|---|
| default | Show amount and fee, ask to confirm, sign, save the transaction, broadcast |
--yes | Skip the confirmation. Password, safety checks, and grant rules still apply. |
--dry-run | Price the send and change nothing. No password needed. |
--export-psbt <FILE> | Write the unsigned PSBT to an owner-only file and sign nothing. The file's directory must exist, and sats leaves its permissions alone. The reserved change address is saved so the PSBT stays valid. |
If a broadcast fails after signing, the transaction is already saved.
sats status lists it, and sats tx broadcast <txid> retries it.
Status and history
sats status # saved transactions and their chain status
sats status <txid> # one transaction, by txid or unique prefix
sats history # every wallet transaction, newest first
Both sync first. If sync fails, they show cached data with a warning. Use
--offline to skip sync. status <txid> also finds transactions sats didn't
create, such as incoming payments. A transaction whose broadcast wasn't
confirmed shows as "signed; broadcast unconfirmed", alongside whatever the
chain reports for it. In JSON, sync_status is fresh, offline, or
failed.
PSBT workflow
A normal send never writes a PSBT. To work step by step:
sats send tb1p... 25k --export-psbt spend.psbt
sats psbt inspect spend.psbt
sats psbt sign spend.psbt
sats tx broadcast <txid>
psbt sign accepts base64 or binary PSBTs, including ones from other
wallets. If the wallet's signature completes the transaction, sats saves it
for sats tx broadcast and doesn't keep the signed PSBT. If other signers
are still needed, it writes <name>.signed.psbt beside the input and reports
the PSBT as partially signed. --out <FILE> always writes the signed PSBT to
FILE and saves nothing else.
tx broadcast reads an existing file as raw transaction hex. Otherwise it
looks up a saved transaction by txid or unique prefix and rebroadcasts the
saved bytes, without signing again.
Agent grants
sats agent grant <name> --budget <SATS> [--for <DURATION>] [--max-tx <SATS>]
[--max-fee <SATS>] [--mode ask|observe] [--to <ADDRESS>]...
| Option | Meaning |
|---|---|
--budget | Required. Total for amounts plus fees. |
--for | Lifetime, such as 30m, 24h, or 7d. Default 24h. |
--max-tx | Per-transaction amount cap. Anything above is refused outright. |
--max-fee | Per-transaction fee cap. Default: 2% of the budget, at least 1,000 sats, never above the budget. |
--mode | ask (default): every request waits for your approval. observe: read-only, and every request is refused. |
--to | Repeatable. Restricts recipients to this list; anyone else is refused. |
Agent names are 1–32 characters of lowercase letters, digits, -, or _.
Creating a grant requires your password, but the grant stores only limits and
a token hash, never key material.
Every grant limit is hard. Approval can't lift one; only changing the grant can. Within the limits, every request waits for you, however small. There is no autonomous mode.
The output includes a one-time token and two setup commands, one for Claude Code and one for ChatGPT desktop/Codex. See Connect an agent.
Changing a grant
sats agent mode claude observe # tighten: no password
sats agent mode claude ask # widen: password required
sats agent allow claude <address> # widen: password required
sats agent disallow claude <address> # tighten: no password
sats agent revoke claude
sats agent grant claude ... # re-issue: new limits and a new token
Reducing authority is free and widening it needs the password. Every mode
change is logged with its direction. Allowlist addresses are checked against
the grant's network. Removing the last entry means no recipient is allowed.
disallow is refused on a grant with no allowlist, because a list can't
express "everyone except one address". Re-issue the grant with --to
instead.
Revocation deletes the grant file, and a running MCP server notices on the agent's next request. Re-issuing replaces the token, so rotating a token and revoking it are the same act. Expired grants are removed when listed.
Reviewing agent requests
sats agent requests # requests that need you
sats agent requests --all # every request, newest first
sats agent requests --watch # stay running; print each new pending request
sats agent log # event log, oldest first
sats agent log --request r-2e7d # one request, by id or unique prefix
requests shows id, agent, recipient, amount, status, and age. Listing also
settles any request a crashed approval left in signing.
--watch is the trusted way to learn about requests: it reads the local
store, not anything the agent says. Each line ends with the exact approve
command. Denied requests never appear there, because nothing is waiting on
you; --all shows them. With --json the output is JSONL. Press Ctrl-C to
stop.
log shows one line per event: received, denied, approved, dismissed,
reserved, signed, broadcast, refunded, or failed. It reads local files only,
and skips unreadable lines with a warning.
Approving a request
sats agent approve # pick from a list in your terminal
sats agent approve <id> [--yes] # approve a specific id or unique prefix
sats agent dismiss <id>
Without an id, sats shows a numbered list of pending and failed requests.
Press Enter or r to refresh, w to wait a second and refresh, or q to
cancel. Choosing a request opens its review and approves nothing. sats
re-checks that exact request, so a reordered list can't redirect your choice.
Requests that may already be signed appear with recovery steps but can't be
chosen. Without a terminal, pass an id.
To approve, sats:
- prepares the transaction on fresh chain state;
- checks that it pays exactly the recipient and amount the agent requested;
- re-checks the grant with the real fee;
- shows wallet, network, full recipient, amount, fee, total, and remaining budget;
- asks you to confirm, then asks for your password;
- reserves the budget, signs, saves the transaction, and broadcasts.
--yes skips the confirmation, but only with an explicit id, and never skips
the password. With --json, the review and prompts go to stderr and the
result goes to stdout. A cancelled selection returns status: cancelled.
A request outside its grant's current limits, or whose grant was revoked,
expired, or re-issued, is refused before the password prompt and recorded as
denied. The only way forward is a new request under a new grant. Approval
has no --fee-rate. If fee estimation fails, fix the configured provider.
dismiss declines a pending, failed, or unresolved request. It needs no
password and never refunds an unresolved request's budget. Both commands
accept an id or unique prefix.
When an approval doesn't finish
| Status | What happened | What to do |
|---|---|---|
failed | Stopped before signing, for example a sync or fee error. Nothing was spent. | Fix the cause, then approve again, or dismiss. |
broadcast_pending | Signed and saved, but the broadcast wasn't confirmed. | sats tx broadcast <txid> |
unresolved | Something failed after signing started, so a signature may exist. sats will never sign it again. | Check sats status, then dismiss. |
signing | An approval was interrupted. | Run sats agent requests. sats settles it from the saved records. |
The rules behind these states are in
Security. If a broadcast succeeded but
its receipt wasn't recorded, sats agent requests or
sats tx broadcast <txid> repairs it without broadcasting, signing, or
spending budget again.
Alkanes
sats alkanes is an experimental, signet-first client for Alkanes contracts.
Default builds don't include it. Build it with
cargo install --locked --path crates/sats-cli --features experimental-alkanes.
It needs Subfrost as the network's provider (sats providers add subfrost),
and there is no fallback.
sats alkanes inspect 2:1 # bytecode size and sha256 code hash
sats alkanes simulate 2:1 77 # advisory call simulation
inspect prints the code hash so you can compare it with a build you trust.
simulate shows the recognized fields (status, gas, asset transfers) next to
the raw result. It is display only, never authorization. Both commands are
read-only and check the endpoint's network first. inspect reads bytecode
through the indexer's getbytecode view, checked against Subfrost's signet
endpoint; simulate's request format hasn't been verified against a live
endpoint yet. Alkanes execution is never in released builds.
JSON output
--json is supported by balance, receive, send (all modes), status,
history, psbt inspect, psbt sign, tx broadcast, every agent
subcommand except serve, every providers subcommand, and both alkanes
subcommands in builds that include them.
Field names are a compatibility surface. Branch on the documented status
and reason fields, not on messages. agent grant --json includes token,
the only time it is ever shown, so treat that output as a secret. init has
no JSON mode, so the mnemonic never lands on a machine-readable stream.
Configuration
| Platform | Config file | Data |
|---|---|---|
| Linux | ~/.config/sats/config.toml | ~/.local/share/sats/ |
| macOS | ~/Library/Application Support/sats/config.toml | same directory |
SATS_DIR replaces both with one directory.
network = "signet" # default network
[signet]
fee_target = 1008 # confirmation target in blocks (1–1008, default 2)
Each network's provider lives under [<network>] too, written by
sats providers or by hand. See Providers. sats writes
config.toml owner-only, because it can hold provider API keys.
Exit behavior
Failures print to stderr and exit non-zero. balance, status, and
history are the deliberate exceptions: when sync fails they show cached
data with a warning and mark the JSON result as not synced. Planning, signing,
provider validation, and broadcast failures are always hard failures. So is
a provider choice the network can't serve, for any command that needs the
network.
Local commands and --offline reads never resolve providers, so they keep
working while a provider choice can't be served. Malformed configuration
always fails at startup. Approval checks the request and its grant before
resolving providers, so a settled or revoked request is refused even without
a working provider.