> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kairos.trade/llms.txt
> Use this file to discover all available pages before exploring further.

# Reprice a resting limit order in place

> Moves a resting limit order's price in one venue round trip. The order keeps its Kairos `order_id` AND its venue `exchange_order_id` — that is the property this endpoint exists to provide: no supersession to record, and no second order whose fill could be booked twice. The cancel-and-replace it replaces leaves the book empty of the order for the whole cancel round trip and mints a second id to reconcile.
**Venue support.** Only venues advertising `supports_native_amend` in `GET /exchanges/{exchange_id}/capabilities` — today `kalshi` alone. Anything else is `409` `EXCHANGE_AMEND_UNSUPPORTED`, answered before the order's own state is inspected so the verdict is a permanent property of the venue rather than of this moment. There is deliberately NO internal cancel-and-replace fallback: that substitution changes queue position, order identity and the caller's failure modes, so it is the caller's decision.
**Reprice only.** `quantity` is accepted solely to be refused with `409` `EXCHANGE_AMEND_QUANTITY_UNSUPPORTED`, before the order is read — refused rather than silently ignored so a caller never believes it resized. Limit orders only; `price` must be strictly between 0 and 1, in the order's own outcome's terms.
**This endpoint almost always returns `200`.** `success: false` is an ambiguous venue outcome, never a transport failure — the same convention `POST /orders/{order_id}/cancel` uses, because a 5xx invites a retry against an order that may already have been amended. It covers a terminal order, an order not yet at the venue, and a venue that refused; on every one of those the order is untouched and still resting on its old price. Branch on `success`, not on the status code.
**Queue position.** Kalshi preserves it only when an amend DECREASES size; a reprice forfeits it — but so does the cancel/replace this replaces, so nothing is lost. The win is the closed off-book window, not priority.
**Admission.** Runs the same checks a submit does — kill switch, side/kind/TIF circuit breakers evaluated against the order's own side and kind (an amend changes neither), and the order rate limit. An immediate fill from a crossing amend is booked through the normal poller path, not from this response.
**Auth & scope.** Requires `trade:execute` and ownership of the order.



## OpenAPI

