interface PlaceOrderReq { marketId: string; // app-level symbol, e.g. "NC26-BIN-83479265" side: 'buy' | 'sell'; // lowercase outcome: string; // "0" = YES (first), "1" = NO (second) price: string; // decimal USDC, e.g. "0.42" quantity: string; // decimal outcome qty, e.g. "2" nonce: string; // decimal-string of the salt used in the EIP-712 struct expiry: number; // unix seconds (= Order.expiration); use 0 for no on-chain expiry maker: string; // VAULT address (= VaultFactory.vaultOf(signer)) signature: string; // 0x-prefixed EIP-712 signature clientOrderId?: string; // idempotency key (optional, scoped per associatedWallet) type?: 'limit' | 'market'; // default 'limit' timeInForce?: 'gtc' | 'ioc' | 'fok'; // default 'gtc' for limit, 'ioc' for market}
The request body is whitelisted (forbidNonWhitelisted); any extra
field returns 400 validation_failed.maker is the vault address. The signing EOA is recovered from
signature and must satisfy vaultFactory.vaultOf(signer) == maker.
The backend re-runs that check before storing the order, so passing the
EOA in maker instead of the vault returns 400 bad_signature.
0 < p < 1 (strict — p >= 1 is rejected to block the fee = k × P × (1−P) sign flip at P > 1). Tick size is 0.01 — at most 2 decimal places (integer cents, 99 distinct levels). 0.505 returns invalid_amounts with “price tick size is 0.01”.
400 invalid_amounts
quantity
> 0, at most 6 decimal places (USDC scale).
400 invalid_amounts
type + timeInForce
MARKET orders require ioc or fok; MARKET+GTC rejected
400 invalid_tif
signature
EIP-712 recover (full on-chain Order struct) must equal an EOA whose vault == maker
400 bad_signature
expiry
0 (no expiry, recommended) or unix-seconds in the future. There is no minimum TTL.
400 expired
outcome
"0" or "1". The platform maps this to the on-chain tokenId for the market.
interface PlaceOrderResp { orderId: string; // server-issued UUID, '' when status=REJECTED status: 'PENDING' | 'OPEN' | 'FILLED' | 'CANCELLED' | 'REJECTED' | 'EXPIRED' | 'SETTLEMENT_FAILED'; filledQty: string; // cumulative fill at response time (decimal) remainingQty: string; // quantity - filledQty trades: TradeFill[]; // synchronous fills produced by IOC/FOK or aggressive LIMIT code?: string; // present when status=REJECTED (insufficient_funds, …) message?: string; // human-readable explanation; pairs with code clientOrderId?: string; // echoed from request when supplied}
The clientOrderId field is echoed verbatim when the request supplied
one — present on every shape (success, REJECTED, idempotent replay). Use
it to correlate the server-issued orderId to your own client-side handle
without a follow-up GET /api/orders/{id}. The same value is also carried
on the events.order_placed and events.order_cancelled frames over
/ws/user, so partners that drive their state machine off the WS feed get
the correlation key on both surfaces.
Same shape; side: 'sell'. Balance is not locked for SELL orders —
the outcome token (ERC-1155) must already sit in the vault, not
the EOA. Position resolution is keyed by maker (the vault), which is
why on-chain splits (USDC → YES + NO) must be initiated by
vault.splitPosition(...) rather than by the EOA directly. See
Vaults for the split flow.