Broadcasting a Transaction
With some coins in hand, we can build and sign a Transaction that spends them, with DartSV or Bitcoin4J, and hand it to Arcade. The Bitcoin4J tutorial and the library docs cover the building. In this part we look at what happens once the Transaction leaves our application.
Broadcasting and merkle proofs in the Developer Guide explains why we broadcast through a service like Arcade, and what a wallet does with the proof. This page is the hands-on version, against our local network.
Submitting
Section titled “Submitting”We POST the Transaction to /tx, in Extended Format. Arcade accepts it as JSON, raw bytes or hex:
curl -s -X POST http://127.0.0.1:23011/tx \ -H 'Content-Type: application/json' \ -d '{"rawTx":"<extended format hex>"}'{"txid":"…","status":202,"txStatus":"RECEIVED"}Notice that there is no /v1 in the address. Arcade serves the ARC API at its root.
Arcade answers 202 RECEIVED at once, every time, and takes the Transaction to the network afterwards. That reply only means Arcade has accepted and checked it. To find out how it went, we have to ask again.
A plain raw Transaction is refused with
460 Not extended format. This is the most common surprise when one first points an existing ARC client at Arcade.
Following it
Section titled “Following it”curl -s http://127.0.0.1:23011/tx/<txid>{"txid":"…","txStatus":"MINED","blockHash":"…","blockHeight":103,"merklePath":"<BUMP hex>"}On the local network, a Transaction moves through these steps:
| Status | When | What it means |
|---|---|---|
RECEIVED |
in the reply to POST /tx |
Arcade accepted and checked it |
ACCEPTED_BY_NETWORK |
after about 2 seconds | Teranode took it |
SEEN_ON_NETWORK |
after about 2 to 8 seconds | it is in a subtree, i.e. in the next block the node will mine |
MINED |
about 3 seconds after the next block | it is in a block, and merklePath holds its proof |
A Transaction can also end up REJECTED, with an ARC error code from 460 to 476 and the reason in extraInfo, or DOUBLE_SPEND_ATTEMPTED, with the competing Transactions in competingTxs. PENDING_RETRY means Arcade will try the broadcast again by itself.
Do note that Arcade only knows the Transactions that were submitted to it. Asking about any other txid gives us
404 {"error":"transaction not found"}, even if the Transaction is in a block.
Three ways to follow it
Section titled “Three ways to follow it”Asking over and over is the simplest, but not the only option.
- Polling
GET /tx/{txid}, as above. - Server-sent events. If we submit with an
X-CallbackTokenheader,GET http://127.0.0.1:23013/events?callbackToken=<token>streams every status change of every Transaction submitted with that token. TheMINEDevents carry the merkle path. When we reconnect, we send the last event’sidasLast-Event-ID, and Arcade replays what we missed. - A webhook. If we submit with an
X-CallbackUrlheader, ArcadePOSTs the status to that URL, withAuthorization: Bearer <X-CallbackToken>. By default it only sends the final ones (MINED,REJECTED,DOUBLE_SPEND_ATTEMPTED,IMMUTABLE), andX-FullStatusUpdates: trueasks for every change.
The webhook URL has to be reachable from the Arcade container, not from our machine. A service running on the host is
http://host.docker.internal:<port>/….
A reorg that moves a Transaction to another block sends MINED again, with the new proof.
Checking the proof
Section titled “Checking the proof”The merklePath is a BUMP (BRC-74) in hex, and it has to compute the merkle root in the header of the block at blockHeight. The stack comes with a script that checks exactly that:
./scripts/verify-proof.py <txid>It prints PROOF VALID, or PROOF DOES NOT MATCH. Our own wallet code should do the same check against its own headers before it stores a proof, and the next part shows where to get those headers from.