````yaml /openapi/execution.yaml post /orders/{order_id}/amend
openapi: 3.1.0
info:
  title: Kairos Order Execution API
  version: 1.0.0
  summary: >-
    Order entry, cancellation, fee quotes, and live position exposure across
    venues.
  description: >
    The Order Execution API at `execution.kairos.trade` places and manages

    orders across every venue Kairos integrates. Two lanes:


    - **Custodial** (`POST /orders`) — Kairos signs and routes on your behalf
      (custodial signing). Async ack: a 200 means validated + enqueued; track via
      `GET /orders/{order_id}` or the WebSocket order/fill stream.
    - **Self-custody / external signing** (`POST /v2/orders/intent` +
      `POST /v2/orders/submit`) — for allow-listed institutional accounts on
      Polymarket and Predict.fun: Kairos builds the EIP-712 payload, you sign
      with your own key, submission is synchronous with the venue's verbatim
      result. `POST /v2/onchain/intent` + `POST /v2/onchain/submit` are the
      same pattern for on-chain operations (approvals, redeem, split/merge,
      unwrap) on Polygon, where you also pay the gas.

    ## Authentication


    All endpoints require a Kairos API key (`X-Client-Id` + `X-Api-Key` +

    `X-Api-Secret`) or a first-party session JWT, plus the per-operation

    scope (`trade:execute`, `trade:read`, `position:read`) where the operation

    documents one. Synthetic Books accepts any otherwise-valid account

    credential without an extra scope or allowlist. There is

    no anonymous tier. Scope checks apply to API-key credentials only; a

    session JWT carries no scope list and is treated as holding every scope.


    The custodial mutation endpoints (`POST /orders`, the three cancel

    endpoints) and Synthetic Books create/refresh/release carry an additional

    `RequireServiceToken` gate satisfied by **any** of: the full API-key triple,

    an internal `X-Service-Token`, or a valid `X-Csrf-Token`. API-key consumers

    need no extra header — but a bare `Authorization: Bearer <jwt>` alone is NOT

    sufficient on those routes. The `/v2/orders/*` and `/v2/onchain/*`

    external-signing routes have no such gate: a session JWT alone works there.


    ## Rate limits


    Order submission is capped per user (default 5 orders per **second**,

    sliding window, `ORDER_RATE_LIMIT_PER_SEC`); some credentials carry an

    `orders` override, which is enforced on its own per-credential window of

    the same duration *in addition to* the per-user window — either one

    denying is a `429`. Idempotent replays (same `client_order_id`) return the

    existing order before the limiter runs and never consume a slot. The

    limiter is Redis-backed and **fails closed**: if Redis is unreachable the

    submission is denied with `429`.


    Separately, repeated *authentication failures* from one client IP are

    throttled at 10 failures per 60 s (also fail-closed). That limiter returns

    the minimal `{"error": "Too many authentication attempts"}` body.


    **No rate-limit headers.** This service does not emit `Retry-After`,

    `X-RateLimit-*`, or any other backoff hint on a `429` — back off on your

    own schedule.


    ## Error responses


    **Six different error body shapes are in use across this service and they

    are not interchangeable.** Check the shape documented on the specific

    operation before writing a parser; a client that assumes one shape will

    read `undefined` for the reason on the others.


    1. `OrderErrorResponse` — the structured envelope (`error`, `code`,
       `error_details{code,message,details?,metadata?,actions}`) returned by
       every handler that surfaces an `ExecutionError`/`ApiError`: order
       submission, cancel-all, the CTF endpoints, the Polymarket/Opinion
       onboarding endpoints, Hyperliquid withdraw/transfer, and most of the
       deposit-wallet family. Note the two `code` fields differ in case:
       top-level `code` is PascalCase (`"AuthInsufficientScope"`), while
       `error_details.code` is the SCREAMING_SNAKE_CASE wire code
       (`"AUTH_INSUFFICIENT_SCOPE"`). Match on `error_details.code`.
    2. `OrderSimpleErrorResponse` — the minimal `{"error": "..."}` (sometimes
       with `code`) used by the auth middleware (any endpoint's
       `401`/`403`/`429`), the whole `/v2/*` external-signing and on-chain
       lane, the Kalshi and Predict.fun endpoints.
    3. `OrderMarketLinkErrorResponse` — `{"message": "..."}`, with no `error`
       and no `code`. Used by `POST /orders/market-links` **only**.
    4. **Empty or plain-text, not JSON at all.** Several deposit-wallet
       endpoints — most importantly
       the RPC signed-batch submission and
       `POST /exchanges/polymarket/imported/relay-info` — return most 4xx/5xx
       responses with a **completely empty body** and `content-type:
       text/plain`. A handful of cases carry a bare plain-text sentence
       (`batch is not an allowed withdrawal or collateral conversion`,
       `relayer rejected batch: …`). Do not attempt to JSON-parse these.
    5. Handlers whose Rust signature returns a bare `StatusCode` likewise send
       **no body at all**; those responses are marked "empty body (status code
       only)".
    6. `SyntheticBookErrorResponse` — `{"error": "stable_snake_case_code",
       "message": "...", "details"?: {...}}` on `/v1/synthetics*`. Match the
       top-level `error`; upstream definition validation may be nested under
       `details`.

    A further wrinkle inside shape 1: some provider-access checks discard the

    specific reason and return a generic `"Request failed with status 403"` /

    `AUTH_CREDENTIALS_INVALID` body, while others preserve

    `AUTH_INSUFFICIENT_SCOPE` and the real message. Do not rely on the message

    text of an access denial being stable.


    The central `ExecutionError` → HTTP mapping (`ApiError::from`) is:


    | `ExecutionError` | Status | `error_details.code` |

    |---|---|---|

    | `InsufficientBalance` | 400 | `FUNDS_INSUFFICIENT_USDC` |

    | `CollateralLocation` | 400 | `FUNDS_COLLATERAL_LOCATION` |

    | `InvalidOrder` | 400 | `VALIDATION_INVALID_ORDER` |

    | `PositionShortfall` | 400 | `FUNDS_INSUFFICIENT_BALANCE` |

    | `MarketClosed` | 400 | `EXCHANGE_POLYMARKET_MARKET_CLOSED` |

    | `OrderAlreadyCancelled` | 400 | `VALIDATION_INVALID_ORDER` |

    | `UnsupportedExchange` | 400 | `EXCHANGE_UNSUPPORTED` |

    | `SlippageExceeded` | 400 | `MARKET_FOK_NOT_FILLED` |

    | `FokNotFilled` | 400 | `MARKET_FOK_NOT_FILLED` |

    | `AuthenticationError` | 401 | `AUTH_CREDENTIALS_INVALID` |

    | `CredentialError` | 401 | `AUTH_CREDENTIALS_NOT_FOUND` |

    | `MarketNotFound` | 404 | `VALIDATION_MARKET_NOT_FOUND` |

    | `OrderNotFound` | 404 | `VALIDATION_INVALID_ORDER` |

    | `MarketNotSettledOnChain` | 409 | `VALIDATION_MARKET_NOT_SETTLED_ON_CHAIN`
    |

    | `OrderbookUnavailable` | 422 | `ORDERBOOK_UNAVAILABLE` |

    | `ExchangeError` (code `429`/`RATE_LIMITED`) | 429 |
    `EXCHANGE_POLYMARKET_RATE_LIMITED` |

    | `ExchangeError` (code `401`/`UNAUTHORIZED`) | 401 |
    `AUTH_POLYMARKET_API_KEY_INVALID` |

    | `SigningError` | 500 | `SIGNATURE_ERROR` |

    | `DatabaseError` | 500 | `DATABASE_ERROR` |

    | `InternalError` / `LedgerReconciliationRequired` /
    `SponsoredRequestWedged` / `PreTradeError` | 500 | `INTERNAL_ERROR` |

    | `NetworkError`, `ExchangeError` (any other code) | 502 | `NETWORK_ERROR` /
    venue-classified |

    | `LockError` | 503 | `INTERNAL_ERROR` |

    | `Timeout` | 504 | `NETWORK_TIMEOUT` |


    A venue `ExchangeError` is further classified from the venue's own message

    text before it is mapped, so an "allowance is not enough" rejection becomes

    `ALLOWANCE_CTF_NOT_SET`, a "not enough balance" rejection becomes

    `FUNDS_INSUFFICIENT_BALANCE`, "post-only mode" becomes `MARKET_NOT_READY`,

    a "no liquidity" rejection becomes `MARKET_INSUFFICIENT_LIQUIDITY`, and so

    on. Match on `error_details.code`, never on `error`.


    ## Server-level guards


    Every request is subject to a 120 s timeout (`OE_REQUEST_TIMEOUT_SECS`), a

    2 MiB request-body cap (`OE_MAX_BODY_BYTES`, over-size bodies get `413`),

    and a 1024-request global concurrency ceiling

    (`OE_MAX_CONCURRENT_REQUESTS`). Every response carries
    `X-Content-Type-Options:

    nosniff`, `X-Frame-Options: DENY`, HSTS, and a `default-src 'none'` CSP.


    ## Conventions


    - Prices are decimal strings on the 0–1 scale; `*_bps` fields are the
      same value in basis points (× 10000).
    - Quantities/sizes are decimal strings.

    - Structured errors carry `error_details.code`
      (SCREAMING_SNAKE_CASE) plus actionable recovery `actions`.

    ## Changelog


    **2026-09-14 — collateral routing fields on `POST /orders`.** Four optional

    request fields and one optional response field. `collateral`

    (`skip` | `check` | `fund`) **defaults to** `skip`, which is exactly today's

    path: no affordability check, no hold, no funding, zero added latency — so

    no existing caller changes behaviour without opting in. `shard_funding`

    **defaults to** `true`, which is also exactly today's behaviour: the Kalshi

    shard move is same-venue, zero-fee and already runs for every account; send

    `false` to opt out. `collateral=fund` requires both `max_bridge_fee_usdc`

    and `max_funding_wait_ms` and is rejected `400` naming the missing cap;

    either cap without `fund` is likewise a `400`. The response gains

    `funding`, `null` whenever no funding work ran. FIX sessions carry the same

    fields on `NewOrderSingle(D)` as optional tags `5701`–`5704`; a session that

    sends none of them is unchanged.
  contact:
    name: Kairos
    url: https://app.kairos.trade/docs/api-reference
  termsOfService: https://kairos.trade/terms
