Purchases
What you bought, what was in it, and the paper trail for the orders that never closed.
A settled quote becomes a sale. Quotes are the working record: they expire, they get cancelled, they churn. Sales are the permanent one.
Order history
GET /purchases lists your settled sales, newest first, optionally filtered by
buyer_ref. Each row is the financial summary of one order:
user_price_total, what you paid.our_price_total,cost_basis_total,margin_totalandprofit_total, the breakdown behind it.wallet_count, how many wallets were in the lot.payment_tx_hash, the transfer that paid for it, when the chain gave us one.settled_at,quote_idandbuyer_ref.
Finding one
search works out what it was given, so nobody has to know which kind of id
they are holding before they can look something up.
| What you paste | What it matches |
|---|---|
| a uuid | the sale, the quote it settled from, or any wallet it sold |
| anything else | the buyer reference, or the address of a wallet sold, by prefix and case-insensitively |
The match reaches into the wallets an order contained, not only the fields on
the row, so an address is enough to find the order it was sold in. buyer_ref
stays an exact filter and the two combine.
GET /quotes/abandoned takes the same search, resolved the same way, so one
search box can cover both listings without meaning two different things.
Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Query Parameters
Filter purchases (settled sales) to a single buyer reference.
nullint6450int640Find one purchase from an identifier: a sale, quote or wallet uuid, a wallet address, or part of a buyer reference.
nullResponse Body
application/json
curl -X GET "https://example.com/purchases"[ { "buyer_ref": "string", "chain": "string", "cost_basis_total": "string", "id": "string", "is_internal": true, "margin_total": "string", "org_id": "string", "our_price_total": "string", "payment_tx_hash": "string", "profit_total": "string", "quote_id": "string", "settled_at": "string", "sweep_tx_hash": "string", "user_price_total": "string", "wallet_count": 0 }]What was in an order
GET /purchases/{id}/wallets returns the line items: one row per wallet, with
the address, the price it went out at, and how old it was on the day of the
sale.
This endpoint deliberately returns metadata only. Private keys come from
GET /wallets/{id}/key or from the originating quote's
/keys call. Key material never rides along in a listing.
Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Path Parameters
Response Body
application/json
curl -X GET "https://example.com/purchases/string/wallets"[ { "age_days_at_sale": "string", "our_price": "string", "pubkey": "string", "user_price": "string", "wallet_id": "string" }]The orders that did not close
Most quotes never become sales. Someone opened a basket and walked away, a
transfer arrived too late, a lot was cancelled. GET /quotes/abandoned is the
record of those: expired, cancelled and failed quotes by default, or whatever
you pass in statuses.
It is the endpoint behind a "recover abandoned baskets" view. What was in the basket, what it would have cost, when it lapsed.
Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Query Parameters
int6450int640Find one quote from an identifier: a quote or wallet uuid, a wallet address, or part of a buyer reference.
nullResponse Body
application/json
curl -X GET "https://example.com/quotes/abandoned"[ { "buyer_ref": "string", "chain": "string", "completed_at": "string", "created_at": "string", "hard_expiry": "string", "id": "string", "margin_total": "string", "org_id": "string", "our_price_total": "string", "pay_address": "string", "payment_tx_hash": "string", "reserved_until": "string", "status": "string", "updated_at": "string", "user_price_total": "string", "wallet_count": 0 }]Which wallets a quote had held
GET /quote/{id}/wallets returns the wallets a quote reserved, from a snapshot
taken at reservation time. That snapshot is the point. It survives expiry and
cancellation, and it does not change when the wallets go back to the catalog and
get sold to someone else.
So for a settled quote this tells you what you bought, and for an abandoned one it tells you what you nearly bought, which is exactly what you need to rebuild the basket.
Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Path Parameters
Response Body
application/json
curl -X GET "https://example.com/quote/string/wallets"[ { "age_days_at_reserve": "string", "balance": "string", "our_price": "string", "pubkey": "string", "user_price": "string", "wallet_id": "string" }]Recovering a customer's orders in one call
GET /orders?buyer_refs=a,b,c returns, for up to a hundred buyer references at
once, every quote placed under each of them together with the keys delivered on
the settled ones. It is the endpoint behind "resend this customer their
wallets": one call, the whole history, keys included where there are any.
Keys ride along here, unlike on the listings above, because the call exists for
re-delivery. Fetch it server-side and treat the response like you treat
GET /quote/{id}/keys.
Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Query Parameters
Comma-separated buyer references (order codes), at most 100 per call.
Response Body
application/json
curl -X GET "https://example.com/orders?buyer_refs=string"[ { "buyer_ref": "string", "keys": [ { "chain": "string", "private_key_hex": "string", "pubkey": "string", "purged_at": "string", "wallet_id": "string" } ], "quote": { "buyer_ref": "string", "chain": "string", "completed_at": "string", "created_at": "string", "expected_amount": "string", "hard_expiry": "string", "id": "string", "items": [ { "balance": "string", "our_price": "string", "token_holdings": [ { "balance": "string", "decimals": 0, "mint": "string", "post_sale": true, "sellable_balance": "string", "sellable_usd_value": 0, "symbol": "string", "ui_multiplier": "string", "usd_value": 0 } ], "user_price": "string", "wallet_id": "string" } ], "margin_total": "string", "org_id": "string", "our_price_total": "string", "pay_address": "string", "pay_checkout_url": "string", "pay_intent_id": "string", "payment_status": "string", "payment_tx_hash": "string", "received_amount": "string", "reserved_until": "string", "sell_tokens_after_purchase": true, "status": "string", "topup_allocations": [ { "amount": "string", "wallet_id": "string" } ], "topup_max": "string", "topup_min": "string", "topup_per_wallet": "string", "topup_plan_id": "string", "topup_required": "string", "topup_total": "string", "total_due": "string", "user_price_total": "string" } }]Being told instead of asking
If your organisation has a webhook registered, settlement delivers a
sale.completed payload to it:
{
"event": "sale.completed",
"sale_id": "…",
"quote_id": "…",
"org_id": "…",
"chain": "solana",
"user_price_total": "…",
"margin_total": "…"
}Webhook registration is not self-serve today, so ask us to set one up. Until
then, polling GET /quote/{id} until it reads settled is the supported way to
find out.
Last updated on