rail402 (package @rail402.dev/cli) is the buyer SDK on the command line. Every payment
needs --max, passes the spending policy and the signing guard, and prints a receipt with the transaction
hash and its explorer link.
From zero to a paid call on the testnet
Commands
Call input for
pay, purchase and quote: --method, --query k=v (repeatable), --header "k: v",
--data <json> or --data-file <path> for HTTP; --tool <name>, --args <json> or --args-file <path> and
--transport streamable-http|sse for MCP; --path-param k=v for a listing’s path parameters. Payment flags:
--asset, --scheme exact|upto (a preference; the policy decides), --session <name> and
--idempotency-key <key>. Repeating a paid command with
the same --idempotency-key never pays twice. It returns the stored result with replayed: true, or the
stored error while the payment’s outcome is open. It checks that payment on chain first, and pays again only
once the chain shows the earlier authorization expired unused. A key used again for another resource in the
same window is refused with payment_request_invalid.
upto resources
quote, pay and purchase pay upto resources like exact ones. The seller’s price is a cap: the quote
shows it, --max must cover it, and the receipt reports both what was authorized and what the seller settled:
session show). tx <hash> recognises an upto settlement (x402.shape: "upto"). On testnet the
canonical settlement contract is pinned by default; RAIL402_UPTO_CONTRACTS pins another (for a private
network), RAIL402_UPTO_FACILITATORS limits the settlement authorities, and RAIL402_SCHEMES=exact turns
upto off. See Spending policy for every check.
JSON output
With--json (or RAIL402_OUTPUT=json) stdout carries exactly one JSON document per command; progress,
logs and --verbose payment events go to stderr.
result by command:
Amounts are decimal strings in whole units of the asset (
"0.015"); atomic fields are base units. JSON
output escapes C1 control characters and bidirectional overrides, so seller text printed to a terminal is inert.
Exit statuses
A script can tell from the status alone whether retrying is safe.paid decides first: any error whose paid is settled or unknown exits 5, even when it is retryable. A
paid command whose receipt status is unknown also exits 5, with an ok: true document. Status 3 also covers
a signed payment that never reached the seller (payment_settlement_unconfirmed or egress_request_too_large
with paid: none): nothing was paid, but its reserved budget stays pending until the authorization expires.
Any failure after a payment was sent, including a budget ledger that cannot be written, reports paid: unknown. The
error-code reference lists the exit status of every code.
Configuration
Settings are read from flags, thenRAIL402_* environment variables, then the config file
($XDG_CONFIG_HOME/rail402/config.json, or --config <path>), then the network’s defaults. The variables are the
same as the MCP server’s, plus RAIL402_FACILITATOR_URL (for tx
attribution and seller check; defaults to the Bazaar URL), RAIL402_FRIENDBOT_URL (testnet only) and
RAIL402_OUTPUT. The config file takes the same settings in camelCase (network, rpcUrl, bazaarUrl,
maxPerCall, maxTotal, assets, keyFile, schemes, uptoContracts, …) and is refused if it holds anything that looks like a secret
key.
The key is found in this order: --key-file, RAIL402_SECRET_KEY_FILE, RAIL402_SECRET_KEY, the config
file’s keyFile, then the default key file for the network
($XDG_CONFIG_HOME/rail402/keys/stellar-testnet.key). keys new and keys import never overwrite a file
(key_exists).
Global flags: --network stellar:testnet|stellar:pubnet, --rpc-url, --bazaar-url, --facilitator-url,
--key-file, --config, --json, --verbose, --timeout <seconds> (default 120), and, for local
development on testnet only, --allow-http and --allow-private-network. On stellar:pubnet the RPC and Bazaar
URLs and both caps are required, account setup needs --yes and never uses Friendbot, and --usdc is
refused with cli_command_not_supported_on_network.