API reference
The frozen v1 contract: REST and gRPC into the OMS, order streams over WebSocket, and a separate market-data feed. This page is the tour; the repo holds the letter of the law.
/api/v2) and a deprecation window. The machine-readable spec is openapi.yaml; the full prose contract is docs/API.md.Wire surfaces
| Surface | Transport | Port | Purpose |
|---|---|---|---|
| Order API | REST | 8080 | Orders, balances, history, positions |
| Order API | gRPC | 9090 | Same domain, plus server-side streams |
| Order events | WebSocket /ws | 8080 | Live order updates for your own orders |
| Market data | WebSocket /ws | 8081 | Books, trades, ticker, candles, for everyone |
| Admin ops | REST | 8082 | Cluster lifecycle and operations |
Money and ids
Two rules prevent a whole category of bugs:
- Money is a decimal string. Prices, quantities, amounts, and balances cross the wire as strings with up to 8 decimal places:
"110224.00000000". JSON numbers are IEEE doubles and silently corrupt exact values; strings round-trip bit-exactly. Responses always carry exactly 8 decimals. - Big ids are strings too.
omsOrderIdandtradeIdare 64-bit ids serialized as JSON strings ("331884039403208704"): they exceed JavaScript's exact-integer range. Treat them as opaque. Small ids (userId,marketId) stay numbers.
{ "price": "110224.00000000", "quantity": "0.10000001", "omsOrderId": "331884039403208704" }Authentication
Authorization: Bearer <token> on every call; identity always comes from the token, never from the body. Providers: api-key (default), jwt, dev (local only). Browser WebSockets pass the token as the second Sec-WebSocket-Protocol offer; query-string tokens are rejected. Details in security.
REST endpoints
Base: http://<oms>:8080. The ones you'll use daily:
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/orders | Create an order |
PUT | /api/v1/orders/{id} | Cancel-and-replace price/quantity |
DELETE | /api/v1/orders/{id} | Cancel |
GET | /api/v1/orders/{id} | Fetch one order (terminal ones come from persistence) |
GET | /api/v1/orders?status= | Your active orders |
GET | /api/v1/orders/history | Order history, newest first |
GET | /api/v1/executions | Your fills, newest first |
GET | /api/v1/positions | Net position per market, from executions |
GET | /api/v1/accounts/{userId} | Balances |
POST | /api/v1/accounts/{userId}/deposit | Credit a balance |
POST | /api/v1/accounts/{userId}/withdraw | Debit; rejected if it exceeds available |
GET | /api/v1/markets | Market list |
GET | /api/v1/health | Liveness (no auth) |
Order lifecycle: PENDING_RISK → PENDING_HOLD → PENDING_NEW → NEW → PARTIALLY_FILLED → FILLED | CANCELLED | REJECTED (stop and trailing orders add PENDING_TRIGGER). filledQty always derives from the authoritative trade stream.
Order types: limit, market, post-only (limit-maker), stop-loss, stop-limit, trailing-stop, iceberg. Time-in-force: GTC, GTD, IOC, FOK.
Errors and rejections
Two different things, deliberately kept apart:
- API errors are HTTP errors with a stable shape:
{ "error": "...", "code": "VALIDATION" }. Codes:VALIDATION,REJECTED,UNAUTHORIZED,FORBIDDEN,NOT_FOUND,UNAVAILABLE,INTERNAL. - Order rejections are business outcomes, not transport errors: a normal response with
accepted: falseand arejectReasonfrom a fixed vocabulary (INSUFFICIENT_BALANCE,PRICE_COLLAR_BREACH,RATE_LIMIT_EXCEEDED,MARKET_HALTED, and friends).
Idempotency
Pass a clientOrderId (up to 64 chars, your choice) when creating an order. Retrying the same request after a lost response returns the existing order with duplicate: true instead of creating a double. The dedupe window survives OMS restarts. Once the order is terminal, the id is free to reuse.
Streams: WebSocket and gRPC
Order events (/ws on 8080): subscribe with {"action":"subscribe","channel":"orders","userId":N} and receive the same order JSON as REST, pushed on every state change.
gRPC (9090): OrderService (create, cancel, get, history, trades, plus StreamOrders/StreamExecutions) and AccountService. Same money-as-strings rule; auth via the authorization metadata key. Proto lives in oms-api.
Market data plane
The market gateway (ws://:8081/ws) streams book snapshots and deltas, trades, ticker, candles, and cluster status to all comers. One caveat worth knowing: its money values are JSON numbers, display-grade by design. The OMS plane is the authoritative one for money.
Stability policy
Frozen since v0.3.0-beta: no removals, renames, or type changes without a major API version and a one-minor-release deprecation window. Additive changes (new fields, endpoints, enum values) can land in any minor release, so write clients that tolerate unknown fields. The precise policy text is at the bottom of API.md.