servers:
  - url: https://execution.kairos.trade
    description: Production (central primary, us-east-1)
  - url: https://eu-west-1-polymarket.executor.kairos.trade
    description: >-
      Production regional execution node — Ireland, colocated with Polymarket.
      Same API surface; assigned at onboarding.
  - url: https://ap-northeast-1-predictfun.executor.kairos.trade
    description: >-
      Production regional execution node — Tokyo, colocated with Predict.fun.
      Same API surface; assigned at onboarding.
  - url: https://staging-execution.kairos.trade
    description: Staging
security:
  - apiKeyClientId: []
    apiKeyKey: []
    apiKeySecret: []
paths:
  /orders/{order_id}/amend:
    post:
      tags:
        - Orders
      summary: Reprice a resting limit order in place
      description: >-
        Moves a resting limit order's price in one venue round trip. The order
        keeps its Kairos `order_id` AND its venue `exchange_order_id` — that is
        the property this endpoint exists to provide: no supersession to record,
        and no second order whose fill could be booked twice. The
        cancel-and-replace it replaces leaves the book empty of the order for
        the whole cancel round trip and mints a second id to reconcile.

        **Venue support.** Only venues advertising `supports_native_amend` in
        `GET /exchanges/{exchange_id}/capabilities` — today `kalshi` alone.
        Anything else is `409` `EXCHANGE_AMEND_UNSUPPORTED`, answered before the
        order's own state is inspected so the verdict is a permanent property of
        the venue rather than of this moment. There is deliberately NO internal
        cancel-and-replace fallback: that substitution changes queue position,
        order identity and the caller's failure modes, so it is the caller's
        decision.

        **Reprice only.** `quantity` is accepted solely to be refused with `409`
        `EXCHANGE_AMEND_QUANTITY_UNSUPPORTED`, before the order is read —
        refused rather than silently ignored so a caller never believes it
        resized. Limit orders only; `price` must be strictly between 0 and 1, in
        the order's own outcome's terms.

        **This endpoint almost always returns `200`.** `success: false` is an
        ambiguous venue outcome, never a transport failure — the same convention
        `POST /orders/{order_id}/cancel` uses, because a 5xx invites a retry
        against an order that may already have been amended. It covers a
        terminal order, an order not yet at the venue, and a venue that refused;
        on every one of those the order is untouched and still resting on its
        old price. Branch on `success`, not on the status code.

        **Queue position.** Kalshi preserves it only when an amend DECREASES
        size; a reprice forfeits it — but so does the cancel/replace this
        replaces, so nothing is lost. The win is the closed off-book window, not
        priority.

        **Admission.** Runs the same checks a submit does — kill switch,
        side/kind/TIF circuit breakers evaluated against the order's own side
        and kind (an amend changes neither), and the order rate limit. An
        immediate fill from a crossing amend is booked through the normal poller
        path, not from this response.

        **Auth & scope.** Requires `trade:execute` and ownership of the order.
      operationId: amendOrder
      parameters:
        - name: order_id
          in: path
          required: true
          description: Internal Kairos order id.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderAmendRequest'
      responses:
        '200':
          description: >-
            Amend outcome. Check `success` — a `200` does not guarantee the
            order was actually amended (see description).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderAmendResponse'
        '400':
          description: >-
            The order is not a limit order, the requested price is not strictly
            between 0 and 1, or the price is off the market's tick grid —
            checked before any venue request, against the same grid a submit is
            held to, with the message naming the tick
            (`VALIDATION_INVALID_ORDER` / `VALIDATION_INVALID_PRICE`). A
            non-UUID `{order_id}` or a missing/unparseable JSON body is rejected
            before the handler runs and answers `400` with a plain-text body
            rather than the `OrderErrorResponse` envelope. Both representations
            are modelled below; branch on the response `Content-Type` and do not
            assume `error_details` is present on every `400` from this path.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
            text/plain:
              schema:
                type: string
                description: >-
                  Rejection emitted before the handler runs — a malformed
                  `{order_id}` or a missing/unparseable JSON body. Carries no
                  `error_details`; the text is not a stable contract, so branch
                  on the status and media type, never on its wording.
                example: >-
                  Invalid URL: Cannot parse `order_id` with value `not-a-uuid`
                  to a `Uuid`
        '401':
          $ref: '#/components/responses/ExecUnauthorized'
        '403':
          description: >-
            Missing `trade:execute` scope, or API-key access to the order's
            venue is disabled (`AUTH_INSUFFICIENT_SCOPE`). This endpoint is also
            behind the service-token/CSRF/API-key mutation gate, whose own
            rejections are `403` with a minimal `error` body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
        '404':
          description: >-
            No order with that id, or the order belongs to another user — both
            answer the same way (`VALIDATION_INVALID_ORDER`) so an id is not a
            probe.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
        '409':
          description: >-
            The venue has no native amend or is unknown
            (`EXCHANGE_AMEND_UNSUPPORTED`), or the request carried a `quantity`
            (`EXCHANGE_AMEND_QUANTITY_UNSUPPORTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
        '429':
          description: >-
            Order rate limit exceeded — an amend charges the same limiter a
            submit does. Carries `VALIDATION_INVALID_ORDER`; match the status,
            not the code.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
        '500':
          description: The order could not be read (`INTERNAL_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
        '503':
          description: >-
            Execution disabled by a circuit breaker (`MARKET_PAUSED`), no
            executor registered for the venue (`EXCHANGE_ERROR`), venue
            credentials unavailable (`AUTH_CREDENTIALS_NOT_FOUND`), or the
            API-key provider-access lookup was unavailable (reported with
            `AUTH_INSUFFICIENT_SCOPE` — match the status here, not the code).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderErrorResponse'
components:
  schemas:
    OrderAmendRequest:
      type: object
      description: >-
        Request body for `POST /orders/{order_id}/amend`. Reprice only —
        `quantity` exists solely to be refused.
      properties:
        price:
          type: number
          description: >-
            New limit price, strictly greater than 0 and less than 1, in the
            order's OWN outcome's terms (send a Kalshi `No` order's `No` price;
            do not complement it client-side). Omitted re-sends the price the
            order already has.
        quantity:
          type: number
          description: >-
            Accepted only to be REFUSED with `409`
            `EXCHANGE_AMEND_QUANTITY_UNSUPPORTED`, before the order is even
            read. This route reprices; it does not resize. Refused rather than
            ignored so a caller never believes an order was resized when it was
            not.
    OrderAmendResponse:
      type: object
      description: >-
        Response for `POST /orders/{order_id}/amend`. Returned with HTTP 200
        even when `success` is `false`.
      required:
        - success
        - order_id
      properties:
        success:
          type: boolean
          description: >-
            Whether the venue confirmed the amend. `false` is an AMBIGUOUS
            outcome, not an error status — the amend may have been refused, or
            it may have reached the venue and been applied without us learning
            so. That ambiguity is why it is not a 5xx, which would invite a
            blind retry against an order that may already carry the new price. A
            non-amendable status answers 200 with a false `success` too. On any
            `false`, re-read the order before acting on it.
        order_id:
          type: string
          format: uuid
          description: >-
            The id that was addressed, always unchanged. An amend never mints a
            new order.
        exchange_order_id:
          type: string
          description: >-
            The venue's order id, also unchanged — Kalshi AmendOrder v2 answers
            with the same id it was given, so there is no supersession to
            record. Omitted only on the pre-submission path, where the order has
            no venue identity yet.
        price:
          type: string
          description: >-
            Decimal string. The new resting price on success. On an unsuccessful
            path it is the price we last recorded — the resting price when the
            venue explicitly refused, but only our last known value when the
            call failed in transit or came back unreadable, where the venue may
            in fact hold the new price. Omitted when unknown.
        quantity:
          type: string
          description: >-
            Decimal string, the order's total size — always the size it was
            placed for. This route never resizes. Omitted when unknown.
        message:
          type: string
          description: >-
            Human-readable outcome detail, e.g. "Order amended on exchange",
            "Order cannot be amended - status is filled", or "Could not amend on
            the exchange: <reason>". A success whose row write failed reads
            "Order amended on exchange (price not persisted)".
    OrderErrorResponse:
      type: object
      description: >-
        Standard structured error body returned by every endpoint that surfaces
        an `ExecutionError`/`ApiError` (order submission, cancel-all, and the
        other endpoints noted as returning this schema). Endpoints noted as
        "empty body (status code only)" do NOT use this shape.
      required:
        - error
        - error_details
      properties:
        error:
          type: string
          description: >-
            Same text as `error_details.message`, kept for backwards-compatible
            consumers.
        code:
          type: string
          nullable: true
          description: >-
            Debug-formatted copy of `error_details.code` (e.g.
            `"FundsInsufficientUsdc"` — PascalCase, NOT the SCREAMING_SNAKE_CASE
            wire code in `error_details.code`).
        error_details:
          $ref: '#/components/schemas/OrderErrorDetails'
    OrderErrorDetails:
      type: object
      description: >-
        Structured, machine-readable error payload shared across order-execution
        error responses.
      required:
        - code
        - message
        - actions
      properties:
        code:
          $ref: '#/components/schemas/OrderErrorCode'
        message:
          type: string
          description: Human-readable error message.
        details:
          type: string
          nullable: true
          description: Optional extended explanation.
        metadata:
          nullable: true
          description: >-
            Error-type-specific structured metadata (shape varies by `code` —
            e.g. `{type: "insufficient_funds", required, available, currency,
            shortfall}` or `{type: "rate_limit", retry_after_seconds, limit,
            window}`). Untagged union; treat as opaque unless you match on the
            embedded `type` field.
          additionalProperties: true
        actions:
          type: array
          description: Zero or more actionable recovery steps a client can surface.
          items:
            $ref: '#/components/schemas/OrderErrorAction'
    OrderErrorCode:
      type: string
      description: Machine-readable error code, SCREAMING_SNAKE_CASE on the wire.
      enum:
        - AUTH_CREDENTIALS_NOT_FOUND
        - AUTH_CREDENTIALS_INVALID
        - AUTH_POLICY_OUTDATED
        - AUTH_INSUFFICIENT_SCOPE
        - AUTH_POLYMARKET_API_KEY_INVALID
        - AUTH_TURNKEY_WALLET_NOT_FOUND
        - AUTH_TURNKEY_AUTH_FAILED
        - AUTH_TURNKEY_SIGNING_FAILED
        - VALIDATION_INVALID_ORDER
        - VALIDATION_INVALID_PRICE
        - VALIDATION_INVALID_SIZE
        - VALIDATION_MARKET_NOT_FOUND
        - VALIDATION_TOKEN_NOT_FOUND
        - VALIDATION_ORDER_EXPIRED
        - VALIDATION_MARKET_NOT_SETTLED_ON_CHAIN
        - AUTH_IDENTITY_MISMATCH
        - FUNDS_INSUFFICIENT_USDC
        - FUNDS_INSUFFICIENT_GAS
        - FUNDS_INSUFFICIENT_TOTAL
        - FUNDS_INSUFFICIENT_BALANCE
        - FUNDS_COLLATERAL_LOCATION
        - ALLOWANCE_USDC_NOT_SET
        - ALLOWANCE_CTF_NOT_SET
        - ALLOWANCE_APPROVAL_FAILED
        - NETWORK_RPC_ERROR
        - NETWORK_TIMEOUT
        - NETWORK_HTTP_ERROR
        - NETWORK_ERROR
        - EXCHANGE_POLYMARKET_API_ERROR
        - EXCHANGE_POLYMARKET_UNAUTHORIZED
        - EXCHANGE_POLYMARKET_MARKET_CLOSED
        - EXCHANGE_POLYMARKET_RATE_LIMITED
        - EXCHANGE_KALSHI_API_ERROR
        - EXCHANGE_KALSHI_UNAUTHORIZED
        - EXCHANGE_KALSHI_MARKET_CLOSED
        - EXCHANGE_ERROR
        - EXCHANGE_UNSUPPORTED
        - SIGNATURE_EIP712_FAILED
        - SIGNATURE_ERROR
        - GAS_ESTIMATION_FAILED
        - TRANSACTION_FAILED
        - GAS_SPONSORSHIP_FAILED
        - TRANSACTION_TIMEOUT
        - MARKET_NOT_READY
        - MARKET_PAUSED
        - MARKET_INSUFFICIENT_LIQUIDITY
        - ORDERBOOK_UNAVAILABLE
        - MARKET_FOK_NOT_FILLED
        - MARKET_ORDER_SIZE_EXCEEDS_MAX
        - DATABASE_ERROR
        - DATABASE_CREDENTIAL_DECRYPTION_FAILED
        - INTERNAL_ERROR
      example: FUNDS_INSUFFICIENT_USDC
    OrderErrorAction:
      type: object
      description: An actionable recovery step a client can surface to the end user.
      required:
        - action
        - label
        - primary
      properties:
        action:
          $ref: '#/components/schemas/OrderErrorActionType'
        label:
          type: string
          description: Human-readable button/link label.
          example: Add Funds
        url:
          type: string
          nullable: true
        primary:
          type: boolean
          description: Whether this is the primary/recommended action.
    OrderErrorActionType:
      type: string
      description: Machine-readable recovery-action identifier, snake_case on the wire.
      enum:
        - enable_trading
        - reenable_trading
        - update_policies
        - regenerate_api_key
        - contact_support
        - retry
        - review_order
        - adjust_price
        - adjust_size
        - browse_markets
        - refresh_market
        - create_new_order
        - add_funds
        - reduce_size
        - swap_usdc
        - add_matic
        - approve_usdc
        - approve_ctf
        - retry_approval
        - wait_and_retry
        - check_status
        - use_limit_order
        - update_kalshi_credentials
        - enable_bridge_funding
  responses:
    ExecUnauthorized:
      description: >
        No valid credential presented — missing/invalid API-key headers or an
        invalid/expired session token.
      content:
        application/json:
          schema:
            type: object
            required:
              - error
            properties:
              error:
                type: string
                example: Unauthorized
  securitySchemes:
    apiKeyClientId:
      type: apiKey
      in: header
      name: X-Client-Id
      description: >-
        Credential client id (`kairos_ck_...`). Must be sent together with
        X-Api-Key and X-Api-Secret.
    apiKeyKey:
      type: apiKey
      in: header
      name: X-Api-Key
      description: 64-char hex API key.
    apiKeySecret:
      type: apiKey
      in: header
      name: X-Api-Secret
      description: 64-char hex API secret.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.