Hushxima
Marketplace

Wallets & keys

Your inventory after the sale. Retrieving keys, the format they come in, and tracking who each wallet is for.

Once an order settles, the wallets are yours. They keep their whole history (balance, age, transaction count, tier, persona) and gain three fields: the sale they came from, the price they went out at, and a buyer_ref if you set one.

Your inventory

GET /wallets lists the wallets your organisation owns. It is cursor-paged and filterable by chain and buyer_ref. Each row carries key_purged_at, null while the platform still holds the wallet's key and set once it no longer does, so a list can show which wallets can still be exported without asking for each key.

GET
/wallets

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Query Parameters

buyer_ref?string|null
chain?string|null
cursor?|

Opaque keyset cursor from a prior response's next_cursor. When set, returns the page strictly after it.

Defaultnull
limit?integer
Formatint64
Default50

Response Body

application/json

curl -X GET "https://example.com/wallets"
{  "items": [    {      "aging_status": "string",      "balance": "string",      "buyer_ref": "string",      "chain": "string",      "cost_basis": "string",      "created_at": "string",      "first_tx_at": "string",      "funded_at": "string",      "funding_exchange": "string",      "funding_source": "string",      "id": "string",      "imported": true,      "inventory_org_id": "string",      "key_purged_at": "string",      "last_tx_at": "string",      "network": "string",      "owner_org_id": "string",      "persona_name": "string",      "pubkey": "string",      "sale_id": "string",      "sold_at": "string",      "source_kind": "string",      "status": "string",      "target_age_days": 0,      "tier": "string",      "timezone_name": "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        }      ],      "tx_count": 0,      "user_price": "string"    }  ],  "next_cursor": "string"}

Price breakdown

A wallet is not priced from a list. It is priced from what it cost to produce, and two margins are applied on top of that: ours, then yours.

The anchor: what went into the wallet

cost_basis is the native currency the platform funded the wallet with to create it and age it. It is the floor of everything below: a wallet is never sold for less than the money sitting in it.

The wallet's live balance can raise that anchor but never lower it, so the base is whichever of the two is higher.

base = max(cost_basis, balance)

Read that as a floor rather than as a formula. Almost every listed wallet has spent a good part of its native balance on the activity that makes it worth buying, and the anchor is what keeps it priced at what went in rather than at what is left. The other side is the exception: a wallet whose balance actually grew past its funding is priced on what it now holds, so a wallet that made money is not handed over at cost.

Our margin, which varies with the wallet

On top of that base the platform takes a margin that depends on the wallet's tier, the quality band it was aged under. tier0 is funded and nothing more; the higher bands carry real activity, held positions, and cross-chain history, all of which take longer to build and are worth more to hold. A tier3 wallet therefore carries a different rate from a tier0 one, on the same base.

our_price = base × (1 + platform margin for that tier)

The rate is configured per tier and per organisation, so this is your price rather than a public one. Nothing in the API lets you change it; it is what the platform has agreed with you.

Your margin, which is yours

On top of our price, your organisation applies whatever markup it wants, and it varies with the tier the same way ours does.

user_price = our_price × (1 + your markup for that tier)
margin     = user_price − our_price

Your four rates live on your organisation config as markup_tiers and you set them with PUT /config, one at a time or all at once, unless they have been fixed for you. See Organisation. The platform does not cap them. What you resell at is your business.

FieldWhat it is
cost_basiswhat the platform funded the wallet with
our_pricethe platform's price for you: base plus the tier margin
user_pricewhat the order charges: our_price plus your markup
marginthe difference, which is yours

A quote totals these into our_price_total, user_price_total and margin_total. total_due, the number the payment address waits for, is built from user_price_total. All of them are base units, on the quote's chain.

Your margin does not need collecting. When the order settles, margin_total is credited to your organisation's balance on that chain, as one ledger entry per order, and you withdraw it when you like. See Balance.

A top-up bought with the order is not part of this. It sits in topup_required, is passed through at cost plus the funding fee, and earns no margin either way. That is why total_due and user_price_total are two different numbers.

Catalog prices are computed with exactly this arithmetic at the moment you ask, so the figure you display is the figure the order charges. On a wallet you already own, user_price means the price actually paid, frozen at settlement.

Getting a key

GET /wallets/{id}/key returns one wallet's private key. It works for any wallet you own, whichever order it came from, which makes it the endpoint to use long after the purchase. GET /quote/{id}/keys is the other one: it hands back a whole lot at once, and it is what you call right after settlement.

Both are idempotent and neither consumes anything. For what happens to a key while we are still holding it, see Security.

GET
/wallets/{id}/key

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

id*String

Response Body

application/json

curl -X GET "https://example.com/wallets/string/key"
{  "chain": "string",  "private_key_hex": "string",  "pubkey": "string",  "purged_at": "string",  "wallet_id": "string"}

Key formats

The field is called private_key_hex, and on Solana it is not hex. The encoding follows what each ecosystem's wallets actually import.

ChainEncoding
solanabase58 of the full 64-byte ed25519 keypair (seed ‖ pubkey), the format Phantom, Solflare and solana-keygen produce and accept
ethereum, base, bsc, robinhoodhex of the 32-byte secp256k1 key

Solana wallets reject hex, and they reject the bare 32-byte seed too, so there was not much of a choice. Read the chain field on the response and decode accordingly.

When the key is gone

If your organisation has opted into key retention, we stop holding the keys of the wallets you buy a set number of minutes after the sale. Past that window the response still comes back, but with private_key_hex: null and purged_at set to the moment it was dropped.

This is off by default, and turning it on is a one-way door for each wallet. Past the window we cannot hand the key back, because we do not have it. Make sure your own custody is in place before asking for it.

Deleting keys yourself

You do not have to wait for a policy. DELETE /wallets/{id}/key purges one wallet's key on the spot, and POST /wallets/keys/purge does it for up to five hundred wallet ids in one call. Both only touch wallets your organisation owns, and both leave everything else about the wallet alone: it stays in your inventory, keeps its history and its buyer reference, it just can no longer be exported from here.

The bulk call reports one outcome per id rather than failing as a whole:

outcomeMeaning
purgedthe key is gone
already_purgedit was gone before this call
farmingthe platform still farms this wallet for you and needs the key; stop the farm first
not_ownednot your wallet, or no such wallet, and nothing was touched

The single-wallet route answers the same situations with status codes: 409 for a wallet still being farmed, 404 for one that is not yours.

Same one-way door as retention. Export the key first, then delete it.

DELETE
/wallets/{id}/key

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

id*String

Response Body

application/json

curl -X DELETE "https://example.com/wallets/string/key"
{  "outcome": "string",  "wallet_id": "string"}
POST
/wallets/keys/purge

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Wallets whose private keys the org wants purged from the platform.

Response Body

application/json

curl -X POST "https://example.com/wallets/keys/purge" \  -H "Content-Type: application/json" \  -d '{    "wallet_ids": [      "string"    ]  }'
{  "purged": 0,  "results": [    {      "outcome": "string",      "wallet_id": "string"    }  ]}

Selling a wallet out of its tokens

An aged wallet arrives holding whatever positions its history gave it. They are part of what makes it look lived-in, and once it is yours they are your positions. POST /wallets/{id}/sell-all unwinds them: one swap per token, back to the chain's native asset.

Nothing is transferred out. The proceeds land on the wallet that held the tokens, which is the only place they can go without handing you a second address to reconcile. What comes back is one operation id per token found, queued rather than done.

The same wallet is addressable by its public key, which is the identifier you are likelier to have in hand once the keys have been exported: POST /wallets/by-pubkey/{pubkey}/sell-all. An address that does not exist and an address that belongs to another organisation both answer 404.

To sell the whole lot at once, ask for it at purchase time instead, with sell_tokens_after_purchase on the quote. Same mechanism, applied to every wallet in the order the moment it settles.

A wallet whose key we no longer hold, under key retention, is refused with 409. We cannot sign for it any more. Sell it out before the window closes, or do it yourself with the key you exported.

POST
/wallets/{id}/sell-all

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

id*String

Response Body

application/json

curl -X POST "https://example.com/wallets/string/sell-all"
{  "chain": "string",  "operation_ids": [    "string"  ],  "tokens": 0,  "wallet_id": "string"}
POST
/wallets/by-pubkey/{pubkey}/sell-all

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

pubkey*String

Response Body

application/json

curl -X POST "https://example.com/wallets/by-pubkey/string/sell-all"
{  "chain": "string",  "operation_ids": [    "string"  ],  "tokens": 0,  "wallet_id": "string"}

What is still in there

GET /wallets/{id}/tokens, or /wallets/by-pubkey/{pubkey}/tokens, reads the chain at the moment you ask and lists what the wallet still holds, each entry with its mint, symbol, raw balance, decimals and a live usd_value when the mint is priced. has_tokens is the short answer.

It also tells you what is already on its way out, which is the difference between "nothing left to sell" and "the sale has not landed yet":

FieldWhat it means
has_queued_sella sell-out queued earlier has not settled
has_queued_withdrawa withdrawal is in flight for this wallet
queued_operationsthe operations behind those two, with their kind, status and mint

A wallet can list tokens and carry has_queued_sell: true at the same time. The swap is queued, not done. Poll rather than queueing a second sell-out.

Not everything a wallet holds is ours to sell. A sell-out unwinds what farming left on the wallet, not positions its owner opened after taking delivery. Each holding therefore carries sellable_balance and sellable_usd_value, the share a sell-out would actually sell, and post_sale: true marks a token that appeared after the wallet changed hands. has_sellable_tokens on the response is the short answer to "is there anything left for a sell-out to do".

On Solana the listing is exhaustive, since a wallet's token accounts can be read straight from the chain. EVM chains offer no equivalent, so the balances checked there are those of the tokens we know the wallet to have held. An unusual token can fall outside that set, in which case it is neither listed here nor sold by a sell-out.

GET
/wallets/{id}/tokens

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

id*String

Response Body

application/json

curl -X GET "https://example.com/wallets/string/tokens"
{  "chain": "string",  "count": 0,  "has_queued_sell": true,  "has_queued_withdraw": true,  "has_sellable_tokens": true,  "has_tokens": true,  "pubkey": "string",  "queued_operations": [    {      "created_at": "string",      "id": "string",      "kind": "string",      "mint": "string",      "status": "string",      "to_address": "string",      "tx_hash": "string"    }  ],  "sellable_usd_value": 0,  "tokens": [    {      "balance": "string",      "decimals": 0,      "mint": "string",      "post_sale": true,      "sellable_balance": "string",      "sellable_usd_value": 0,      "symbol": "string",      "ui_multiplier": "string",      "usd_value": 0    }  ],  "total_usd_value": 0,  "wallet_id": "string"}
GET
/wallets/by-pubkey/{pubkey}/tokens

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

pubkey*String

Response Body

application/json

curl -X GET "https://example.com/wallets/by-pubkey/string/tokens"
{  "chain": "string",  "count": 0,  "has_queued_sell": true,  "has_queued_withdraw": true,  "has_sellable_tokens": true,  "has_tokens": true,  "pubkey": "string",  "queued_operations": [    {      "created_at": "string",      "id": "string",      "kind": "string",      "mint": "string",      "status": "string",      "to_address": "string",      "tx_hash": "string"    }  ],  "sellable_usd_value": 0,  "tokens": [    {      "balance": "string",      "decimals": 0,      "mint": "string",      "post_sale": true,      "sellable_balance": "string",      "sellable_usd_value": 0,      "symbol": "string",      "ui_multiplier": "string",      "usd_value": 0    }  ],  "total_usd_value": 0,  "wallet_id": "string"}

Buyer references

buyer_ref is a free-text label you attach to wallets and orders. Nothing in the API interprets it. It exists so you can answer "which of my customers has this wallet". You can set it at quote creation, at settlement, or afterwards.

Four endpoints manage it after the fact:

  • PUT /wallets/{id}/owner sets or clears it on one wallet.
  • POST /wallets/reassign moves every wallet from one reference to another and returns how many moved.
  • POST /wallets/unassign clears one reference across the board.
  • GET /wallets/users lists the distinct references currently in use, which is the raw material for a customer picker.
PUT
/wallets/{id}/owner

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Path Parameters

id*String

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X PUT "https://example.com/wallets/string/owner" \  -H "Content-Type: application/json" \  -d '{}'
{  "ok": true}
POST
/wallets/reassign

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/wallets/reassign" \  -H "Content-Type: application/json" \  -d '{    "from_buyer_ref": "string",    "to_buyer_ref": "string"  }'
{  "count": 0}
POST
/wallets/unassign

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/wallets/unassign" \  -H "Content-Type: application/json" \  -d '{    "buyer_ref": "string"  }'
{  "count": 0}
GET
/wallets/users

Authorization

bearerAuth
AuthorizationBearer <token>

JWT from /auth/login (users) or an org API key (mk_...) for integrations.

In: header

Response Body

application/json

curl -X GET "https://example.com/wallets/users"
"string"

Putting money in them

Wallets bought here arrive with whatever balance they had. To add native currency, to them or to any address you own elsewhere, see Top up wallets.

Last updated on

On this page