Skip to main content
Discover what each venue supports before you build against it — order types, time-in-force values, tick and size bounds, fee model, settlement currency — and read or grant the on-chain token allowances that on-chain venues need before they can trade on your behalf. Full parameter reference and a live tester: API Reference.

Base URL

Authentication

Every endpoint on this page requires authentication — none are public. Same API-key headers as Orders:
Examples below read credentials from KAIROS_CLIENT_ID, KAIROS_API_KEY, and KAIROS_API_SECRET in your shell. The three metadata reads enforce no scope — any credential that authenticates can call them. POST /exchanges/{exchange_id}/allowances is fund-affecting: it requires the trade:execute scope, sits behind the mutation gate that runs before user auth (so a bad credential triple is a 403, not a 401), and requires your installed Turnkey trading policies to be current. Scopes only bind API-key callers; a session-JWT caller carries no scope set and skips both the scope check and the per-provider enable flag.

Exchange ids

Gotcha: {exchange_id} is matched case-sensitively against the registry, with exactly one alias: kalshi_offchain resolves to kalshi (that alias is matched case-insensitively). Everything else is used verbatim, so Polymarket and POLYMARKET return 404 where polymarket succeeds.
The recognised ids are polymarket, kalshi, predictfun, opinion, and hyperliquid. Which of them are actually registered is deployment-dependent — Kalshi, Opinion, and Predict.fun each register conditionally, and a cell-scoped deployment registers exactly one venue. Call GET /exchanges to see what this deployment actually serves rather than assuming the full list.

List exchanges

Every exchange id registered on this deployment. Call it first to find out which venues this deployment serves. Authenticated; no scope required.

Request

No parameters.

Response

A bare JSON array of strings — not a wrapped object. The order is not stable between calls; sort client-side if you need determinism.

Get exchange

Display metadata plus the full capability block for one venue. Authenticated; no scope required.

Request

Response

Gotcha: id echoes the request, so a lookup on kalshi_offchain returns "id": "kalshi_offchain" while capabilities.exchange_id is "kalshi". Key off capabilities.exchange_id when you need the canonical value.
Gotcha: an unregistered id returns 404 with an empty body — no JSON, so do not try to parse one.

Get capabilities

The capability block on its own. This is the endpoint to consult before submitting an order: it tells you whether the venue will accept your time-in-force, whether post_only is honoured, and what the tick and size bounds are. Authenticated; no scope required.

Request

Response

Gotcha: all decimal fields are JSON strings, not numbers ("0.01", not 0.01) — including min_tick_size, min_order_size, max_order_size, and max_price. chain_id is a string too. Nullable fields emit an explicit null; nothing in this object is ever omitted.
An unregistered id returns 404 with an empty body.

Capability matrix

These values are compile-time constants, identical across deployments: Only market and limit are true in supported_order_types on every venue — no venue advertises native stop, stop-limit, take-profit, or trailing-stop orders.

Allowances

On-chain venues need ERC-20 and ERC-1155 approvals in place before the exchange contracts can move your collateral and outcome tokens. The REST mutation grants them; the RPC query reads their status.
Gotcha: only polymarket, predictfun, and opinion have an allowance manager registered. kalshi and hyperliquid have no allowance manager. opinion reports requires_allowances: false yet still has a manager, so branch on the 404 rather than on the capability flag.

Read allowance status

Allowance reads are available through the authenticated RPC query exchange.getAllowances (API keys need the position:read scope) with { "exchangeId": "polymarket" }. The RPC gateway resolves the caller’s wallet and preserves the per-user read limit. There is no REST GET /exchanges/{exchange_id}/allowances.

Set allowances

Submits any missing approvals for the venue. Already-sufficient slots are skipped, so a repeat call after success submits nothing on-chain and still returns success: true. Scope trade:execute. Behind the mutation gate, so a bad credential triple is 403, and your installed Turnkey trading policies must be current.

Request

Gotcha: the three identity fields are optional on CTF operations but all three are required here — a missing field is 422.
They are still identity assertions, not overrides: a value that does not match the authenticated caller is rejected 403 with error_details.code = AUTH_IDENTITY_MISMATCH naming the offending field, and a user_id that is not a UUID is 400 user_id is not a valid UUID. These values cannot change the account’s custody assignment. For a Kairos-managed Polymarket account, POST repairs only missing approvals for its effective trading wallet and is safe to retry.

Response

Gotcha: null hashes do not mean failure — they mean no transaction was needed. On predict.fun both hashes are always null, even on a successful first-time approval, because that venue’s transaction hashes are not surfaced through this endpoint; a successful predict.fun call is {"usdc_tx_hash": null, "ctf_tx_hash": null, "success": true}. Read exchange.getAllowances to confirm state rather than inferring it from the hashes.
Polymarket approvals cover the CTF Exchange V2, NegRisk Exchange V2, NegRisk CTF Collateral Adapter, and NegRisk Adapter (CLOB v1) spenders. A successful request may return a batch_tx_id; re-read exchange.getAllowances to confirm which approvals are now sufficient. Predict.fun submits its missing approvals in sequence and fails fast rather than leaving a wallet half-enabled.

Custody and active-wallet selection

Kairos selects the active trading wallet from the account’s saved venue setup; request fields cannot switch that selection. The wallet_address returned by exchange.getAllowances is the best first diagnostic. If it does not match the wallet your integration expects, do not compensate by changing the POST body: contact support with the returned address and your expected address.

Errors

The allowance endpoints return the same structured envelope as order submission — top-level code is legacy PascalCase, error_details.code is the canonical SCREAMING_SNAKE_CASE wire code, and error_details.details/metadata are omitted when absent rather than null. Match on error_details.code. The three metadata endpoints (/exchanges, /exchanges/{id}, /exchanges/{id}/capabilities) do not use that envelope: their only failure is 404 with an empty body. Auth-layer and mutation-gate failures on any endpoint return a bare {"error": "<message>"} with no error_details. The Code column holds error_details.code where this page names one; — means the status covers conditions with no distinct code. POST /allowances returns the stable ALLOWANCE_APPROVAL_FAILED code for submission failures and records the internal cause in server logs. Sensitive account and infrastructure details are deliberately not copied into the public response. The status and primary action distinguish a retryable upstream failure from a terminal signing/configuration failure. Re-read exchange.getAllowances before any retry to see which slots are still missing. An exact Polymarket result of three sufficient entries out of eight commonly means the wallet has only part of the current approval set (for example, the three V2 pUSD spenders) and is missing the legacy NegRisk adapter plus ERC-1155 operator grants. It is not, by itself, evidence of a bad API credential. Re-read the allowance state after POST and, if it remains incomplete, contact support with the wallet, missing spender names, timestamp, and complete response rather than rotating credentials solely because it is 3/8.