Buying
Buy one or more available units by requesting a quote, signing it, and submitting the signed PSBT.
Before starting, create a session for your buying address and use an HTTP client. Your signer must support PSBT signing for the returned input indexes and sighash. Addresses, signatures, and transaction IDs shown as placeholders must be replaced with your own values.
Request, review, and complete
Section titled “Request, review, and complete”Check the returned quote before signing: total_sats must equal subtotal_sats
plus platform_fee_sats and network_fee_sats; signing.sighash must be ALL.
Verify the actual PSBT against your intended purchase, sign only
signing.inputs_to_sign, then submit the signed PSBT with the quote’s fill_id.
curl -s -X POST "$API/v1/fills/request" -H 'content-type: application/json' -d '{ "items": [{ "asset": "RAREPEPE", "quantity": 1, "max_price_sats": 120000 }], "buyer_address": "bc1q..."}'
curl -s -X POST "$API/v1/fills/complete" -H 'content-type: application/json' \ -H "x-session-token: $TOKEN" \ -d '{"fill_id":"...","psbt_hex":"70736274ff..."}'Choose quantity and maximum price
Section titled “Choose quantity and maximum price”A cart line is {asset, quantity, max_price_sats}, not a listing id. The API
resolves each line to the cheapest listings at or under your max_price_sats
when it builds the quote. max_price_sats is a per-unit ceiling and is your
slippage tolerance. A cart holds at most 10 lines and 20 units.
For one particular unit, add target_outpoint: {txid, vout} to the line and
set quantity: 1. The API then buys only that outpoint, even if another unit
is cheaper. It never substitutes another unit. A missing, unavailable, or
over-budget target returns 409 target_unavailable; a stale signed quote
still returns checkout_changed. Refreshing keeps the same target and price
ceiling. There is still at most one line per asset.
The quote lists exactly what you are buying in settlement_items (listing id,
seller, outpoint, price) and which coins fund it in selected_outpoints.
Quotes reserve nothing
Section titled “Quotes reserve nothing”A quote is a short-lived PSBT snapshot (about three minutes). Other buyers can
hold overlapping quotes at the same time. Only POST /v1/fills/complete, after it
verifies your signature byte-for-byte against the stored template, claims the
listings. The first valid signature returned wins, not the first quote.
POST /v1/fills/request needs no session for a public cart. Completing it requires
the buyer’s session and a SIGHASH_ALL signature on the funding inputs.
Sign only signing.inputs_to_sign. Your
funding inputs must be free of Counterparty assets; the API proves that before
quoting.
When the book moves: checkout_changed
Section titled “When the book moves: checkout_changed”If a listing sold, was repriced or delisted, or its UTXO moved, completion answers
409 with code checkout_changed. The body is written against your original
intent:
{ "error": "checkout changed — review and sign a fresh transaction", "code": "checkout_changed", "can_fulfill": true, "requires_new_signature": true, "previous_subtotal_sats": 200000, "current_subtotal_sats": 205000, "previous_platform_fee_sats": 5000, "current_platform_fee_sats": 5125, "changes": [ { "asset": "RAREPEPE", "requested": 2, "available": 2, "max_price_sats": 120000, "current_subtotal_sats": 205000 } ]}Request a new quote and sign again. Never resubmit the old PSBT, and never have your bot silently accept a smaller quantity or a different asset: decide explicitly, then sign the new template.
Other refusals worth handling: insufficient_funds (the body carries
need_sats, have_sats, shortfall_sats), signature_invalid,
template_mismatch, and expired.
Delivery
Section titled “Delivery”By default the purchased balance is detached to an address: output 0 of the
checkout is an encrypted Counterparty detach OP_RETURN, and one detach message
delivers every unit in the cart. You receive an ordinary address balance, not an
asset UTXO.
delivery_addresssets where it goes. It can be any Counterparty address family, legacy1...included. It defaults tobuyer_address.buyer_addressitself is the signing address that funds the purchase, so it must be P2TR or P2WPKH.
quote.delivery echoes mode and address. Its defaulted flag is true when
you omitted delivery_mode and received the detached default; it says nothing
about the address, so check delivery.address against the destination you meant.
For a single acquired unit, delivery_mode: "attached" keeps the asset on
a new UTXO at the buyer’s trading address. This requires exactly one acquired
asset and cannot use a different delivery address. The quote exposes the
retained BTC as delivery.utxo_value_sats; include it when showing the total
wallet debit. It is retained value, not a fee.
After completion
Section titled “After completion”completeFill returns the broadcast txid (and already_completed: true when
you repeat a completion that already happened). The server journals the exact
transaction before it broadcasts and rebroadcasts it if needed.
| Route | Use |
|---|---|
GET /v1/fills/:id |
Buyer-session status of one checkout, for a lost response |
GET /v1/address/:address/in-flight-purchases |
Your journaled, unconfirmed purchases |
GET /v1/fills |
Public recent fills (collection, limit, cursor) |
Private counters
Section titled “Private counters”A seller can answer your exact offer with a private counter. Fill it through the
same checkout with counter_id in place of items: POST /v1/fills/request
{"counter_id": "...", "buyer_address": "..."}. Only the addressed buyer can see
or fill it. List yours with GET /v1/address/:address/counters.
Mempool races
Section titled “Mempool races”Between broadcast and confirmation, a purchase is visible in the mempool like any Bitcoin transaction.
- Listings. The seller’s listing signature is never served; only the market builds purchases from it. Once your purchase is broadcast, though, that signature is public, and someone could build a competing purchase that pays the seller the same price with a higher fee. You would lose the race, not money: your coins are simply not spent. Checkout broadcasts at the next-block rate so the window is short.
- Exact offers and collection offers are fully committed transactions; no third party can reuse their signatures.
- A bidder can back out of an accepted but unconfirmed offer by double-spending their own funding with a higher fee. The sale then fails and no asset moves.