Base URL
Authentication
Every endpoint on this page requires authentication — none are public. Same API-key headers as Orders: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.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
Request
No parameters.Response
Get exchange
Request
Response
Gotcha:idechoes the request, so a lookup onkalshi_offchainreturns"id": "kalshi_offchain"whilecapabilities.exchange_idis"kalshi". Key offcapabilities.exchange_idwhen 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
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.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 queryexchange.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
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.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
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. Thewallet_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-levelcode 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.
