Organisation
Markup, credentials, people, and the numbers on your dashboard.
Everything on this page is administration of your own organisation. All of it
needs an organisation admin signed in as a user, since
API keys do not qualify.
The exceptions are GET /config and GET /stats, which any credential can
read.
Configuration
GET /config returns six fields.
| Field | Meaning |
|---|---|
markup_tiers | your resale markup, one rate per wallet tier |
markup_locked | whether you may change it |
rate_limit_rpm | your requests-per-minute allowance |
is_internal | whether this is a platform-internal organisation |
private_marketplace | whether you can run your own funding plans and sell from your own inventory |
funding_fee_bps | the platform fee you pay on each of those plans, in basis points |
markup_tiers always carries the four tiers, ordered tier0 to tier3, each
with its own markup_pct. The platform's own margin has always varied with the
tier, and yours can too: a tier3 wallet is worth more to hold than a tier0
one, and can carry a different rate.
PUT /config changes the markup and nothing else. It takes either form.
// one rate for the whole catalog, applied to all four tiers
{ "markup_pct": "0.25" }
// tier by tier; the tiers you leave out keep their rate
{ "markup_tiers": [
{ "tier": "tier0", "markup_pct": "0.15" },
{ "tier": "tier3", "markup_pct": "0.40" }
] }Passing both is a 400, and so is passing neither, because the two would
disagree about what tier0 should be and the API will not pick a winner. The
call is all-or-nothing: one rate it refuses leaves every tier exactly as it was,
including the ones listed before it.
If markup_locked is true, meaning your pricing has been fixed for you, the
call returns 403 with markup is locked by admin. The lock covers the whole
set at once. Reading markup_locked before offering the control is the
friendlier way to handle that.
The markup feeds straight into catalog pricing. It is one of the two components
sitting on top of a wallet's base price, alongside the platform's own margin for
that wallet's tier. Change a tier's rate and every user_price in the catalog
for that tier moves with it.
Authorization
bearerAuth 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/config"{ "funding_fee_bps": 0, "is_internal": true, "markup_locked": true, "markup_tiers": [ { "markup_pct": "string", "tier": "string" } ], "private_marketplace": true, "rate_limit_rpm": 0}Authorization
bearerAuth 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 PUT "https://example.com/config" \ -H "Content-Type: application/json" \ -d '{}'{ "funding_fee_bps": 0, "is_internal": true, "markup_locked": true, "markup_tiers": [ { "markup_pct": "string", "tier": "string" } ], "private_marketplace": true, "rate_limit_rpm": 0}API keys
Covered under Authentication: creating one, the fact that the secret is shown exactly once, and revoking it.
People
Users belong to one organisation and hold one of two roles, org_admin or
org_member. Admin is what unlocks this page.
There is no sign-up form. You invite an address, they get an email, they
register with the token in it. POST /users/invite defaults to org_member
when no role is given.
PUT /users/{id}/status flips a user between active and disabled. A
disabled user's tokens stop working on the next request, because the check is on
the account rather than on the token, so there is no window to wait out.
Pending invitations live at GET /invitations until they are accepted, and
DELETE /invitations/{id} withdraws one that should not have gone out.
Authorization
bearerAuth 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/users"[ { "created_at": "string", "email": "string", "id": "string", "last_login_at": "string", "name": "string", "org_id": "string", "role": "string", "status": "string", "timezone": "string" }]Authorization
bearerAuth 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/users/invite" \ -H "Content-Type: application/json" \ -d '{ "email": "string" }'{ "accepted_at": "string", "created_at": "string", "email": "string", "expires_at": "string", "id": "string", "org_id": "string", "role": "string", "status": "string"}Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Path Parameters
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
curl -X PUT "https://example.com/users/string/status" \ -H "Content-Type: application/json" \ -d '{ "status": "string" }'{ "ok": true}Authorization
bearerAuth 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/invitations"[ { "accepted_at": "string", "created_at": "string", "email": "string", "expires_at": "string", "id": "string", "org_id": "string", "role": "string", "status": "string" }]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 DELETE "https://example.com/invitations/string"{ "ok": true}Dashboard numbers
GET /stats is the summary: how many orders you have placed, how many wallets
you hold, how many quotes are open right now, and your spend, margin and balance
broken down per chain.
Two finer cuts sit next to it. GET /stats/sales sums your settled sales per
day and per chain, with the days cut in your timezone (your profile's, tz to
override, UTC for an API key), over the last days days. GET /stats/buyers
sums them per buyer reference and chain instead, which is the "who buys the
most" view. Both carry sales, wallets, user_price_total and margin_total,
in base units per chain, as everywhere.
The per-chain breakdown is not an accident. Base units mean different things on
different chains, so a single cross-chain total would be meaningless. Price each
chain's native token with GET /prices and sum in USD if you want one number.
Authorization
bearerAuth 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/stats"{ "active_quotes": 0, "balances": [ { "amount": "string", "chain": "string" } ], "margin_by_chain": [ { "amount": "string", "chain": "string" } ], "spent_by_chain": [ { "amount": "string", "chain": "string" } ], "total_purchases": 0, "wallets_bought": 0}Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Query Parameters
How many days back to include. Omit for all-time.
int64nullIANA zone the day buckets are cut in. Defaults to the caller's profile timezone; API-key callers have none and get UTC.
nullResponse Body
application/json
curl -X GET "https://example.com/stats/sales"{ "buckets": [ { "bucket": "string", "chain": "string", "cost_basis_total": "string", "is_internal": true, "margin_total": "string", "org_id": "string", "our_price_total": "string", "profit_total": "string", "sales": 0, "user_price_total": "string", "wallets": 0 } ], "timezone": "string"}Authorization
bearerAuth JWT from /auth/login (users) or an org API key (mk_...) for integrations.
In: header
Query Parameters
How many days back to include. Omit for all-time.
int64nullIANA zone the day buckets are cut in. Defaults to the caller's profile timezone; API-key callers have none and get UTC.
nullResponse Body
application/json
curl -X GET "https://example.com/stats/buyers"[ { "buyer_ref": "string", "chain": "string", "last_sale_at": "string", "margin_total": "string", "sales": 0, "user_price_total": "string", "wallets": 0 }]Last updated on