# Get order book Source: https://docs.numofx.com/api-reference/endpoint/book GET /v1/book # Create order Source: https://docs.numofx.com/api-reference/endpoint/create POST /v1/orders # Cancel order Source: https://docs.numofx.com/api-reference/endpoint/delete POST /v1/orders/cancel # List markets Source: https://docs.numofx.com/api-reference/endpoint/get GET /v1/markets # Get order status Source: https://docs.numofx.com/api-reference/endpoint/order-status GET /v1/orders/{order_id} # Get trades Source: https://docs.numofx.com/api-reference/endpoint/trades GET /v1/trades # Execute match Source: https://docs.numofx.com/api-reference/endpoint/webhook POST /execute # Numo API Source: https://docs.numofx.com/api-reference/introduction REST endpoints and WebSocket streams for the Numo futures orderbook and execution stack. ## Welcome Numo's orderbook stack is split across a public orderbook service and a separate match executor. Use this reference for: * discovering enabled futures markets * reading the current book and recent trades over REST * streaming real-time book, trades, and order updates over the [WebSocket](/api-reference/websocket) * submitting signed orders to the matcher * checking the status of a submitted order * understanding the executor payload that clears matched orders onchain View the `openapi.json` file used to build the API reference. ## Services covered here * `markets-service` * `execution-service` The contracts and risk engine behind those services live in: * `execution-contracts` * `risk-core` ## Orderbook flow Read `GET /v1/markets` to find the canonical market symbol, `asset_address`, `sub_id`, and settlement metadata for instruments like `USDCcNGN-SEP16-2026`. Read `GET /v1/book` and `GET /v1/trades` from `markets-service`, or subscribe to the `book` and `trades` [WebSocket](/api-reference/websocket) channels for real-time updates instead of polling. Post signed order payloads to `POST /v1/orders`, then track fills with `GET /v1/orders/{order_id}` or the authenticated `orders` WebSocket channel. Let the matcher send crossed-order payloads to `POST /execute` on `execution-service`. # WebSocket streams Source: https://docs.numofx.com/api-reference/websocket Real-time book, trades, and order streams from markets-service over a single WebSocket. ## Overview `markets-service` exposes a realtime feed at `GET /v1/ws`. Connect once and multiplex any number of channel subscriptions over the same socket. Every stream starts with a snapshot and then delivers incremental updates, so you never poll `GET /v1/book` or `GET /v1/trades` in a loop. * **Endpoint** — `wss://api.numofx.com/v1/ws` * **Framing** — JSON text frames * **Model** — snapshot, then a strictly increasing stream of deltas you can resume after a reconnect Every server frame that belongs to a market carries the `seq` it reflects. The `snapshot` frame is taken at a specific `seq`; each following `update` is the next `seq`. A gap in the `seq` you receive means you missed an event — resume (see [Resume after reconnect](#resume-after-reconnect)). ## Channels | Channel | Access | Snapshot | Updates | | -------- | --------------------------- | ------------------------------------------ | ---------------------------------------- | | `book` | Public | Top bids and asks (mirrors `GET /v1/book`) | Price-level size changes | | `trades` | Public | Recent fills (mirrors `GET /v1/trades`) | One frame per new fill | | `orders` | **Private** — requires auth | Your open orders | Status and fill changes for your address | The `orders` channel is scoped to the authenticated owner address. You must send a valid `auth` frame before subscribing to it, and you only ever receive your own order events. ## Client frames Send these as JSON text frames. The `op` field selects the operation. ### Subscribe ```json theme={null} { "op": "subscribe", "channel": "book", "market": "USDCcNGN-SEP16-2026" } ``` One of `subscribe`, `unsubscribe`, `auth`, `ping`. `book`, `trades`, or `orders`. Canonical market symbol such as `USDCcNGN-SEP16-2026`, or an `asset_address:sub_id` pair. Required for `book` and `trades`. Optional for `orders`, where it scopes the stream to a single market. Optional. The last `seq` you processed. When present, the server replays events after that point instead of sending a fresh snapshot. See [Resume after reconnect](#resume-after-reconnect). Subscriptions are idempotent — subscribing again to the same `(channel, market)` is a no-op. ### Unsubscribe ```json theme={null} { "op": "unsubscribe", "channel": "book", "market": "USDCcNGN-SEP16-2026" } ``` The server replies with an `ack` frame. ### Ping ```json theme={null} { "op": "ping" } ``` The server replies with `{ "type": "pong" }`. The server also sends protocol-level WebSocket pings every 15 seconds and drops the connection on missed pongs, so most clients do not need to send `ping` themselves. ### Auth Required before subscribing to the private `orders` channel. See [Authentication](#authentication). ```json theme={null} { "op": "auth", "address": "0xC7bE60b228b997c23094DdfdD71e22E2DE6C9310", "signature": "0x…", "nonce": "b3f1…", "issued_at": 1745000000, "expiry": 1745000300 } ``` ## Server frames The `type` field identifies the frame. | `type` | Meaning | | ---------- | -------------------------------------------------------------------------- | | `snapshot` | Full state for a `(channel, market)` at `seq`. Sent first after subscribe. | | `update` | One incremental delta. Its `seq` is greater than the snapshot's. | | `ack` | Acknowledges `auth` or `unsubscribe`. | | `pong` | Reply to a client `ping`. | | `error` | A problem with the last frame or the stream. Carries `code` and `message`. | Frame shape: ```json theme={null} { "type": "update", "channel": "book", "market": "USDCcNGN-SEP16-2026", "seq": 10441, "data": { "…": "…" } } ``` ### Update payloads The `data` on an `update` frame is a compact delta, not the whole book. ```json book theme={null} { "side": "buy", "price_ticks": "1390", "limit_price": "1390", "size_delta": "-0.001", "order_open": "0", "order_id": "…" } ``` ```json trades theme={null} { "trade_id": 4821, "price": "1390", "size": "0.001", "aggressor_side": "buy", "taker_order_id": "…", "maker_order_id": "…", "created_at": "2026-04-01T00:00:00Z" } ``` ```json orders theme={null} { "order_id": "…", "status": "filled", "side": "sell", "filled_amount": "0.001", "desired_amount": "0.001", "limit_price": "1390", "price_ticks": "1390" } ``` The `snapshot` payload mirrors the matching REST response: `book` snapshots use the same shape as `GET /v1/book`, and `trades` snapshots use the trade list from `GET /v1/trades`. ## Subscribe and snapshot consistency On subscribe, the server: Nothing is dropped between the read and the first frame. The snapshot data and its `seq` come from a single consistent read. Buffered deltas with `seq <= boundary` are dropped, so you never double-apply an event already reflected in the snapshot. ## Resume after reconnect Track the highest `seq` you have applied per `(channel, market)`. On reconnect, subscribe with `since_seq` set to that value: ```json theme={null} { "op": "subscribe", "channel": "book", "market": "USDCcNGN-SEP16-2026", "since_seq": 10441 } ``` * If the events after `since_seq` are still retained, the server replays them and goes live — no snapshot, no gap. * If `since_seq` is older than the retention horizon, the server sends `error` with code `resume_too_old`, followed by a fresh `snapshot`. Discard your local state and rebuild from the snapshot. ## Authentication The `orders` channel requires a signed `auth` frame. Authentication uses an EIP-191 `personal_sign` signature over a canonical message, recovered to your owner address. Build and sign this exact message (unix seconds, no trailing newline): ```text theme={null} wants you to authenticate for the Numo markets WebSocket. Address:
Nonce: Issued At: Expiration Time: ``` Then send the fields in an `auth` frame: Your owner address, `0x` + 40 hex characters. Recovery must match this address. The `personal_sign` signature over the canonical message. A unique value per auth frame. Unix seconds when the frame was signed. Rejected if far in the future. Unix seconds when the frame stops being valid. Keep the window short; the server enforces a maximum TTL. On success the server replies `{ "type": "ack", "message": "authenticated" }` and the connection is authorized for that address until it closes. On failure it replies with an `error` frame, code `auth_failed`. ## Error codes `error` frames carry a `code` and a human-readable `message`. | Code | Cause | | ----------------- | ---------------------------------------------------------------------------- | | `bad_json` | Frame was not valid JSON. | | `bad_op` | Unknown `op`. | | `bad_channel` | Unknown channel. | | `unknown_market` | Market symbol or `asset:sub_id` did not resolve. | | `auth_required` | Subscribed to `orders` without authenticating first. | | `auth_failed` | The `auth` frame failed signature, domain, or expiry checks. | | `resume_too_old` | `since_seq` is older than retained history; a fresh snapshot follows. | | `stream_reset` | The server dropped the subscription; resubscribe with `since_seq`. | | `snapshot_failed` | The snapshot could not be loaded; resubscribe. | | `slow_consumer` | Your client fell behind and the connection was closed. Reconnect and resume. | The server enforces backpressure. If your client cannot keep up with the send rate, the connection is closed with `slow_consumer`. Reconnect and resume with `since_seq` rather than holding a slow socket open. # Order lifecycle Source: https://docs.numofx.com/development How an order moves through markets-service, execution-service, execution-contracts, and risk-core ## System overview Numo's futures orderbook is split across four layers: Public REST API for market discovery, orderbook reads, trades, order entry, and cancellation. Backend executor that validates crossed-order payloads and submits `verifyAndMatch(...)`. Onchain matching and action-verification contracts used by the trusted backend executor. Margin engine, subaccounts, cash asset accounting, manager hooks, settlement, and liquidation. ## End-to-end flow Clients call `GET /v1/markets` on `markets-service` to discover enabled instruments and retrieve the canonical `asset_address` and `sub_id`. Clients poll `GET /v1/book` and `GET /v1/trades` on `markets-service` for top-of-book state, recent prints, and 24h stats. Clients submit signed order payloads to `POST /v1/orders`. The service validates that order metadata matches the embedded action payload before persisting the order. The matcher loop in `markets-service` scans for crossed orders, computes fill amounts, and builds an executor payload for the market. `execution-service` validates the payload, ABI-encodes `TradeModule.OrderData`, simulates `Matching.verifyAndMatch(...)`, and broadcasts the transaction through `execution-contracts`. `risk-core` validates account state through manager and asset hooks, updates margin state, and handles cash settlement, funding, and liquidation behavior. ## Deliverable cNGN future The physically delivered FX future exposed by the backend is: * `market`: `USDCcNGN-SEP16-2026` * `contract_type`: `deliverable_fx_future` * `settlement_type`: `physical_delivery` * `display_name`: `USDC/cNGN SEP-16-2026 Future` The settlement note in the matcher registry is: > Physically delivered on Base. Long pays cNGN and receives fixed USDC notional; short pays fixed USDC notional and receives cNGN. ## What each repo is responsible for ### `markets-service` * stores active orders and trade fills * exposes `GET /v1/markets`, `GET /v1/book`, `GET /v1/trades` * accepts `POST /v1/orders` and `POST /v1/orders/cancel` * resolves market metadata from exact `(asset_address, sub_id)` ### `execution-service` * accepts `POST /execute` * requires `actions.length === signatures.length` * requires `actions[0].subaccount_id === order_data.taker_account` * requires every `action.module === module_address` ### `execution-contracts` * verifies signed actions * owns the trusted `Matching` execution path * executes matched transfers against the core account system ### `risk-core` * stores balances in `SubAccounts` * lets managers validate final account state * lets assets track transfer semantics and settlement inputs * provides `CashAsset` and `PerpAsset` primitives used by managers during settlement # Execution and risk Source: https://docs.numofx.com/essentials/navigation How execution-service, execution-contracts, and risk-core turn orders into settled futures trades ## Execution service `execution-service` is a thin HTTP wrapper around the trusted executor. It exposes: * `GET /healthz` * `POST /` * `POST /execute` The payload contains: * `market` * `asset_address` * `module_address` * `maker_order_id` * `taker_order_id` * `actions` * `signatures` * `order_data` The service validates payload shape with Zod before submitting anything onchain. ## Execution contracts `execution-contracts` contains the `Matching` and action-verification contracts used by the backend. These contracts are responsible for: * verifying signed user actions * constraining which modules can execute those actions * forwarding matched transfers into the account system The execution path is trusted-backend driven. The orderbook does not send raw orders directly onchain. ## Risk core `risk-core` is the account and margin layer underneath the orderbook. Important concepts: * `SubAccounts` stores `{asset, subId, balance}` state per account * managers validate final account state and can handle settlement and liquidation * assets define transfer semantics and settlement hooks * `CashAsset` is the main cash accounting rail * `PerpAsset` tracks unsettled PnL, funding, and price state for perpetual-style instruments ## Why this split exists This architecture keeps concerns separate: * `markets-service` handles order intake and market data * `execution-service` handles verified transaction submission * `execution-contracts` handles onchain match execution * `risk-core` handles solvency, settlement, and liquidation # Markets service Source: https://docs.numofx.com/essentials/settings Public orderbook and order-entry endpoints exposed by markets-service `markets-service` is the public-facing REST surface for Numo's futures orderbook. ## Public endpoints The service exposes these core routes: * `GET /healthz` * `GET /v1/markets` * `GET /v1/book` * `GET /v1/trades` * `POST /v1/orders` * `POST /v1/orders/cancel` ## Market discovery Use `GET /v1/markets` before you submit anything. The response contains the canonical metadata you need for order entry: * `market` * `asset_address` * `sub_id` * `contract_type` * `settlement_type` * `display_name` * `tick_size` * `order_entry_spec` when a market has a specialized UI contract ## Reading the book `GET /v1/book` accepts either: * `symbol=USDCcNGN-SEP16-2026`, or * `asset_address=0x...&sub_id=1777507200` The response returns: * `market_presentation` * `bids` * `asks` The matcher currently returns the top 25 levels on each side. ## Reading trade history `GET /v1/trades` supports: * `symbol` * `asset_address` * `sub_id` * `limit` * `before_trade_id` The response includes: * `market_presentation` * `stats_24h` * `trades` * `next_before_trade_id` ## Submitting an order `POST /v1/orders` persists a signed order after validating the embedded action payload. Required fields include: * `order_id` * `owner_address` * `signer_address` * `subaccount_id` * `recipient_id` * `nonce` * `side` * `asset_address` * `sub_id` * `desired_amount` * `limit_price` * `worst_fee` * `expiry` * `action_json` * `signature` The service rejects the order if the order metadata and `action_json` disagree on owner, signer, subaccount, or nonce. ## Cancelling an order Use `POST /v1/orders/cancel` with: * `owner_address` * `nonce` Cancellation is owner-and-nonce based, not order-id based. # Introduction Source: https://docs.numofx.com/index Numo is an exchange for physically delivered, FX stablecoin futures. Numo Numo ## Start building with the Numo API Use these guides and references to integrate faster. Make your first market read and submit your first signed order. Learn the signed payload fields required for authenticated order requests. Browse the Numo API reference for request and response formats. Use this reference as your source of truth while you design websocket stream consumers. Review common order validation requirements before you submit production traffic. Build predictable polling loops for orderbook and trade reads. Start from copy-paste examples in cURL and adapt them to your stack. Integrate the execution webhook route used by your match executor. # Quickstart Source: https://docs.numofx.com/quickstart Read the futures orderbook and submit your first signed order Use this sequence to integrate against Numo's orderbook for physically delivered FX futures. ## 1. Discover supported markets Query `markets-service` first so you know which instruments are enabled in the matcher. ```bash theme={null} curl "$MARKETS_SERVICE_URL/v1/markets" ``` For the deliverable cNGN future, look for: * `market = "USDCcNGN-SEP16-2026"` * `contract_type = "deliverable_fx_future"` * `settlement_type = "physical_delivery"` * `asset_address` and `sub_id` ## 2. Read the book and recent trades Once you have the market symbol or the `(asset_address, sub_id)` pair, fetch the current orderbook and recent prints. ```bash theme={null} curl "$MARKETS_SERVICE_URL/v1/book?symbol=USDCcNGN-SEP16-2026" curl "$MARKETS_SERVICE_URL/v1/trades?symbol=USDCcNGN-SEP16-2026&limit=50" ``` `markets-service` is the public read surface for: * market metadata * top-of-book bids and asks * recent trades and 24h stats * order submission and cancellation ## 3. Submit a signed order Orders are posted to `markets-service`, but the request must already contain a signed action payload that matches the contracts. ```bash theme={null} curl -X POST "$MARKETS_SERVICE_URL/v1/orders" \ -H "Content-Type: application/json" \ -d '{ "order_id": "future-order-1", "owner_address": "0xOWNER", "signer_address": "0xSIGNER", "subaccount_id": "10", "recipient_id": "10", "nonce": "1", "side": "buy", "asset_address": "0xFUTURE_ASSET", "sub_id": "1777507200", "desired_amount": "100000000000000000", "limit_price": "1605000000000000000000", "worst_fee": "0", "expiry": 1893456000, "action_json": { "subaccount_id": "10", "nonce": "1", "module": "0xTRADE_MODULE", "data": "0x...", "expiry": "1893456000", "owner": "0xOWNER", "signer": "0xSIGNER" }, "signature": "0x..." }' ``` `markets-service` rejects the order unless: * the instrument is enabled * `action_json.subaccount_id` matches `subaccount_id` * `action_json.nonce` matches `nonce` * `action_json.owner` matches `owner_address` * `action_json.signer` matches `signer_address` ## 4. Let the executor clear crossed orders The matching loop in `markets-service` identifies crossed orders and emits a payload to `execution-service`. `execution-service` then: * validates the match payload shape * ABI-encodes `TradeModule.OrderData` * simulates `Matching.verifyAndMatch(...)` * submits the transaction to the onchain matching contracts ## 5. Understand where risk and settlement live `execution-contracts` owns the trusted backend execution path. `risk-core` owns the margin system, subaccounts, cash settlement, and liquidation rules. For the `USDCcNGN-SEP16-2026` future: * settlement type is physical delivery * longs pay cNGN and receive fixed USDC notional at expiry * shorts pay fixed USDC notional and receive cNGN Follow the full path across markets-service, execution-service, execution-contracts, and risk-core. Review the public orderbook, trades, and order-entry surface.