@x402r/cli
v0.2.0
Published
One-shot CLI for paying x402 / x402r endpoints. Wallet-agnostic: raw key, JSON-RPC signer, or custom module.
Readme
@x402r/cli
One-shot CLI for paying x402 / x402r HTTP 402 endpoints. Wallet-agnostic: raw private key, JSON-RPC signer, or a custom signer module. Zero provider SDK dependencies.
Install
Use npx — no project install required.
npx @x402r/[email protected] pay <url> [options]Surface
x402r pay <url> [signer flags] [--max-amount N] [--rpc <url>] [--chain <eip155:id>] [--json]Exactly one signer source must resolve. CLI flag > env var.
| Source | Flag | Env |
|-------------------|-------------------------------------------------|--------------------------------|
| Raw key | --key 0x... | PRIVATE_KEY |
| Remote RPC signer | --signer-url <url> --signer-address 0x... | SIGNER_URL, SIGNER_ADDRESS |
| Custom module | --signer-module <pkg-or-path> | SIGNER_MODULE |
Env var names are unprefixed to match the Foundry / Hardhat / x402-reference convention. The flag form is available when you want to avoid collisions.
Exit codes
| Code | Meaning |
|------|---------|
| 0 | success |
| 1 | network error |
| 2 | malformed 402 / unusable accepts[] |
| 3 | price exceeds --max-amount guard |
| 4 | signature rejected |
| 5 | settlement failed (merchant 4xx/5xx after payment, or facilitator error) |
| 6 | signer resolution failed (none, multiple, or partially-configured sources) |
--json envelope
{
"body": "<merchant response body>",
"status": 200,
"tx": "0x…",
"elapsedMs": 1234,
"signer": { "kind": "key", "address": "0x…" }
}signer is omitted when the URL returned non-402 (no payment was made).
Examples
Raw key
PRIVATE_KEY=0x... \
npx @x402r/[email protected] pay https://example.com/paidJSON-RPC signer
Anything speaking eth_signTypedData_v4 works: Privy wallet RPC, Turnkey,
Fireblocks, Safe, a local cast wallet endpoint, a hardware wallet exposed
over an RPC bridge, etc.
npx @x402r/[email protected] pay https://example.com/paid \
--signer-url https://signer.example/rpc \
--signer-address 0x...Custom module — Privy (@privy-io/server-auth)
// privy-signer.js
import { PrivyClient } from '@privy-io/server-auth'
import { createViemAccount } from '@privy-io/server-auth/viem'
export default async function () {
const privy = new PrivyClient(process.env.PRIVY_APP_ID, process.env.PRIVY_APP_SECRET)
return createViemAccount({
walletId: process.env.PRIVY_WALLET_ID,
address: process.env.PRIVY_WALLET_ADDRESS,
privy,
})
}npx @x402r/[email protected] pay https://example.com/paid \
--signer-module ./privy-signer.jsCustom module — Coinbase CDP server wallet
// cdp-signer.js
import { CdpClient } from '@coinbase/cdp-sdk'
import { toAccount } from 'viem/accounts'
export default async function () {
const cdp = new CdpClient()
const acct = await cdp.evm.getOrCreateAccount({ name: process.env.CDP_ACCOUNT_NAME })
return toAccount(acct)
}npx @x402r/[email protected] pay https://example.com/paid \
--signer-module ./cdp-signer.jsSigner module contract
The module's default export is a factory () => Promise<Account> that
returns a viem Account. The CLI
only needs signTypedData — transaction broadcasting is handled by the
facilitator.
Notes
- Wallet-agnostic by design: the CLI carries zero Privy / CDP / Turnkey
dependencies. Provider SDKs are loaded only when
--signer-modulepoints at a user-authored shim. - Pinned-version invocation (
npx @x402r/[email protected]) is recommended so agent skill wrappers can wildcard-allow exact command shapes. - Supports Base and Base Sepolia out of the box. Any EVM chain known to
viem/chainsworks; unknown chain IDs require--rpc <url>.
