Docs
Buying with escrow
When a plan includes paid items, they go into one escrow order. Your money is held by an escrow agent, not paid to sellers, until you accept the delivery or the acceptance window ends. If a seller does not deliver by the deadline, you are refunded in full. Everything on this page runs in test mode: no real money is held, paid or refunded.
Test mode. The default rail is simulated: balances are numbers in memory and no real money moves. USDC, USDT and BTC rails exist only as payment links and deposit checks, and the escrow contract is unaudited and not deployed. See Payment rails and test mode.
Where you can run this. The hosted app at reuseforge.com has no checkout yet, so no order can be placed there: checkout and orders are coming soon. Everything on this page that you can run uses the simulator or your own node. Its commands and fields are checked against the node code.
How an order works
- Quote. You create an order from the plan's paid items. The signed order records the items and prices, the rail, the fee rate, the arbiter and both deadlines. They cannot change afterwards.
- Deposit. You pay the total to the escrow address for your rail. The escrow agent checks the deposit matches the order total and marks the order funded.
- Delivery. Each seller marks their own item delivered, with an access link and the content hash of what they delivered. When every item is delivered, the acceptance window starts.
- Decision. You accept, or you do nothing until the window ends, or you dispute.
- Settlement. On release, each seller receives the price minus the network fee. On refund, you receive the whole total back.
Free items and build steps never enter an order. If a plan has nothing to buy, there is no escrow.
Order states
| State | Meaning | Leaves this state when |
|---|---|---|
QUOTED | Order created, no money held. | The agent confirms the deposit, or the buyer cancels. |
FUNDED | Total deposited. Waiting for sellers. | Every item is delivered, the delivery deadline passes, or the buyer disputes. |
DELIVERED | Every seller has delivered. The acceptance window is running. | The buyer accepts, the window ends, or the buyer disputes. |
RELEASED | Sellers are paid, minus the fee. Final. | Never. |
REFUNDED | The buyer got the full total back. Final. | Never. |
DISPUTED | The buyer objected. Waiting for the arbiter. | The arbiter chooses release or refund. |
CANCELLED | The buyer cancelled before paying. Final. | Never. |
Who can do what
These limits are enforced when the log is read, so every node applies them the same way. A step signed by the wrong party is ignored.
| Party | Can |
|---|---|
| Buyer | Create the order. Cancel while it is QUOTED. Accept while DELIVERED. Dispute while FUNDED or DELIVERED. Review an item after release. |
| Seller | Mark their own item delivered while the order is FUNDED. Nothing else on the order. |
| Escrow agent (arbiter) | Confirm the deposit. Resolve a dispute. Move the money once an order is released or refunded. On a self-hosted node this is the node operator. |
| Anyone | Record that a deadline has passed. This only freezes a state the clock already implies. |
Acceptance window and deadlines
- Acceptance window. Starts when the last item is delivered. Default 7 days. The buyer can choose another length when creating the order (
accept_window_days), from 1 hour to 90 days. If the window ends in silence, the order is released. This protects sellers from a buyer who simply never answers. - Delivery deadline. Starts when the order is funded. Default 7 days. If any item is still undelivered when it ends, the order is refunded.
Mind the window. Once it ends, the order is released and a dispute is too late. Check what you received, run the tests, and compare the content hash to the one in the listing before the window closes.
One late seller refunds the whole basket. Delivery is all or nothing across the order. If three sellers delivered and a fourth did not, the deadline refunds all of it. If you want sellers to be independent, create separate orders.
Disputes and refunds
| Situation | Result |
|---|---|
| A seller does not deliver by the deadline | Automatic full refund. No arbiter needed. |
| You accept | Released. Sellers are paid. |
| You say nothing after delivery | Released when the window ends. |
You dispute while FUNDED or DELIVERED | The order freezes. The arbiter reads your reason, up to 2,000 characters, and decides release or refund, with a note. |
| The arbiter releases | Sellers are paid in full, minus the fee. |
| The arbiter refunds | You receive the whole total back. No fee is charged. |
The arbiter's decision is all or nothing. There is no partial refund and no per-item split. Say exactly what you ran, what you expected and what failed. There is no fixed rubric yet.
Known gap. Nothing times out a dispute. If the arbiter never answers, the order stays DISPUTED. The architecture notes list arbiter pools and jury-style resolution as open work, so today you are trusting one arbiter. The contract design adds a limit: if the arbiter stays silent for the dispute window (30 days by default), anyone can settle the item with a 50/50 split. That is a design, not running code.
Fees
A released order pays a network fee. The default on a self-hosted node is 1% of each item's price, set by the operator with REPOBLOCK_FEE_BPS. The rate is written into the order when you create it. The fee is deducted from the seller's side: the buyer deposits exactly the sum of the listed prices. Each seller's net is rounded down to the cent, and the fee is the rest. Refunds carry no fee. Free items have no order and so no fee. The amounts in this section are test-mode examples.
| Line | Paid to | Amount |
|---|---|---|
| Checkout kit, listed at $15.00 | Seller | $14.85 |
| Starter UI, listed at $12.00 | Seller | $11.88 |
| Network fee | Fee address | $0.27 |
| Buyer's deposit | $27.00 |
How far the fee can go depends on where the order runs. A self-hosted node starts only with a rate from 0 to 1000 basis points (10%) and rejects any order that carries a higher one. Within that bound, whoever runs the node sets REPOBLOCK_FEE_BPS, and the order shows the rate before you deposit. The escrow contract specified in docs/PAYMENTS.md caps the fee at 500 basis points (5%), records the rate when an order is funded, and applies a later change to new orders only. That contract is not deployed, and only its interface (contracts/src/interfaces/IRepoEscrow.sol) is in the repository. For fees on the hosted app, see Pricing.
Payment rails and test mode
The escrow rules do not depend on the rail. A rail only decides how a deposit is described, checked and paid out. Choose one with --rail when you start the node.
| Rail | Status | What it does |
|---|---|---|
mock | Works | Default. In-memory USD balances. Completes the full cycle. Nothing real moves. |
usdc-base | Partial | USDC on Base. Builds a payment link for the buyer's wallet, and can check a deposit transaction if REPOBLOCK_RPC_URL is set. The node does not send payouts. |
usdt-ethereum | Partial | USDT on Ethereum. Same as above. |
btc | Link only | Builds a Bitcoin payment link priced from a USD quote. No deposit check, no payout. Bitcoin cannot hold a conditional escrow on its own. |
On the non-mock rails, creating an order fails unless every seller has a payout address for that rail. Payouts are meant to be made by an escrow contract for USDC and USDT, specified in docs/PAYMENTS.md. It settles each item on its own and pays out by credit that sellers claim, so one blocked payee cannot hold up the others. Its source is not in the repository yet, it is unaudited and nothing is deployed. Until it is, a stablecoin deposit would sit with whoever runs the escrow agent, and you would be trusting them with it.
Run an order from the command line
Against a local node (setup) on the default simulated rail, so no real money moves, with jq installed:
NODE=http://127.0.0.1:8420
curl -s -X POST $NODE/api/plan -d '{"goal":"RAG chatbot over my PDF invoices with a dashboard and crypto checkout","budget_usd":50}' > plan.json
# 1. quote: send the plan's paid items, with a 3 day acceptance window
OID=$(jq '{paid_items: .recommended.paid_items, accept_window_days: 3}' plan.json \
| curl -s -X POST $NODE/api/orders -d @- | jq -r .order.order_id)
curl -s -X POST $NODE/api/orders/$OID/pay -d '{}' | jq .order.state # "FUNDED" (simulated deposit)
curl -s -X POST $NODE/api/orders/$OID/deliver -d '{}' | jq .order.state # "DELIVERED" (as the demo seller)
curl -s -X POST $NODE/api/orders/$OID/accept -d '{}' | jq .order.payouts # sellers paid, plus the fee lineTo dispute instead of accepting, post {"reason": "..."} to /api/orders/$OID/dispute. The node operator resolves it with {"outcome": "release"} or {"outcome": "refund"} posted to /api/orders/$OID/resolve. These are the endpoints of a self-hosted node. The hosted API is described in the API reference.
What escrow does not do
- It does not look inside the delivery. It records the access link and the hash the seller states. Checking the content against the hash is your job.
- It cannot stop a buyer copying code that was delivered. Escrow protects payment, not secrecy.
- It does not guarantee the code works. It gives you a window to find out, and a way to object.
- It does not make an unaudited contract safe. See what is not guaranteed.
Last updated: