Orders

Submit a new order

Submits a custodial order: Kairos signs and routes it to the venue on the caller's behalf (custodial signing). This is the standard order-entry path used by both the web app and API-key consumers who have not been allow-listed onto the self-custody "external-signing" lane (POST /v2/orders/intent + POST /v2/orders/submit). Ownership. The order is always created for the authenticated caller; any user_id in the body is accepted for backwards compatibility but is ignored — the JWT/API-key identity wins. Ack semantics (async). A 200 response means the order was validated, persisted, and enqueued — it does NOT mean the order is live on the venue yet. The response status is "queued" (or, on an idempotent replay, the current status of the previously-created order). Track the order via GET /orders/{order_id}, GET /orders polling, or (lowest latency) the /ws socket, which pushes order_update / fill events as the worker submits to the venue and fills arrive. Idempotency. Pass client_order_id to make retries safe: a second submit with the same (user, exchange_id, market_id, client_order_id) tuple returns the existing order instead of creating a duplicate, and does not consume a rate-limit slot. If omitted, the server generates a random one (i.e. retries without a client-supplied id are NOT deduped). Provider routing. exchange_id selects the venue-specific executor (polymarket, kalshi, predictfun, opinion, hyperliquid, …; kalshi_offchain is a deprecated alias of kalshi). Each venue advertises its own capabilities (supported time_in_force values, max price, order types) — a request that is well-formed in general but unsupported by the specific venue is rejected with 400 EXCHANGE_UNSUPPORTED / 400 VALIDATION_INVALID_ORDER. Hyperliquid HIP-4. Use the numeric outcome id as market_id, the selected display label as outcome, and the side coin from the market-data OrderbookSnapshot.token_ids as token_id (for example #1010 or #1011). Quantities are whole shares. Hyperliquid maps GTC/GTD to venue GTC and IOC/FAK/FOK to venue IOC. Single-order and selected-order batch cancellation are supported; cancel-all and fee quotes are unavailable. Order types. kind="market" is a marketable taker order — pair it with time_in_force FOK (fill-or-kill) or FAK/IOC (fill-and-kill / immediate-or-cancel, partials allowed, remainder cancelled). kind="limit" is a resting quote — pair it with GTC (good-til-cancelled) or GTD (good-til-date, requires expiration_minutes). price is REQUIRED for every order, including market orders — for a marketable order it is the limit you are willing to cross to, not a market-price sentinel. Circuit breakers & gating. Before persisting, the order passes through, in order: the per-user order rate limit, size/price bounds, the venue time-in-force capability gate, a global + per-exchange execution kill-switch, the custodial trading-enabled check, the post-only capability gate, side/kind/TIF-specific breakers, and the API-key minimum-notional rule. Kill-switch and restriction rejections are 503 with error_details.code = MARKET_PAUSED; a disabled or identity-less account is 403 with AUTH_CREDENTIALS_INVALID. Designated canary users bypass both breaker checks. Rate limiting. A Redis sliding-window limiter caps order submissions per authenticated user (default 5 orders per second, ORDER_RATE_LIMIT_PER_SEC). API keys with an orders override are checked against BOTH their own per-credential window and the aggregate per-user window (either denying is a 429); idempotent replays are resolved before the limiter and never consume a slot. The limiter fails closed — an unreachable Redis denies with 429. The 429 body carries error_details.code = VALIDATION_INVALID_ORDER with the message Order rate limit exceeded (there is no dedicated rate-limit code on this path), and no Retry-After header. Auth & scope. Requires trade:execute. This mutation endpoint is additionally gated by a service-token/CSRF/API-key check (RequireServiceToken), which any request bearing X-Client-Id / X-Api-Key / X-Api-Secret satisfies automatically — no extra header is needed for API-key consumers.

post/orders

Request

  • Base URL
    https://execution.kairos.trade
  • Auth
    API key in header X-Client-Id and API key in header X-Api-Key and API key in header X-Api-Secret

Request body

user_idstring

Ignored. The authenticated caller's id is always used.

exchange_idstring required

Venue identifier, must be a registered exchange (e.g. polymarket, kalshi, predictfun, hyperliquid).

market_idstring required

Market/contract identifier on the venue. Hyperliquid expects the numeric HIP-4 outcome id.

token_idstring nullable

Outcome-token id. For Hyperliquid, pass the selected side coin #<10 * market_id + side_index>; it is required to select side 1.

outcomestring nullable

Human-readable outcome label for display/attribution (e.g. "Yes", "No", a team name).

side'buy' | 'sell' | 'BUY' | 'SELL' | 'Buy' | 'Sell' required

buy or sell, case-insensitive on input.

kind'market' | 'limit' required

market or limit, lower-case exactly.

quantitystring required

