tx.build
Build an unsigned swap transaction for a route. Re-routes over the current snapshot (never stale), assembles the transaction, and returns everything the wallet needs to sign and broadcast.
For a tx that stays current as pools move, subscribe with tx.subscribe instead — it pushes a freshly assembled tx whenever the route changes.
Request
{
"id": 1,
"method": "tx.build",
"params": {
"sell": "bch",
"buy": "b79bfc8246b5fc4707e7c7dedcb6619ef1ab91f494a790c20b0f4c422ed95b92",
"amount": "1",
"side": "buy",
"funding": [
{
"txid": "aabbccdd...eeff",
"vout": 0,
"value": "60000000",
"script_hex": "76a914...88ac"
}
],
"receive_addr": "bitcoincash:qz...",
"change_addr": "bitcoincash:qz...",
}
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
sell |
string | yes | Sold asset ("bch" or 64-char hex) |
buy |
string | yes | Bought asset ("bch" or 64-char hex) |
amount |
string | yes | Exact-in amount in base units |
funding |
array | yes | Funding UTXOs (1–500 entries) |
receive_addr |
string | yes | CashAddr for bought tokens / token change |
change_addr |
string | yes | CashAddr for BCH change |
min_output |
string | no | Slippage floor (integer string, base units). Applies to side: "sell" only — in buy mode the output is the amount you asked for, so nothing is held to this. |
See the full JSON Schema for request parameters.
Funding Input
| Field | Type | Required | Description |
|---|---|---|---|
txid |
string | yes | UTXO txid (display-order hex) |
vout |
integer | yes | Output index |
value |
string | yes | Sats value (integer string) |
script_hex |
string | yes | scriptPubKey hex |
token |
object | no | CashToken payload ({category, amount}) |
Response
{
"id": 1,
"result": {
"unsigned_tx_hex": "02000000...",
"source_outputs": [
{
"value": "60000000",
"locking_script_hex": "76a914...88ac"
}
],
"inputs_to_sign": [0],
"expected_output": "1",
"fee_sats": "3000",
"fee_token_amount": "0",
"miner_fee_sats": "1234",
"route": {
"kind": "cauldron",
"..."
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
unsigned_tx_hex |
string | The unsigned transaction (hex). Sign and broadcast this. |
source_outputs |
array | Prevouts index-aligned to inputs (WizardConnect whole-tx signing). |
inputs_to_sign |
array | Input indices the client signs (its own funding inputs). |
expected_output |
string | Expected bought amount (slippage reference). |
fee_sats |
string | Router fee in sats (token↔BCH). "0" if fee in tokens or none. |
fee_token_amount |
string | Router fee in sold token units (token↔token). "0" if fee in BCH or none. |
miner_fee_sats |
string | Estimated miner fee in sats. |
route |
object | The route the build was assembled for — the same coarse view a quote returns. |
See the full JSON Schema for the response format.
Signing
Every response carries both aids, so a wallet can use whichever suits it:
source_outputs (prevouts index-aligned to all inputs, for signing the whole
transaction) and inputs_to_sign (the indices of the funding inputs the client
owns).
Broadcasting
The response is an unsigned transaction, and the router never broadcasts it —
sending it is the client's step. Sign unsigned_tx_hex, then POST the signed
transaction to the transaction broadcast API:
curl -X POST https://broadcast.cauldron.quest/broadcast \
-H 'Content-Type: application/json' \
-d '{"tx": "<signed transaction hex>"}'
{ "txid": "94a933a0fa55093a0965eb867f1b9cac2bb07488ced4825bc31f86c9371f76aa" }
Prefer it over broadcasting to a single node: trades against the same pools chain on one another, so a transaction that reaches only part of the network is how double-spend conflicts start.
Transaction Fee Output
Each built transaction carries a router fee as one of its outputs, currently 10 basis points (0.1%) of the trade. This is the on-chain fee for the trade itself, separate from the terms for using the service.
The rate may change during the beta, so read what the build actually pays rather
than hard-coding it: fee_sats for a BCH fee, fee_token_amount for a token
one, "0" where neither applies.
Build Safety
The assembler runs a safety gate before returning the transaction:
- No token burn — every token category is conserved (Σ in == Σ out)
- Sane miner fee — fee is between the relay floor and a max-overpay band
- Fee output present — when a fee was charged, the output must exist at the fee address
- No unspendable outputs — all outputs have non-empty scripts and meet dust thresholds
If any check fails, the build returns a build_failed error instead of an unsafe transaction.
Errors
| Code | Cause |
|---|---|
bad_request |
Malformed params, invalid asset, too many funding inputs (>500) |
route_failed |
No pools or no fill at this amount |
insufficient_funds |
Funding inputs cannot cover outputs + miner fee |
build_failed |
Safety gate violation (token burn, insane fee, etc.) |
slippage |
Expected output below min_output |