Orders
Place, cancel, and query orders on the 4rho prediction market CLOB.
Place Order
POST /v1/orders
Scope: trade:orders
Placing orders requires both HMAC API key authentication (request headers) and an EIP-712 signature (request body). See the EIP-712 Signing guide for prerequisites and examples.
Sign the correct fee.
fee_rate_bpsis part of the signed order. It MUST equal the market's canonical effective fee or placement is rejected422(fee_rate_bps mismatch), and a resting order silently stops matching if the canonical fee later moves (#3672). ReadGET /v1/market-data/:market_id/fee-rateand signmaker_fee_bps(currently50platform-wide). Details: Fees.
Request Body
{
"market_id": "550e8400-e29b-41d4-a716-446655440000",
"token_id": "12345678901234567890",
"side": "BUY",
"maker_amount": "1000000",
"taker_amount": "2000000",
"maker": "0xYourWalletAddress",
"signer": "0xYourWalletAddress",
"taker": "0x0000000000000000000000000000000000000000",
"salt": "123456789",
"nonce": 0,
"expiration": 0,
"fee_rate_bps": 50,
"signature": "0x...",
"outcome_index": 0,
"time_in_force": "GTC",
"post_only": false
}
| Field | Type | Required | Description |
|---|---|---|---|
market_id | UUID | Yes | Market to trade on |
token_id | string | Yes | ERC-1155 token ID for the outcome |
side | string | Yes | BUY or SELL |
maker_amount | string | Yes | Amount the maker provides (wei) |
taker_amount | string | Yes | Amount the maker receives (wei) |
maker | string | Yes | Maker wallet address |
signer | string | Yes | Signer wallet address |
taker | string | Yes | Must be the zero address - directed orders are not supported |
salt | string | Yes | Random salt for order uniqueness |
nonce | int | Yes | Order nonce |
expiration | int | No | Unix timestamp (0 = no expiry) |
fee_rate_bps | int | Yes | Fee rate in basis points |
signature | string | Yes | EIP-712 signature (signing guide) |
outcome_index | int | Yes | Outcome index (0 = Yes, 1 = No) |
time_in_force | string | No | GTC, GTD, FOK, FAK (default: GTC) |
post_only | bool | No | Reject if would cross the book |
Response
{
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"order_hash": "0xabc123...",
"status": "open",
"matches": 0
}
| Status | Description |
|---|---|
open | Resting on the book, no fills |
partially_filled | Some fills, remainder resting |
filled | Fully filled |
Cancel Order
DELETE /v1/orders/:hash
Scope: trade:orders
Cancels an open or partially filled order by its order hash.
Response
{ "message": "order cancelled" }
Get Order
GET /v1/orders/:hash
Scope: read:account
Returns the full order details including current status and fill amounts.
Response
{
"id": "550e8400-...",
"order_hash": "0xabc123...",
"market_id": "...",
"side": "BUY",
"price": "0.50000000",
"status": "open",
"remaining": "1000000",
"filled_amount": "0",
"time_in_force": "GTC",
"post_only": false,
"created_at": "2024-03-01T00:00:00Z"
}
Get Open Orders
GET /v1/orders/open/:market_id
Scope: read:account
Returns all open and partially filled orders for the authenticated user in a specific market.
Get Account Orders
For account-wide filtered order history use GET /v1/user/orders/v2
(read:account). Its order DTO adds nullable integer-string USDC fields
initial_limit_notional_usdc_micro, filled_limit_notional_usdc_micro, and
remaining_limit_notional_usdc_micro. Divide by 1,000,000 for dollar display.
These are signed-limit projections, not actual match proceeds: BUY uses
maker-asset USDC; SELL uses the canonical cumulative integer-floor conversion.
Missing or invalid lifecycle evidence is null, never a fabricated zero.
An explicit "0" is known zero. Initial normally equals filled + remaining;
cancelled partial FAK orders instead report zero remaining for the killed
unfilled portion, so that terminal shape does not conserve the original total.
Get Order Book
GET /v1/orders/book/:market_id?outcome_index=0&levels=20
Public - No authentication required.
| Parameter | Type | Default | Description |
|---|---|---|---|
outcome_index | int | 0 | Outcome index (0 or 1). Ignored when both is set. |
levels | int | 20 | Max price levels per side (1-100). |
both | bool | - | When true (or 1), return both outcome books in one response - see below. |
Each level carries price (USDC per token), num_orders, and two explicit
decimal-string integer amounts:
| Field | Meaning |
|---|---|
token_quantity_atomic | Outcome-token quantity in 6-decimal atomic units. |
notional_usdc_micro | Collateral notional in micro-USDC; use for dollar depth. |
size | Deprecated maker-denominated remainder, preserved without redefining it. Synthetic ask levels can mix BUY collateral and SELL tokens; it is not a canonical token or dollar amount. |
Canonical amounts come from the signed order ratios and cumulative fills,
not rounded display prices. If any contributor lacks exact evidence, both
canonical fields are omitted: unknown is not zero. A known zero is "0".
Do not substitute size for a missing canonical field.
Response
{
"market_id": "...",
"outcome_index": 0,
"bids": [
{ "price": "0.50", "size": "5000000", "token_quantity_atomic": "10000000", "notional_usdc_micro": "5000000", "num_orders": 1 }
],
"asks": [
{ "price": "0.60", "size": "14000000", "token_quantity_atomic": "20000000", "notional_usdc_micro": "12000000", "num_orders": 2 }
]
}
Both outcomes in one request
GET /v1/orders/book/:market_id?both=true&levels=20
Pass both=true (or both=1) to get outcome 0 and outcome 1 in a single response. outcome_index is ignored. For a market maker quoting two-sided this is one rate-limited request instead of two. The keys are outcome_0 and outcome_1:
{
"market_id": "...",
"outcome_0": {
"bids": [ { "price": "0.50", "size": "5000000", "token_quantity_atomic": "10000000", "notional_usdc_micro": "5000000", "num_orders": 1 } ],
"asks": [ { "price": "0.60", "size": "14000000", "token_quantity_atomic": "20000000", "notional_usdc_micro": "12000000", "num_orders": 2 } ]
},
"outcome_1": {
"bids": [ { "price": "0.40", "size": "4000000", "token_quantity_atomic": "10000000", "notional_usdc_micro": "4000000", "num_orders": 1 } ],
"asks": [ { "price": "0.50", "size": "5000000", "token_quantity_atomic": "10000000", "notional_usdc_micro": "5000000", "num_orders": 1 } ]
}
}
The YES ask above combines a direct SELL of 10 tokens at 0.60 and an opposite
BUY of 4 USDC at 0.40. Together they represent 20 YES tokens / 12 USDC, not the
14 mixed units in legacy size.
Current WebSocket book_delta frames describe individual order events; they
cannot replace or remove a whole price level. Coalesce them into bounded REST
refreshes of both outcomes, and retain periodic/reconnect recovery for dropped
frames. Depth aggregates do not prove your order's FIFO or direct/complement
queue position.
Cancel-Replace
POST /v1/orders/cancel-replace
Scope: trade:orders
Atomically cancels an existing order and places a new one. If the cancellation fails, the new order is not placed.
Request Body
{
"cancel_hash": "0xoriginal_order_hash...",
"new_order": {
"market_id": "...",
"side": "BUY",
"maker_amount": "1500000",
"taker_amount": "3000000",
...
}
}
Amend Order
PUT /v1/orders/:hash/amend
Scope: trade:orders
Re-price or re-size a resting order. Because maker_amount and taker_amount are part of the signed EIP-712 order, any change to the amounts requires a freshly signed replacement - supply it as new_order (a full Place Order body). The server cancels the old order and places the re-signed one atomically (a cancel-replace). A price change or quantity increase loses time priority.
The bare
maker_amount/taker_amountfields are retained for backwards compatibility but now reject any actual amount change with422 AMEND_RESIGN_REQUIRED- they can no longer silently invalidate a signature. Always usenew_orderto re-price or re-size.
Request Body
{
"new_order": {
"market_id": "market-id-1",
"outcome_index": 0,
"maker": "0xYourWallet",
"maker_amount": "2000000",
"taker_amount": "4000000",
"expiration": 0,
"nonce": "1718900000000",
"signature": "0x... (EIP-712 signature over the NEW amounts)"
}
}
Response
{
"order_hash": "0xnewhash...",
"status": "open",
"price": "0.50000000",
"maker_amount": "2000000",
"taker_amount": "4000000",
"remaining": "2000000",
"time_priority": false,
"price_changed": true
}