Decimal string, shares/contracts. Must be > 0 and <= 1,000,000. A minimum order size (default 1.0, MIN_ORDER_QUANTITY) applies to BUY orders only — SELL/close orders are allowed at any size so a position can be fully closed after partial fills. API-key-authenticated BUYs additionally require quantity × price >= $5 notional; session-JWT callers and all SELLs are exempt.

pricestring

Decimal string, REQUIRED for every order (including market orders, where it is the limit you're willing to cross to). Must be > 0 and <= the venue's max price (typically 1.0 for prediction markets).

time_in_forcestring

GTC, GTD, FOK, FAK, or IOC (case-insensitive on input, normalized to upper-case). Defaults to GTC if omitted; an unrecognized non-empty value is rejected rather than silently downgraded to GTC. Must be a TIF the target venue's capabilities advertise.

post_onlyboolean nullable

Maker-only. When true the venue must REJECT the order rather than let any part of it cross the spread and take liquidity — it is a guarantee, never a hint, so an order that cannot honour it is rejected instead of being downgraded to a taker. Requires kind=limit and time_in_force of GTC or GTD, on a venue whose capabilities advertise post-only support (currently Polymarket, Kalshi, Predict.fun and Hyperliquid — the last expresses maker-only as its Alo time-in-force rather than a flag, but the request field is the same). Any other combination is a 400. Defaults to false.

expiration_minutesinteger nullable

Minutes from now until expiry. Only meaningful (and validated) when time_in_force=GTD: must be in [1, 43200] (30 days).

trigger_pricestring nullable

Decimal string. Trigger price for stop-loss/take-profit style orders. Must be > 0 and, like price, <= the venue's max price.

collateral'skip' | 'check' | 'fund' nullable

How much of the collateral router runs for this order. skip (the server default) is today's path: no affordability check, no hold, no router call, zero added latency. check places an affordability check and a hold and refuses a known shortfall, but never moves money. fund is check plus cross-ledger funding inside the two caps below, and REQUIRES both of them. An unrecognised value is rejected 400 naming the three valid values; it is never downgraded to skip.

shard_fundingboolean nullable

Kalshi only. Move collateral between the account's own Kalshi shards before submit. Defaults to true — same venue, zero fee, one REST call, and already every account's behaviour — so an existing caller is unaffected. Send false to skip it.

max_bridge_fee_usdcstring nullable

Decimal string. The most bridge fee this order consents to pay, in USDC. Required when collateral=fund and rejected 400 otherwise; 0 is a valid cap meaning "fund only if it is free".

max_funding_wait_msinteger nullable

The longest this order consents to wait for funding, in milliseconds. Required when collateral=fund and rejected 400 otherwise.

gas_sponsoredboolean nullable

Requests gas sponsorship. Currently not implemented server-side.

client_order_idstring nullable

Caller-supplied idempotency key. A retry with the same (user, exchange_id, market_id, client_order_id) returns the existing order instead of creating a duplicate, and does not consume a rate-limit slot. If omitted, a random id is generated server-side (so un-keyed retries are NOT deduped). On hyperliquid it is sent unchanged as the order's cloid and must be 0x followed by 32 lowercase hex characters; any other value is refused with 400.

max_slippagestring nullable

Deprecated — use max_slippage_cents. Decimal in [0, 0.5] (e.g. 0.10 = 10%).

max_slippage_centsinteger nullable

Maximum acceptable slippage in cents. Must be in [1, 99] if provided.

max_retriesinteger nullable

Maximum retry attempts for transient failures. Defaults to 0.

bot_idstring nullable

Trading-bot id this order is attributed to.

sourcestring nullable

Order-source attribution override. Pass copytrade explicitly when relevant; otherwise the server derives it from the auth method (API-key auth → api, bot_id present → bot, else → manual).

Example request

{
  "exchange_id": "polymarket",
  "outcome": "Yes",
  "side": "buy",
  "kind": "limit",
  "quantity": "100",
  "price": "0.52",
  "time_in_force": "GTC",
  "expiration_minutes": 60,
  "collateral": "skip",
  "shard_funding": true,
  "max_bridge_fee_usdc": "1.50",
  "max_funding_wait_ms": 30000,
  "max_slippage_cents": 5,
  "source": "api"
}

Response

Order accepted, persisted, and enqueued for execution. Does not imply the order is live on the venue yet — poll or subscribe to /ws for the terminal state.

order_idstring uuid required

Internal Kairos order id. Use with GET /orders/{order_id} and the cancel endpoints.

statusstring required

queued for a newly-created order; on an idempotent replay (matching client_order_id), the pre-existing order's current status (pending, live, partial, filled, cancelled, expired, or failed).

Example response

{
  "status": "queued"
}

Changes

No recorded changes to this operation across all 1 revision of this API.