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 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 is { "price": "0.50", "size": "5000000", "num_orders": 3 }. size is the total remaining wei at that price; num_orders is the count of resting orders there.
Response
{
"market_id": "...",
"outcome_index": 0,
"bids": [
{ "price": "0.50", "size": "5000000", "num_orders": 3 },
{ "price": "0.49", "size": "3000000", "num_orders": 1 }
],
"asks": [
{ "price": "0.52", "size": "2000000", "num_orders": 2 },
{ "price": "0.55", "size": "4000000", "num_orders": 1 }
]
}
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", "num_orders": 3 } ],
"asks": [ { "price": "0.52", "size": "2000000", "num_orders": 2 } ]
},
"outcome_1": {
"bids": [ { "price": "0.48", "size": "1000000", "num_orders": 1 } ],
"asks": [ { "price": "0.50", "size": "3000000", "num_orders": 2 } ]
}
}
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
}