Access is invite-only. Contact our team to get a broker key.
Trust model
VOOI is authentication-agnostic by design. You — the broker — are the sole authority over user identity. Whatever auth method your users see (wallet signature, email, SSO, Passkeys, or carrier pigeon) lives entirely in your app; VOOI neither implements nor sees those flows. The contract between your backend and VOOI is one Ed25519 keypair per user. You generate or collect it however fits your auth model, register the public half with VOOI, and VOOI then executes for whoever signs with the matching private key. VOOI has no opinion about who that signer is. Two consequences to surface to your own users:- You can fully impersonate any of your users.
PUT /broker/users/public-keylets you rotate any of your users’ keys to one you control, after whichPOST /user/tokensauthenticates as them. This is a direct consequence of you being the identity authority, not a gap — users trusting your app are trusting you accordingly. - Mapping your external identities to
userIds is your job. VOOI exposes no lookup by wallet address, email, public key, or anything other thanuserIditself. Persist theuserIdagainst whatever external identity your auth flow produces; on reconnect or re-login, reuse it rather than re-registering.
What brokers can and can’t do
Brokers are read-only on statistics and write-only on public keys:- Can register users, set or rotate their Ed25519 public keys, configure per-exchange builder / integrator defaults, and pull their trading statistics (orders and trades).
- Cannot see user session tokens, private keys, wallet addresses, account balances, positions, or any data a user holds privately.
Flow
The end-to-end flow for onboarding and signing in a user:1
Obtain an Ed25519 keypair for the user
Derive it from a wallet signature, generate it server-side, or let the user generate and keep it — whichever fits your auth model (see Trust model). You only need the public half for registration.
2
Register the user
Your backend calls
POST /broker/users with the public key. VOOI returns a userId (UUID) that belongs to the user forever.3
(Optional) Rotate the public key later
Call
PUT /broker/users/public-key whenever the user regenerates their keypair. Rotation automatically revokes that user’s unnamed session tokens.4
User signs requests and obtains a token
The user signs a message with the private key and calls
POST /user/tokens to get a vooi_… bearer token. See API Tokens for the signature format.5
User trades; broker observes
The user places orders, opens positions, and trades against VOOI directly. Your backend reads their statistics from
GET /broker/orders and GET /broker/trades.Quickstart
Authenticate every/broker/* request with your broker key in the X-Broker-Key header. Keep this key server-side — never ship it in a client app.
1. Register a user
id — it’s the user’s permanent UUID:
Registration is not idempotent. Every successful
POST /broker/users call returns a new userId. Dedupe on your side: map whatever identity your auth flow produces (wallet address, email, SSO subject, …) to the userId VOOI returned, and only call once per user. On reconnect or re-login, look up the existing userId from your mapping and reuse it — VOOI has no recovery path if you lose it.publicKey is optional in the request. A user registered without one cannot mint tokens — POST /user/tokens returns 400 Public key not set until you attach one with the PUT call below.2. Rotate the public key (when needed)
204 No Content on success. Pass "publicKey": null to clear it (the user will not be able to mint new tokens until a key is set again). Submitting the user’s existing key is a no-op — no tokens are touched.
Rotation affects that user’s outstanding tokens as follows:
- Session tokens (unnamed) are deleted. The next request carrying one gets
401 Invalid or expired session. - Named tokens are left alone. They keep working until their own
expiresAt. If you need a hard re-auth, enumerate them with the user’s bearer viaGET /user/tokensand delete each one. - Historical orders, trades, and positions are untouched. Rotation only changes signature verification for future
createToken/deleteTokencalls.
3. Read user activity
All broker read endpoints use cursor pagination and share a{ cursor, items } envelope — cursor is null on the final page; pass it back unchanged on the next request.
GET /broker/ordersandGET /broker/trades— filters:userId,exchange,from,to,cursor,limit(1–1000, default 100). Ordered newest first; ties are broken deterministically so cursor paging stays stable. When bothfromandtoare set, the window must not exceed 30 days.
4. Read statistics
Several endpoints cover stats — they answer different questions:GET /broker/stats and GET /broker/users/stats require from and to as Unix milliseconds, hard-reject with 400 Bad Request if to - from > 30 days, and (for users/stats) reject to set in the future. Ranges crossing the 30-day boundary must be split into multiple calls.
GET /broker/users/top-volume is the long-window companion to users/stats: it takes ISO dates (default the last 30 days, maximum range 180 days, not earlier than 2026-01-01), pages up to limit 1–1000 (default 100), and accepts an optional exchanges filter. Reach for users/stats when you need tradesCount or a strict 30-day executed-volume window, and users/top-volume when you want a deeper leaderboard across a longer period.
The three /broker-statistics/daily-* endpoints take from and to as Unix milliseconds, both inclusive. from defaults to 30 days back (1 day at hour resolution), to defaults to now, and from cannot be earlier than 2026-01-01.
They also take a resolution of day (UTC calendar day, the default) or hour (UTC clock hour). from and to snap to interval boundaries, and hour limits the range to 7 days. Missing intervals come back as amount: 0, so items always has one row per interval in the window.
Optional filters: exchange narrows to one venue, baseSymbolPrefix and baseSymbolNotPrefix keep or drop trades by base-symbol prefix (case-insensitive — useful for isolating a Hyperliquid HIP-3 DEX such as xyz:), and usersStoplist excludes up to 100 user IDs.
Per-user and per-list statistics
The endpoints above aggregate across all of your users or rank them. Two other shapes are available when you have specific users in hand. All of them share thefrom / to / resolution conventions described above.
For one user, by query parameter (userId):
GET /broker-statistics/user-volume— that user’s trade volume and fees, split by exchange. SettotalAccountStats=trueto count the whole exchange account’s volume rather than only volume from orders created through this API.GET /broker-statistics/user-pnl— that user’s realized PnL, split by exchange.GET /broker-statistics/user-volume/chartandGET /broker-statistics/user-pnl/chart— the same figures rolled up per interval, with gaps filled.
usersList of 1–100 entries (each an id plus an optional startedFrom timestamp clamping how far back that user’s trades are counted):
POST /broker-statistics/users-volume— per-user executed notional, broker fees, and trade fees, split by exchange.POST /broker-statistics/users-volume/chart— the same figures rolled up per interval across the whole list, with gaps filled.
exchanges array to count trades only on those venues. Excluded venues still appear in the response with zero amounts.
Broker vs user read scope
Brokers read only what these endpoints return: aggregate and per-day stats, per-user stats, and (userId-filterable) order / trade history. Per-user positions, balances, open orders, and account settings are not exposed on the broker API — that data lives behind the user’s bearer token on the/exchange routes.
Configure exchange defaults
Each venue passes a “broker” record on every order — a builder address (or integrator account index for Lighter and Robinhood) plus fees in basis points. Binance and MEXC are the exceptions: their broker records are attribution identifiers only, with no fee fields. Brokers can register one default per venue so that orders from any of their users automatically carry those values. When a default is set, it overrides the per-orderbroker field on POST /exchange/orders — your users cannot opt out from the client side. Without a default, the order’s own broker is used (or no builder if it was omitted).
Settings are versioned rather than edited in place. Each write adds a record effective from that moment; the most recent record is the one in force, and statistics apply whichever record was active when each order was created. Past figures therefore stay stable when you change your fees.
POST /broker/exchanges— add a settings record for one venue, effective now. Responds201. Body fields:exchange—aster,hyperliquid,lighter,robinhood,extended,binance, ormexc.identifier— the EVM builder address (Aster, Hyperliquid), the integrator account index (Lighter, Robinhood), or, on Extended, the Builder clientId from the Extended UI API page (a positive-integer string, not an EVM address or account index). On Binance,identifieris an attribution tag (up to 24 characters fromA–Z a–z 0–9 . / : _ -) prepended to auto-generated client order IDs. On MEXC it is the Broker_ID MEXC issued you. Neither Binance nor MEXC takes fee fields.marketFeeBps— fee in basis points, as a positive decimal string, charged on orders the venue executes as market orders. Trigger orders count as market on Hyperliquid, Aster, and Ondo.limitFeeBps— the same, for orders the venue executes as limit orders. On Lighter and Robinhood the two are applied as the taker and maker fees respectively.maxFeeBps— fee cap in basis points, used as the default during user-side approval. BothmarketFeeBpsandlimitFeeBpsmust be ≤maxFeeBps; the call returns400otherwise. On Hyperliquid this becomes the cap baked into the user’sapproveBuilderFeesignature, so raising the cap later requires the user to re-sign. On Aster, the fees still cannot exceed the cap baked into the user’s Aster registration.
GET /broker/exchanges— list the settings currently in force. Each row carriesidentifier,marketFeeBps,limitFeeBps, andmaxFeeBps; the fee fields arenullon venues that have no builder fee.DELETE /broker/exchanges/{exchange}— deactivate the venue. Responds204 No Content. The exchange stops being listed byGET /broker/exchanges, new orders carry no builder attribution, and builder approval falls back to caller-suppliedidentifierandmaxFeeBps. Earlier records remain in effect for past statistics.
Approve flow fallback
A user trading through your app only has to run the user-side approve flow when your broker actually uses builder fees on a venue — see Approve a builder fee on Hyperliquid. One pair of endpoints serves all supported venues:POST /exchange/broker/preparebuilds the data the user signs.POST /exchange/broker/executesubmits the signature.GET /exchange/broker/approvedreports whether a given builder/integrator is already approved.
exchange (hyperliquid, lighter, robinhood, or aster) in the body. When a default is configured here, the user-side prepare call can omit identifier and maxFeeBps and picks them up from the broker record; if they are passed explicitly they must match the configured defaults exactly, or the call returns 400. Without a broker default, both fields are required — typically the case for users connecting their own wallets directly.
The signer differs by venue: Hyperliquid and Aster expect an EIP-712 signature; Lighter and Robinhood expect a hex message signed with an Ethereum wallet. For Aster, an account that approved a builder during Aster registration can re-run this flow later to raise the cap or rotate the builder without re-registering.
Every other venue is an exception: none of Extended, Binance, Bybit, MEXC, or Ondo needs a user-side builder-fee approval. Extended’s builder code applies automatically per order, and Binance and MEXC attribution is identifier-only — so the approve endpoints accept only hyperliquid, lighter, robinhood, and aster.
API reference
Full request and response schemas for every broker endpoint are in the API reference.Next steps
End-user API tokens
How your users sign requests and obtain session tokens
Connecting exchanges
Aster, Hyperliquid, Lighter, Robinhood, Extended, Binance, Bybit, MEXC, and Ondo key setup