Ask your agent
Steps
-
Build, on a machine with network access:
This writes unsigned base64 XDR to
unsigned.xdrand exits. It contacts RPC to read the source account’s sequence number, but nothing is signed and nothing is submitted. -
Sign it. This step needs no RPC connection, so it is the one stage you can run offline. Always pass
--networkor--network-passphraseon this stage:A signature commits to the network passphrase. An unqualifiedtx signfalls back to the saved default network, which can silently be the wrong one and produces a valid-looking envelope that fails to submit. -
Submit the signed envelope, back on a machine with network access:
What --build-only skips
Every tx new <OPERATION> accepts --build-only, and so do the contract commands that submit a transaction (deploy, upload, invoke, extend, restore, asset deploy). It stops before signing and submitting. Read-only commands such as contract id asset do not take it. Building still reads the source account’s sequence number over RPC, so it needs network access and a funded source account. token transfer and token approve do not accept --build-only, so use tx new payment --build-only for a payment handoff. tx sign, tx send, tx hash, tx simulate, tx decode, and the other envelope commands read XDR from stdin, so build, sign, and send compose into a pipeline. tx new builds from flags and tx fetch takes --hash; neither reads stdin.
Parsing the result
tx new <OPERATION> has no --output flag and writes nothing to stdout on success. The transaction hash appears only in the ℹ️ Signing transaction: <HASH> line on stderr, and --quiet removes that line along with everything else. This is not like token transfer, which prints the bare hash as its last stdout line and does support --output json.
To get a parseable receipt, let tx send produce it rather than trying to scrape an earlier stage. tx send, the last stage in step 3 above, returns JSON on success only, with top-level keys status, ledger, application_order, fee_bump, tx_hash, created_at, envelope, result, result_meta, and events. On success status is SUCCESS. The hash field is tx_hash, not hash. It has no --output flag, so the shape is always the indented one.
On a submission failure it writes zero bytes to stdout and puts the error on stderr. Empty stdout with a non-zero exit is therefore a definite failure, not an ambiguous one, and the exception below is specifically about the case where stdout has content you could not parse.
Never merge stderr into stdout here. tx send writes ℹ️ Transaction hash is <HASH> to stderr and the JSON receipt to stdout; 2>&1 interleaves them and breaks the parse. Unparseable content on stdout is not evidence of failure. The transaction may have already succeeded. Confirm with stellar tx fetch result --hash <HASH> --network <NETWORK> before retrying. Capture stderr to a file rather than discarding it; the hash you need for that check is only there.
Inspection before signing
Base64 XDR is not human-readable. Showing someoneAAAAAgAAAABfEbrRS/av… and calling it an approval step is not an approval step: they cannot see the destination, the amount, or whether anything has already signed it. Render the envelope first:
source_account, fee, seq_num, cond (the timebounds, "none" if there are none), memo, the operations array, ext, and signatures: [] on an unsigned envelope. tx decode needs no RPC connection. Do not skip memo when you review: it is the field that routes a deposit at an exchange, and a payment with the wrong memo reaches the right account and the wrong person.
None of those fields is the network. A mainnet payment and a testnet payment decode to an identical field set, so the rendered envelope alone cannot tell a reviewer which network they are approving. Carry the network alongside the envelope out of band: name it in the approval request, and keep testnet and mainnet envelopes in separate, differently named files. The account addresses are the only in-band hint, and they are a weak one, because the same key pair is valid on every network.
stellar tx hash then computes the envelope’s hash without signing or submitting, also from stdin and also offline. Use it to confirm the thing you approved is the thing you are about to sign:
tx sign and from the network on submit.
Two tx hash calls compared against each other confirm the envelope is the same envelope, and nothing more. Both are computed from the passphrase you passed, so they agree even when tx sign used a different one.
To catch a wrong-network signature before you submit, compare tx hash against tx sign’s own output. tx sign prints ℹ️ Signing transaction: <HASH> to stderr, computed under the passphrase it actually used. Both commands run offline:
--network-passphrase explicitly to both commands: omit it on both and they resolve the same saved default, agree with each other, and pass on a wrong-network signature. And --quiet suppresses the tx sign line entirely, so the check and --quiet cannot be used together.
Check the destination while you are still at the build stage. A native payment cannot create an account, so paying an address that has never been funded fails at submit with TxFailed / OpInner(Payment(NoDestination)) and exit code 1. In this flow that is the worst place to find out, because a human has already reviewed and approved the envelope. stellar token balance --id native --account <ADDRESS> --output json answers it for free, and tx new create-account is the operation for an account that does not exist yet.
Air-gapped signing
Onlytx sign and tx hash are genuinely offline. --build-only still needs RPC to read the source account’s sequence number, so building cannot happen on a machine with no network access. The real split is three stages across as many machines as you want: build on a networked machine, sign on an offline machine, submit from a networked machine. Move only the unsigned XDR to the offline machine, sign it there, then move only the signed XDR back. The signing key never touching a networked machine is the actual security property, not that the whole pipeline avoids the network.
Soroban paths need a simulation step
A Soroban invocation’s resource footprint depends on what the contract does, sostellar tx simulate --source-account <SOURCE> runs the transaction against current ledger state and returns the resource fee and footprint it would need, without submitting. contract invoke runs this simulation for you automatically. Composing your own tx new pipeline for a Soroban call means running simulate yourself before send.
22 operations, one reference page
stellar tx new <OPERATION> covers 22 operations, from payment and create-account to DEX and sponsorship operations. Each shares this same build, sign, send composition and adds its own flags on top. Run stellar tx new --help for the full operation list.
Common pitfalls
A pipeline breaks silently if an earlier stage fails but still writes something to stdout. Check the exit code of each stage, or run the pipeline one stage at a time the first time you compose it.tx send can fail with TxBadSeq. The sequence number is fixed at build time, step 1, not at submit time, step 3. Anything else signing for the same source account in between, a concurrent agent or another one of your own commands, makes the envelope stale before it reaches step 3. stellar tx update sequence-number next fetches the source account’s current sequence, increments it, and rewrites the envelope in place. It is a read-only network call.
It keeps the signatures the envelope already carried. Patching an envelope you have already signed and then signing again leaves two signatures on it, and the submit fails TxBadAuthExtra. Patch while the envelope is still unsigned, then sign:
TxBadAuthExtra every time.
Elapsed time alone is not the cause: only another transaction from the same source account advances the sequence. A long delay is harmless unless the envelope carries timebounds, which tx decode shows as cond.
Two --build-only calls against the same source return the same sequence number. --build-only reads the account’s current sequence and adds one; it reserves nothing and advances nothing. An agent that batch-builds several envelopes before submitting any of them gets a set that all collide, and only the first can land. Build and submit one at a time, or patch each envelope with the command above before you send it.