Hushxima
Marketplace

Conventions

Amounts, chains, paging and errors. The handful of rules that hold across every endpoint.

Amounts are integers, always

Every monetary value in this API, request or response, is a whole number of the chain's smallest unit, carried as a JSON string.

ChainUnit1 native token
solanalamport1000000000
ethereum, base, bsc, robinhoodwei1000000000000000000

They are strings because they do not fit in a JavaScript number, and integers because the alternative is worse.

Decimals are rejected, not scaled. Sending "0.2" where the API wants lamports gets you a 400, not two hundred million lamports. No parser in this API will quietly turn a decimal into base units, because that is exactly where an off-by-10⁹ funding mistake comes from. 0.2 SOL is "200000000".

For anything user-facing you will want USD. GET /prices returns the current USD price of each chain's native token, and you do the conversion on your side.

GET
/prices

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/prices"
{  "base": 0,  "bsc": 0,  "ethereum": 0,  "robinhood": 0,  "solana": 0}

Chains

solana, ethereum, base, bsc, robinhood. Solana runs on SVM semantics, the other four on EVM. Where the behaviour differs, which is mostly around funding, the page in question says so.

A chain identifier is always lowercase. An unknown one comes back as 400 bad_request with the offending value quoted.

Timestamps

RFC 3339, UTC, as strings: "2026-02-14T09:31:07Z". The one exception is gap_with on the catalog, which takes Unix epoch seconds because it is a comma-separated list and readability lost that argument.

Pagination

Two schemes, and which one you get depends on how the underlying list behaves.

Cursor paging on /catalog and /wallets, where rows move under you. Ask for a page, get back items and a next_cursor, then pass that cursor to get the page strictly after it. A null cursor means you have reached the end. The cursor is opaque, so do not build one yourself.

GET /catalog?chain=solana&count=50
GET /catalog?chain=solana&count=50&cursor=<next_cursor from the previous page>

Offset paging on /quotes, /purchases, /quotes/abandoned and /funding/plans, which are your own records and hold still. limit and offset, both clamped server-side.

Errors

Every failure uses the same envelope:

{
  "error": {
    "code": "conflict",
    "message": "insufficient inventory: requested 50, available 31"
  }
}
StatuscodeTypically means
400bad_requesta malformed value: a decimal amount, a bad UUID, an unknown chain
401unauthorizedmissing, expired, or wrong-scope token
403forbiddenauthenticated, but not allowed. Org admin required, or org suspended
404not_foundno such record for your organisation
409conflictthe state moved. Quote already settled, inventory gone, balance too low
422unprocessablea payment problem
429rate_limitedover your organisation's requests-per-minute
500internalour fault, and the message is deliberately generic

404 deserves a note. Scoping happens before existence, so a quote belonging to another organisation is reported as not found rather than forbidden. Telling you it exists would already be telling you something.

The 2FA variant

Some sensitive actions answer 401 with an extra X-2FA-Required response header and a richer body:

{
  "error": {
    "code": "two_fa_required",
    "message": "two-factor code required",
    "challenge_id": "…",
    "method": "email",
    "label": "…",
    "sent_to": "o…r@example.com"
  }
}

It shares a status code with "your session is gone", which is why the header is there. Check it before you tear down the session. On the organisation surface this only shows up for accounts that have enrolled a second factor.

Idempotency and retries

There is no Idempotency-Key header. Where repeating a call would be dangerous, the endpoint is idempotent by construction instead:

  • GET /quote/{id}/keys returns the same keys however many times you ask.
  • POST /funding/plans/{plan_id}/start reports the current state rather than starting a second time.
  • Settlement is driven by the on-chain balance, so paying and then calling POST /quote/{id}/settle twice settles once.

Where it would not be dangerous but also not free, as with POST /quote and POST /funding/plans, a retry creates a second record. Reconcile with GET /quotes or GET /funding/plans rather than blindly re-sending.

Last updated on

On this page