# Send Bitkit Cli to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "bitkit-cli",
    "name": "Bitkit Cli",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "canonicalUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/bitkit-cli",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=bitkit-cli",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "CONTRIBUTING.md",
      "README.md",
      "SKILL.md",
      "install.sh",
      "rustfmt.toml"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "bitkit-cli",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T13:38:40.455Z",
      "expiresAt": "2026-05-07T13:38:40.455Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=bitkit-cli",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=bitkit-cli",
        "contentDisposition": "attachment; filename=\"bitkit-cli-0.2.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "bitkit-cli"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/bitkit-cli"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/bitkit-cli",
    "downloadUrl": "https://openagent3.xyz/downloads/bitkit-cli",
    "agentUrl": "https://openagent3.xyz/skills/bitkit-cli/agent",
    "manifestUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.md"
  }
}
```
## Documentation

### bitkit-cli -- AI Agent Skill

Bitcoin Lightning payment CLI for agents. Lowest LSP fees. Self-custody wallet with LNURL/Lightning Address support, typed exit codes, JSON envelope output, encrypted Pubky messaging, and daemon mode.

Install: curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh
Binary names: bitkit or bk (identical alias)
Always use: --json flag on every invocation for parseable output.

### JSON Envelope

All --json output uses a consistent envelope:

Success (stdout):

{ "ok": true, "data": { ... } }

Error (stderr):

{ "ok": false, "error": "message", "code": 1 }

Parse with: jq -r '.data.field' for success data, check .ok first.

### Quick Start

# 1. Create wallet (no encryption for agent use)
bk init --no-password --json

# 2. Start daemon for instant command execution
bk start --json

# 3. Get on-chain address and fund it
ADDRESS=$(bk address --json | jq -r '.data.address')

# 4. Order inbound Lightning liquidity via LSP
ORDER=$(bk lsp create-order 500000 --json)
ORDER_ID=$(echo "$ORDER" | jq -r '.data.order_id')

# 5. Pay the order (on-chain to payment_address), then open channel
bk lsp open-channel "$ORDER_ID" --listen 9735 --json

# 6. Create an invoice and receive payment
bk invoice 5000 --description "agent service" --wait --listen 9735 --json

# 7. Pay someone else's invoice
bk pay lnbc50u1p... --json

# 8. Check balance and history
bk balance --json
bk history --json

# 9. Stop daemon when done
bk stop --json

### Daemon Mode

By default each command cold-starts the LDK node (slow). Start a persistent daemon for instant execution:

bk start --json           # start daemon (default port 3457)
bk status --json          # check if running
bk balance --json         # instant -- proxied through daemon
bk stop --json            # stop daemon

When the daemon is running, all commands automatically proxy through its HTTP API. When stopped, commands fall back to per-command cold-start. No code changes needed.

### start

Start the background daemon. Idempotent -- returns current PID if already running.

bk start --json
bk start --port 8080 --json

{
  "ok": true,
  "data": {
    "status": "started",
    "pid": 12345,
    "port": 3457
  }
}

ArgDefaultDescription--port <port>3457HTTP API port

status is "started" or "already_running".

### stop

Stop the running daemon.

bk stop --json

{
  "ok": true,
  "data": {
    "status": "stopped",
    "pid": 12345
  }
}

Errors if no daemon is running (exit code 1).

### status

Check daemon status.

bk status --json

{
  "ok": true,
  "data": {
    "running": true,
    "pid": 12345,
    "port": 3457,
    "started_at": "2026-02-19T10:00:00+00:00",
    "version": "0.1.0"
  }
}

When stopped: running is false, all other fields are null.

### Global Flags

FlagEnv VariableDefaultDescription--json--offMachine-readable JSON to stdout--dir <path>BITKIT_DIR~/.bitkit/Wallet data directory--network <net>BITKIT_NETWORKmainnetmainnet or regtest--listen <port>BITKIT_LISTENoffP2P listen port on 0.0.0.0:<port>--password <pw>BITKIT_PASSWORD--Wallet password (for encrypted seeds)

### Wallet

init

Create a new wallet. Idempotent -- re-running prints existing wallet info.

bk init --no-password --json

{
  "ok": true,
  "data": {
    "node_id": "02abc123...",
    "seed_phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
    "wallet_dir": "/root/.bitkit",
    "network": "mainnet",
    "pubky_id": "8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo"
  }
}

ArgRequiredDescription--no-passwordone ofStore seed as plaintext (for agents)--password <pw>one ofEncrypt seed with AES-256-GCM + Argon2id

info

Show node status, channel counts, sync state.

bk info --json

{
  "ok": true,
  "data": {
    "node_id": "02abc123...",
    "network": "mainnet",
    "channels_active": 1,
    "channels_pending": 0,
    "block_height": 880000,
    "synced": true,
    "wallet_dir": "/root/.bitkit"
  }
}

balance

Show Lightning and on-chain balances in satoshis.

bk balance --json

{
  "ok": true,
  "data": {
    "lightning_sats": 450000,
    "onchain_sats": 50000,
    "total_sats": 500000,
    "total_onchain_sats": 55000,
    "anchor_reserve_sats": 5000,
    "pending_sweep_sats": 0,
    "pending_sweeps": []
  }
}

onchain_sats -- spendable on-chain balance
total_onchain_sats -- all on-chain funds including reserved
anchor_reserve_sats -- sats reserved for anchor channel fees
pending_sweep_sats -- funds sweeping from closed channels (not yet spendable)
pending_sweeps -- array of sweep entries with amount_sats, status, spending_txid, confirmation_height

ArgDescription--btcDisplay in BTC instead of sats (human output only)

config

Show resolved configuration with source tracking. Does not start the node.

bk config --json

{
  "ok": true,
  "data": {
    "network": { "value": "mainnet", "source": "default" },
    "wallet_dir": { "value": "/root/.bitkit", "source": "default" },
    "chain_source": { "value": "esplora", "source": "file" },
    "esplora_url": { "value": "https://blockstream.info/api", "source": "file" },
    "electrum_url": { "value": "", "source": "file" },
    "rgs_url": { "value": "https://rapidsync.lightningdevkit.org/snapshot", "source": "file" },
    "blocktank_url": { "value": "https://api1.blocktank.to/api", "source": "file" },
    "listen_port": { "value": "off", "source": "default" }
  }
}

Each entry has value (resolved value) and source (cli, env, file, or default).

address

Get a new on-chain receive address. Generates a fresh address each call. Optionally validate an existing address.

bk address --json
bk address --type taproot --json
bk address --validate bc1q... --json

Generate address:

{
  "ok": true,
  "data": { "address": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", "address_type": "native-segwit" }
}

Validate address (--validate):

{
  "ok": true,
  "data": { "address": "bc1q...", "valid": true, "network": "mainnet", "reason": null }
}

On invalid address: valid is false and reason explains why.

ArgDefaultDescription--type <type>native-segwitAddress type: legacy, nested-segwit, native-segwit, taproot--validate <addr>--Validate <addr> for the current network; no node start needed--qroffDisplay address as QR code in terminal (human output only)

send

Send bitcoin on-chain to an address.

bk send bc1q... 50000 --json
bk send bc1q... 50000 --fee-rate 5 --json
bk send bc1q... --drain --json
bk send bc1q... 50000 --utxo <txid>:<vout> --json

{
  "ok": true,
  "data": {
    "txid": "abc123...",
    "address": "bc1q...",
    "amount_sats": 50000,
    "drain": false,
    "fee_rate_sat_per_vb": 5,
    "utxos_used": ["abc123...:0"]
  }
}

When --drain is used, amount_sats is null. When no --fee-rate is set, fee_rate_sat_per_vb is null.

ArgDefaultDescription<address>requiredDestination Bitcoin address<amount_sats>--Amount in satoshis (omit with --drain)--drainoffSend all spendable funds (mutually exclusive with --utxo)--fee-rate <sat/vb>autoFee rate in sat/vb--utxo <txid:vout>--Spend a specific UTXO (repeatable; incompatible with --drain)

list-utxos

List all spendable UTXOs in the on-chain wallet.

bk list-utxos --json

{
  "ok": true,
  "data": {
    "utxos": [
      {
        "txid": "abc123...",
        "vout": 0,
        "value_sats": 1000000,
        "outpoint": "abc123...:0"
      }
    ],
    "total_sats": 1000000,
    "count": 1
  }
}

Use outpoint values (e.g. abc123...:0) as --utxo arguments for send and estimate-fee.

estimate-fee

Preview the fee for an on-chain send without broadcasting.

bk estimate-fee bc1q... 50000 --json
bk estimate-fee bc1q... 50000 --fee-rate 5 --json

{
  "ok": true,
  "data": {
    "fee_sats": 250,
    "address": "bc1q...",
    "amount_sats": 50000,
    "fee_rate_sat_per_vb": 5
  }
}

fee_rate_sat_per_vb is null when not specified (node chooses automatically).

ArgDefaultDescription<address>requiredDestination Bitcoin address<amount_sats>requiredAmount to send in satoshis--fee-rate <sat/vb>autoOverride fee rate in sat/vb--utxo <txid:vout>--Restrict coin selection to specific UTXOs

bump-fee

Replace an unconfirmed transaction with a higher-fee version (RBF). The original transaction must be in the mempool and RBF-enabled.

bk bump-fee <txid> <fee_rate> --json

{
  "ok": true,
  "data": {
    "original_txid": "abc123...",
    "new_txid": "def456...",
    "fee_rate_sat_per_vb": 20
  }
}

ArgDescription<txid>TXID of the unconfirmed transaction to replace<fee_rate>New fee rate in sat/vb (must be higher than the original)

On failure (invalid txid, not in mempool, RBF not signalled): exits with code 1.

cpfp

Accelerate a stuck transaction by spending one of its outputs at a higher fee rate (Child-Pays-For-Parent).

bk cpfp <txid> --json
bk cpfp <txid> --fee-rate 30 --json
bk cpfp <txid> --urgent --json

{
  "ok": true,
  "data": {
    "parent_txid": "abc123...",
    "child_txid": "def456...",
    "fee_rate_sat_per_vb": 30,
    "urgent": false
  }
}

fee_rate_sat_per_vb is null when neither --fee-rate nor --urgent is set.

ArgDefaultDescription<txid>requiredTXID of the parent transaction to accelerate--fee-rate <sat/vb>autoExplicit fee rate for the child transaction--urgentoffAutomatically compute a high fee rate to confirm next block

On failure (invalid txid, no spendable output found): exits with code 1.

history

List recent transactions, newest first.

bk history --limit 5 --type send --json

{
  "ok": true,
  "data": [
    {
      "type": "send",
      "amount_sat": 5000,
      "fee_sat": 3,
      "status": "complete",
      "timestamp": "2026-02-15 12:00:00",
      "description": "coffee"
    }
  ]
}

ArgDefaultDescription--limit <n>20Number of entries--type <send|recv>allFilter by direction

Status values: complete, pending, failed
Type values: send, recv

### Lightning Payments

invoice

Generate a BOLT 11 invoice to receive payment.

bk invoice 5000 --description "agent service" --json

{
  "ok": true,
  "data": {
    "bolt11": "lnbc50u1p...",
    "payment_hash": "a1b2c3d4e5f6...",
    "amount_sat": 5000,
    "description": "agent service",
    "expires_at": "2026-02-15T14:30:00+00:00"
  }
}

ArgDefaultDescription<amount_sats>requiredAmount in satoshis--description <text>""Invoice memo--expiry <secs>3600Invoice expiry in seconds--waitoffBlock until payment received--timeout <secs>300Timeout when using --wait--qroffDisplay invoice as QR code in terminal (human output only)

Two-phase output with --wait: First prints the invoice envelope above, then blocks. When payment arrives, prints a second envelope to stdout:

{
  "ok": true,
  "data": {
    "status": "received",
    "amount_sat": 5000,
    "payment_hash": "a1b2c3d4e5f6..."
  }
}

If timeout expires, exits with code 2 (network error). Always use --listen <port> with --wait so the payer can connect.

pay

Pay a BOLT 11 invoice, LNURL, or Lightning Address. Polls until success, failure, or timeout.

bk pay lnbc50u1p... --json
bk pay lnurl1dp68gurn... --json

{
  "ok": true,
  "data": {
    "status": "success",
    "amount_sat": 5000,
    "fee_sat": 3,
    "payment_hash": "a1b2c3d4e5f6...",
    "preimage": "1a2b3c4d5e6f..."
  }
}

ArgDefaultDescription<bolt11>requiredBOLT 11 invoice, LNURL, or Lightning Address--max-fee <sats>unlimitedMaximum routing fee in satoshis--timeout <secs>60Payment timeout

LNURL/Lightning Address support: pay accepts LNURL (lnurl1...) and resolves to a BOLT11 invoice. For LNURL-pay with variable amounts (where min_sendable != max_sendable), the CLI currently requires a fixed-amount LNURL. Lightning Address (user@domain.tld) requires a --amount flag which is not yet implemented — use a BOLT11 invoice instead.

On failure: exits with code 1 ("Payment failed"). On timeout: exits with code 2.

withdraw

Withdraw from an LNURL-withdraw endpoint. Creates an invoice and submits it to the LNURL service.

bk withdraw lnurl1dp68gurn... --json
bk withdraw lnurl1dp68gurn... --amount 10000 --json

{
  "ok": true,
  "data": {
    "status": "submitted",
    "amount_sat": 10000,
    "bolt11": "lnbc100u1p..."
  }
}

ArgDefaultDescription<lnurl>requiredLNURL-withdraw bech32 string--amount <sats>max withdrawableAmount in satoshis

### Channels

open-channel

Open a Lightning channel to a peer. Requires on-chain funds.

bk open-channel 02ab...@127.0.0.1:9735 100000 --json

{
  "ok": true,
  "data": {
    "channel_id": "12345678901234567890",
    "peer": "02ab...",
    "amount_sats": 100000
  }
}

ArgDescription<peer>Peer in pubkey@host:port format<amount_sats>Channel capacity in satoshis--push <sats>Give the peer initial balance

close-channel

Close a Lightning channel. Get channel_id and peer from list-channels.

bk close-channel 12345678901234567890 02ab... --json

{
  "ok": true,
  "data": {
    "channel_id": "12345678901234567890",
    "status": "closing"
  }
}

ArgDescription<channel_id>Channel ID (decimal, from list-channels)<peer>Counterparty node public key--forceForce close (use when peer is unresponsive)

Status values: closing (cooperative), force_closing

list-channels

List all Lightning channels with capacity, balances, and status.

bk list-channels --json

{
  "ok": true,
  "data": [
    {
      "channel_id": "12345678901234567890",
      "peer": "02ab...",
      "capacity_sats": 500000,
      "local_balance_msat": 250000000,
      "remote_balance_msat": 250000000,
      "is_usable": true,
      "is_channel_ready": true,
      "is_outbound": false
    }
  ]
}

Note: balances are in millisatoshis. Divide by 1000 for satoshis.

### LSP (Blocktank Liquidity)

Use the Blocktank LSP to get inbound Lightning liquidity without manually finding peers.

lsp info

Get LSP service info: available nodes, channel limits, fee rates.

bk lsp info --json

{
  "ok": true,
  "data": {
    "nodes": [
      {
        "alias": "blocktank-lsp",
        "pubkey": "02abc...",
        "connection_strings": ["02abc...@1.2.3.4:9735"]
      }
    ],
    "min_channel_size_sats": 20000,
    "max_channel_size_sats": 5000000,
    "min_expiry_weeks": 2,
    "max_expiry_weeks": 52,
    "network": "Mainnet",
    "fee_rates_fast": 10,
    "fee_rates_mid": 5,
    "fee_rates_slow": 1
  }
}

lsp estimate-fee

Estimate the cost of a channel order before creating it.

bk lsp estimate-fee 500000 --json

{
  "ok": true,
  "data": {
    "fee_sats": 500,
    "network_fee_sats": 300,
    "service_fee_sats": 200
  }
}

ArgDefaultDescription<lsp_balance_sats>requiredInbound liquidity amount--expiry <weeks>6Channel expiry in weeks--client-balance-sats <sats>0Outbound liquidity (your side)

lsp create-order

Create a channel order. Returns payment details.

bk lsp create-order 500000 --json

{
  "ok": true,
  "data": {
    "order_id": "ord-abc123...",
    "state": "Created",
    "fee_sats": 500,
    "lsp_balance_sats": 500000,
    "client_balance_sats": 0,
    "payment_address": "bc1q...",
    "payment_bolt11": "lnbc...",
    "order_expires_at": "2026-03-01T00:00:00Z"
  }
}

ArgDefaultDescription<lsp_balance_sats>requiredInbound liquidity amount--expiry <weeks>6Channel expiry in weeks--client-balance-sats <sats>0Your outbound balance--zero-confoffEnable zero-conf channel

After creating, pay to payment_address (on-chain) or payment_bolt11 (Lightning). Then poll get-order until state becomes Paid.

lsp get-order

Check the status of an order.

bk lsp get-order ord-abc123 --json

{
  "ok": true,
  "data": {
    "order_id": "ord-abc123...",
    "state": "Open",
    "state2": "Paid",
    "fee_sats": 500,
    "lsp_balance_sats": 500000,
    "client_balance_sats": 0,
    "channel_expiry_weeks": 6,
    "channel_state": "Opening",
    "funding_tx": "abc123def...",
    "payment_state": "Paid",
    "order_expires_at": "2026-03-01T00:00:00Z",
    "created_at": "2026-02-17T00:00:00Z"
  }
}

channel_state and funding_tx are null until the channel is being opened.

State enum values for lsp get-order:

FieldValuesDescriptionstateCreated, Open, Expired, ClosedLegacy order statestate2Created, Paid, Executed, ExpiredCurrent order state (prefer this)channel_stateOpening, Open, ClosedChannel lifecycle statepayment_stateCreated, Paid, Refunded, RefundAvailable, CanceledPayment lifecycle state

Poll state2 for order progress: Created → Paid (after on-chain payment) → Executed (channel opened).

lsp open-channel

Tell Blocktank to open a channel to your node. The order must be in Paid state. Requires --listen so Blocktank can connect.

bk lsp open-channel ord-abc123 --listen 9735 --json

{
  "ok": true,
  "data": {
    "order_id": "ord-abc123...",
    "state": "Open",
    "channel_state": "Opening",
    "funding_tx": "abc123def..."
  }
}

### Messaging (Pubky Encrypted)

End-to-end encrypted messaging via the Pubky network. Identity is created during init.

message whoami

Print your Pubky messaging ID. Does not require network access.

bk message whoami --json

{
  "ok": true,
  "data": {
    "pubky_id": "8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo"
  }
}

message send

Send an encrypted message to a peer.

bk message send 8pin...xyz '{"type":"invoice","bolt11":"lnbc...","amount_sats":5000,"description":"service"}' --json

{
  "ok": true,
  "data": {
    "message_id": "abc-123-def",
    "recipient": "8pin...xyz"
  }
}

ArgDescription<pubky>Recipient's Pubky ID<text>Message content (plain text or JSON string)

message read

Read the full conversation with a peer, sorted by timestamp.

bk message read 8pin...xyz --json

{
  "ok": true,
  "data": {
    "messages": [
      {
        "sender": "8pin...abc",
        "content": "{\\"type\\":\\"invoice\\",\\"bolt11\\":\\"lnbc...\\",\\"amount_sats\\":5000,\\"description\\":\\"service\\"}",
        "timestamp": 1708000000,
        "verified": true
      }
    ]
  }
}

FieldDescriptionsenderPubky ID of the message authorcontentMessage text (may contain JSON -- parse it)timestampUnix timestamp in secondsverifiedtrue if cryptographic signature verified

message listen

Poll for new messages from a peer. Streams one envelope per line to stdout as messages arrive.

bk message listen 8pin...xyz --interval 3 --timeout 120 --json

Each new message is printed as a single-line envelope:

{"ok":true,"data":{"sender":"8pin...abc","content":"hello","timestamp":1708000001,"verified":true}}

ArgDefaultDescription<pubky>requiredPeer's Pubky ID--interval <secs>3Poll interval--timeout <secs>0 (forever)Stop after this many seconds

Status messages go to stderr. Only message JSON goes to stdout.

### Daemon HTTP API

When the daemon is running (bk start), all commands proxy through an HTTP API on localhost:3457 (configurable via --port). The API uses HTTP Basic Auth with auto-generated credentials.

Authentication: HTTP Basic Auth (bitkit:<password>). Password is auto-generated on first bk start and stored in <wallet_dir>/api-password.

Endpoints:

MethodPathDescriptionGET/healthHealth check (no auth)GET/balanceWallet balanceGET/infoNode infoGET/history?limit=20&type=sendPayment historyGET/channelsList channelsGET/utxosList UTXOsPOST/addressGenerate receive addressPOST/sendSend on-chainPOST/invoiceCreate Lightning invoicePOST/payPay Lightning invoicePOST/estimate-feeEstimate on-chain feePOST/bump-feeRBF fee bumpPOST/cpfpCPFP accelerationPOST/channels/openOpen channelPOST/channels/closeClose channelGET/eventsWebSocket event stream

All responses use the same JSON envelope as CLI --json output: {"ok": true, "data": {...}}.

POST Request Body Schemas:

POST /address:

{ "address_type": "native-segwit" }

address_type is optional. Values: legacy, nested-segwit, native-segwit, taproot.

POST /send:

{ "address": "bc1q...", "amount_sats": 50000, "drain": false, "fee_rate": 5, "utxos": ["txid:0"] }

amount_sats is required unless drain is true. fee_rate, drain, utxos are optional.

POST /estimate-fee:

{ "address": "bc1q...", "amount_sats": 50000, "fee_rate": 5, "utxos": ["txid:0"] }

fee_rate and utxos are optional.

POST /bump-fee:

{ "txid": "abc123...", "fee_rate": 20 }

Both fields required.

POST /cpfp:

{ "txid": "abc123...", "fee_rate": 30, "urgent": false }

txid is required. fee_rate and urgent are optional (mutually exclusive).

POST /invoice:

{ "amount_sats": 5000, "description": "agent service", "expiry": 3600 }

amount_sats is required. description and expiry are optional.

POST /pay:

{ "bolt11": "lnbc50u1p...", "max_fee": 100, "timeout": 60 }

bolt11 is required. max_fee and timeout are optional.

POST /channels/open:

{ "peer": "02ab...@127.0.0.1:9735", "amount_sats": 100000, "push_sats": 0 }

peer and amount_sats are required. push_sats is optional.

POST /channels/close:

{ "channel_id": "12345678901234567890", "peer": "02ab...", "force": false }

channel_id and peer are required. force is optional (default false).

### Webhooks

Configure in ~/.bitkit/<network>/config.toml:

webhook_url = "https://your-agent.example.com/webhook"
webhook_secret = "your-hmac-secret"

Events and Payloads:

Each webhook POST body contains event, data, and timestamp (Unix ms):

payment_received — Lightning payment received:

{
  "event": "payment_received",
  "data": { "payment_hash": "abc...", "amount_sat": 1000 },
  "timestamp": 1708444800000
}

payment_sent — Outbound Lightning payment succeeded:

{
  "event": "payment_sent",
  "data": { "payment_hash": "abc...", "amount_sat": 5000, "fee_sat": 3 },
  "timestamp": 1708444800000
}

payment_failed — Outbound Lightning payment failed:

{
  "event": "payment_failed",
  "data": { "payment_hash": "abc...", "reason": "RouteNotFound" },
  "timestamp": 1708444800000
}

payment_hash and reason are nullable (may be null).

channel_ready — Channel fully open and usable:

{
  "event": "channel_ready",
  "data": { "channel_id": "4242...", "counterparty_node_id": "02ab..." },
  "timestamp": 1708444800000
}

counterparty_node_id is nullable.

channel_closed — Channel closed:

{
  "event": "channel_closed",
  "data": { "channel_id": "4242...", "counterparty_node_id": "02ab...", "reason": "CooperativeClosure" },
  "timestamp": 1708444800000
}

counterparty_node_id and reason are nullable.

Signature: X-Bitkit-Signature: hmac-sha256=<hex> header (HMAC-SHA256 of body using webhook_secret).

Retry: 3 attempts with exponential backoff (1s, 4s, 16s).

### WebSocket Events

Connect to ws://localhost:3457/events with Basic Auth. Receives the same events as webhooks in real-time as JSON text frames.

# Example with websocat
websocat ws://localhost:3457/events -H "Authorization: Basic $(echo -n 'bitkit:PASSWORD' | base64)"

Frame format: Each event is a JSON text frame with the same structure as webhook payloads:

{"event":"payment_received","data":{"payment_hash":"abc...","amount_sat":1000},"timestamp":1708444800000}

Behavior:

Server sends a WebSocket Ping every 10 seconds as a keepalive
If the client falls behind, the server sends a warning frame: {"warning":"missed 3 events"}
Connection closes when: client sends a Close frame, any WebSocket error occurs, or the daemon shuts down
No client-to-server messages are expected (receive-only stream)

### Configuration Reference

Config file: ~/.bitkit/<network>/config.toml (created by bk init). All fields are under [node]:

[node]
network = "mainnet"                    # "mainnet" or "regtest"
chain_source = "esplora"               # "esplora" or "electrum"
esplora_url = "https://blockstream.info/api"
electrum_url = ""                      # used when chain_source = "electrum"
rgs_url = "https://rapidsync.lightningdevkit.org/snapshot"
blocktank_url = "https://api1.blocktank.to/api"
webhook_url = ""                       # POST target for events
webhook_secret = ""                    # HMAC-SHA256 signing key
auto_liquidity = false                 # enable background inbound capacity monitor
auto_liquidity_threshold_sats = 100000 # order channel when inbound < this
auto_liquidity_channel_size_sats = 500000  # channel size to order

FieldTypeDefaultDescriptionnetworkstring"mainnet"Bitcoin networkchain_sourcestring"esplora"Chain sync backend: esplora or electrumesplora_urlstring"https://blockstream.info/api"Esplora API URLelectrum_urlstring""Electrum server URL (regtest: tcp://127.0.0.1:60001)rgs_urlstring(LDK default)Rapid Gossip Sync URL for Lightning routing graphblocktank_urlstring"https://api1.blocktank.to/api"Blocktank LSP API URLwebhook_urlstring""Webhook POST target (empty = disabled)webhook_secretstring""HMAC-SHA256 key for X-Bitkit-Signature headerauto_liquidityboolfalseEnable background inbound capacity monitorauto_liquidity_threshold_satsu64100000Order channel when inbound capacity drops below thisauto_liquidity_channel_size_satsu64500000Size of auto-ordered channels

### Auto-Liquidity

When auto_liquidity = true in config.toml, the daemon monitors inbound channel capacity every 5 minutes. If total inbound capacity drops below auto_liquidity_threshold_sats, it automatically creates a Blocktank channel order for auto_liquidity_channel_size_sats.

Important: The auto-created order still requires on-chain payment to payment_address from the order. The daemon does not pay automatically — an agent or human must fund the order.

Workflow:

Enable in config: auto_liquidity = true
Start daemon: bk start --json
Monitor for channel_ready events via webhook or WebSocket
When a Blocktank order is created, check bk lsp get-order <id> for the payment_address
Pay the order on-chain, then the channel opens automatically

### API Password

The daemon HTTP API uses HTTP Basic Auth. The password is auto-generated on first bk start.

Location: <wallet_dir>/api-password (e.g., ~/.bitkit/api-password)
Format: 32 random bytes encoded as 64 hex characters
Permissions: File mode 0600 (owner read/write only)

# Read the password
cat ~/.bitkit/api-password

# Use with curl
curl -u "bitkit:$(cat ~/.bitkit/api-password)" http://localhost:3457/balance

# Use with WebSocket
websocat ws://localhost:3457/events -H "Authorization: Basic $(echo -n "bitkit:$(cat ~/.bitkit/api-password)" | base64)"

The password persists across daemon restarts. Delete the file to force regeneration on next bk start.

### Exit Codes

CodeNameCommon CausesRecovery0Success----1User errorBad input, missing wallet, invalid invoice, expired invoiceFix input and retry2Network errorConnection failed, sync timeout, payment timeoutRetry after delay3Insufficient fundsNot enough on-chain or Lightning balanceFund wallet or open channel

### Error Envelope

When --json is used and an error occurs, a structured error envelope is written to stderr:

{
  "ok": false,
  "error": "Invalid invoice: ...",
  "code": 1
}

### Bash Pattern

OUTPUT=$(bk pay "$BOLT11" --json 2>/dev/null)
EXIT_CODE=$?

case $EXIT_CODE in
  0) echo "Success: $(echo "$OUTPUT" | jq -r '.data.payment_hash')" ;;
  1) echo "Bad input -- check the invoice" >&2; exit 1 ;;
  2) echo "Network issue -- retrying..." >&2; sleep 5; bk pay "$BOLT11" --json ;;
  3) echo "Insufficient funds" >&2; exit 3 ;;
esac

Important: Errors print to stderr (as JSON envelope when --json is used). Success output prints to stdout. Always capture them separately.

### Multi-Wallet Pattern

Run multiple independent agent wallets by using separate data directories.

export BITKIT_DIR="/tmp/agent-${SESSION_ID}"
bk init --no-password --json
bk address --json
# ... each agent operates independently

Or use the --dir flag:

bk --dir /tmp/agent-alice init --no-password --json
bk --dir /tmp/agent-bob init --no-password --json

Each directory gets its own seed, node identity, Pubky ID, and channel state. Agents sharing a machine can run concurrently with different --dir values.

### Input Validation Reference

FormatPatternExampleNode pubkey66-char hex (compressed secp256k1)02abc123...Peer addresspubkey@host:port02abc...@127.0.0.1:9735Transaction ID64-char hex4a5e1e4baab89f3a...Outpointtxid:vout (decimal vout)4a5e1e4b...:0Channel IDDecimal u12812345678901234567890Bitcoin addressNetwork-validated bech32/base58bc1q..., bcrt1q...BOLT11 invoicelnbc... or lnbcrt... prefixlnbc50u1p...LNURLBech32 with lnurl HRPlnurl1dp68gurn...Lightning Addressuser@domain.tldagent@pay.bitkit.toPubky ID52-char z-base-328pinxrz9tux...45moFee ratePositive integer (sat/vb)5AmountNon-negative integer (satoshis)50000

All validation errors exit with code 1 and include a descriptive error message.

### Agent Message Protocol

Agents communicate structured data by sending JSON strings as message content. The convention uses a type field to distinguish message kinds:

Invoice request:

{
  "type": "invoice",
  "bolt11": "lnbc50u1p...",
  "amount_sats": 5000,
  "description": "data analysis service"
}

Payment confirmation:

{
  "type": "payment_confirmation",
  "payment_hash": "a1b2c3d4e5f6...",
  "amount_sats": 5000
}

### Workflow: Discover, Message, Pay

# 1. Get your own Pubky ID to share with other agents
MY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')

# 2. Send a message to another agent
bk message send "$PEER_PUBKY" "Hello, I need data analysis" --json

# 3. Listen for their reply (they'll send an invoice)
bk message listen "$PEER_PUBKY" --timeout 120 --json | while IFS= read -r MSG; do
  TYPE=$(echo "$MSG" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)
  if [ "$TYPE" = "invoice" ]; then
    BOLT11=$(echo "$MSG" | jq -r '.data.content' | jq -r '.bolt11')
    echo "Received invoice: $BOLT11"
    break
  fi
done

# 4. Pay the invoice
bk pay "$BOLT11" --json

### End-to-End: Pay Another Agent

Complete flow where Agent A requests a service, Agent B invoices for it, Agent A pays, and Agent B confirms.

### Agent B (service provider)

#!/usr/bin/env bash
set -euo pipefail
export BITKIT_DIR=/tmp/agent-b

# Initialize and share identity
bk init --no-password --json >/dev/null
MY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')
echo "Agent B Pubky ID: $MY_ID"

# Listen for requests from Agent A
bk message listen "$AGENT_A_PUBKY" --timeout 300 --json | while IFS= read -r MSG; do
  CONTENT=$(echo "$MSG" | jq -r '.data.content')

  # Create invoice for the requested service
  INVOICE_JSON=$(bk invoice 5000 --description "data analysis" --json)
  BOLT11=$(echo "$INVOICE_JSON" | jq -r '.data.bolt11')

  # Send invoice to Agent A
  PAYLOAD=$(jq -n --arg b "$BOLT11" '{type:"invoice",bolt11:$b,amount_sats:5000,description:"data analysis"}')
  bk message send "$AGENT_A_PUBKY" "$PAYLOAD" --json

  # Wait for payment
  RECEIVED=$(bk invoice 5000 --description "data analysis" --wait --listen 9735 --json | tail -1)
  HASH=$(echo "$RECEIVED" | jq -r '.data.payment_hash')

  # Confirm payment
  CONFIRM=$(jq -n --arg h "$HASH" '{type:"payment_confirmation",payment_hash:$h,amount_sats:5000}')
  bk message send "$AGENT_A_PUBKY" "$CONFIRM" --json
  break
done

### Agent A (client)

#!/usr/bin/env bash
set -euo pipefail
export BITKIT_DIR=/tmp/agent-a

# Initialize
bk init --no-password --json >/dev/null
MY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')

# Request service from Agent B
bk message send "$AGENT_B_PUBKY" "Please analyze dataset X" --json

# Wait for invoice
BOLT11=""
bk message listen "$AGENT_B_PUBKY" --timeout 120 --json | while IFS= read -r MSG; do
  TYPE=$(echo "$MSG" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)
  if [ "$TYPE" = "invoice" ]; then
    BOLT11=$(echo "$MSG" | jq -r '.data.content' | jq -r '.bolt11')
    break
  fi
done

# Pay the invoice
PAY_RESULT=$(bk pay "$BOLT11" --json)
echo "Payment status: $(echo "$PAY_RESULT" | jq -r '.data.status')"
echo "Fee paid: $(echo "$PAY_RESULT" | jq -r '.data.fee_sat') sats"

# Wait for confirmation
bk message listen "$AGENT_B_PUBKY" --timeout 60 --json | while IFS= read -r MSG; do
  TYPE=$(echo "$MSG" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)
  if [ "$TYPE" = "payment_confirmation" ]; then
    echo "Payment confirmed by Agent B"
    break
  fi
done

### Security & Privacy

Trust model: Fully self-custodial. The BIP39 seed never leaves the local machine. No telemetry, analytics, or tracking of any kind.

External endpoints contacted:

EndpointPurposeData SentConfigurableEsplora serverChain sync (mainnet)Transaction queriesYes (esplora_url)Electrum serverChain sync (regtest)Transaction queriesYes (electrum_url)RGS serverLightning gossip syncNone (download only)Yes (rgs_url)Blocktank APILSP channel ordersNode ID, order paramsYes (blocktank_url)LNURL servicePay/withdraw resolutionAmount, callback URLUser-initiated onlyPubky DHTEncrypted messagingE2E encrypted payloadsBuilt-inWebhook URLEvent notificationsPayment/channel eventsYes (webhook_url)

Seed storage: Custom binary format with magic bytes (BKIT), version byte, and encrypted (AES-256-GCM + Argon2id KDF) or plaintext flag. File: ~/.bitkit/<network>/seed.enc.

API password: Auto-generated 32-byte random hex, stored with 0600 permissions. Only used for local daemon HTTP API access.

### LSP Workflow: Fresh Wallet to Lightning-Ready

From a brand new wallet to being able to send and receive Lightning payments:

# 1. Create wallet
bk init --no-password --json

# 2. Get on-chain address
ADDRESS=$(bk address --json | jq -r '.data.address')
echo "Fund this address: $ADDRESS"

# 3. (Fund the address externally, wait for confirmation)

# 4. Estimate channel fees
bk lsp estimate-fee 500000 --json

# 5. Create channel order
ORDER_ID=$(bk lsp create-order 500000 --json | jq -r '.data.order_id')

# 6. Pay the order on-chain (the payment_address from create-order)
# ... or wait for on-chain funding to cover it

# 7. Poll until order is paid
while true; do
  STATE=$(bk lsp get-order "$ORDER_ID" --json | jq -r '.data.state2')
  [ "$STATE" = "Paid" ] && break
  sleep 10
done

# 8. Open the channel
bk lsp open-channel "$ORDER_ID" --listen 9735 --json

# 9. Verify channel is active
bk list-channels --json | jq '.data[0].is_usable'

# Now ready to send and receive Lightning payments

### Tags

bitcoin, lightning, payment, invoice, self-custody, wallet, CLI, agent, LNURL, lightning-address, BOLT11, LSP, lowest-fees, daemon, webhooks, websocket, encrypted-messaging, on-chain, channels, liquidity
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: ovitrif
- Version: 0.2.0
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-04-30T13:38:40.455Z
- Expires at: 2026-05-07T13:38:40.455Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/bitkit-cli)
- [Send to Agent page](https://openagent3.xyz/skills/bitkit-cli/agent)
- [JSON manifest](https://openagent3.xyz/skills/bitkit-cli/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/bitkit-cli/agent.md)
- [Download page](https://openagent3.xyz/downloads/bitkit-cli)