> ## 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.

# Start a wallet action (setup, order, cancel, login)

> Starts one action for a self-custody wallet and returns its first **step**. Kairos never holds the wallet's key: each step either asks the wallet for one EIP-1193 request (`status: needs_signature`) or reports progress (`working`) or the outcome (`done` / `failed`).

**The step loop.**
1. `POST /v3/actions` → a step.
2. While `status` is `needs_signature`: switch the wallet to `chainId` (when non-null), run `request` through the wallet's EIP-1193 provider, and `POST /v3/actions/{id}` with `{"signature": "0x…"}` (or `{"result": …}` for `wallet_sendCalls`). Answer before `expiresAtMs`.
3. While `status` is `working`: `GET /v3/actions/{id}` every 1–2 s (or after `holdUntilMs` when present).
4. Stop at `done` (read `result`) or `failed` (read `error` and `errorCode`).

**Action types.** `setup` prepares the wallet on each requested venue; `order` places one order and runs any missing setup first, in the same action; `cancel` cancels orders on every venue with one Polygon signature; `login` returns a sign-in token for the private reads. There are no other action types.

**Body rules.** Field names are **camelCase only**: unknown fields — including snake_case spellings such as `time_in_force`, `post_only`, `client_order_id`, `expires_at`, `order_ids` — are silently ignored, so a snake_case order becomes a `GTC`, non-post-only order without a client id. `price` and `size` must be JSON **strings**; a JSON number is rejected with a plain-text `422`. `expiresAt` is an integer (Unix seconds).

**Idempotency.** `Idempotency-Key` is **not** honoured here. Use `clientOrderId` on orders: the same `clientOrderId` with the same terms returns the existing order (nothing is signed), and different terms are refused with `CLIENT_ORDER_ID_CONFLICT`.

**Timing.** An action lives 15 minutes from creation and stays readable for 10 more minutes, then reads `404`. Polymarket and Predict.fun order signatures are accepted for about 60 s, Hyperliquid order signatures for 300 s, and cancel signatures (every venue) for 300 s; `expiresAtMs` on the step is authoritative.

**Errors before the first signature are HTTP errors.** When the action fails before the wallet was ever asked to sign (for example every requested venue is unavailable), the response is that error's status and body rather than a `failed` step.

Send `X-Kairos-Wallet-Api: 2`; without it this route serves the original Polymarket-only contract.



## OpenAPI

````yaml /openapi/wallet-api.json post /v3/actions
openapi: 3.1.0
info:
  title: Kairos Partner API
  version: 1.0.0
  description: >-
    Self-custody trading for wallets and apps: Kairos builds each step, the
    user's wallet signs it. Every operation is under `/v3`. Guides:
    https://docs.kairos.trade/wallets/overview
servers:
  - url: https://execution.kairos.trade
    description: Production
  - url: https://staging-execution.kairos.trade
    description: Staging
security: []
tags:
  - name: Actions
    description: >-
      Start `setup`, `order`, `cancel` and `login` actions and answer their
      steps. Live. Send `X-Kairos-Wallet-Api: 2`.
  - name: Reads
    description: >-
      Wallet readiness and balances per venue (partner id or API key, no wallet
      proof), and the wallet's orders and positions (also needs `Authorization:
      SIWE <token>` from a `login` action). Live.
  - name: Trade plans (preview)
    description: >-
      One buy order authorized up front in one signature batch, with a fee
      ceiling and deadlines, optionally funded by bridging the exact shortfall
      from Polygon or Arbitrum USDC. Preview: staging only; production returns
      `503` until release.
  - name: Fees (preview)
    description: >-
      Trading fees from trade plans accrue as a debt per wallet and are paid in
      one settlement — a gasless signature on Polymarket, a USDT transfer on
      Predict.fun. Preview: staging only.
  - name: On-chain intents (preview)
    description: >-
      Split, merge or redeem conditional tokens on a Polymarket deposit wallet
      with two signatures and no gas; Kairos confirms from the finalized
      receipt. Preview: staging only; production returns `503` until release.
paths:
  /v3/actions:
    post:
      tags:
        - Actions
      summary: Start a wallet action (setup, order, cancel, login)
      description: >-
        Starts one action for a self-custody wallet and returns its first
        **step**. Kairos never holds the wallet's key: each step either asks the
        wallet for one EIP-1193 request (`status: needs_signature`) or reports
        progress (`working`) or the outcome (`done` / `failed`).


        **The step loop.**

        1. `POST /v3/actions` → a step.

        2. While `status` is `needs_signature`: switch the wallet to `chainId`
        (when non-null), run `request` through the wallet's EIP-1193 provider,
        and `POST /v3/actions/{id}` with `{"signature": "0x…"}` (or `{"result":
        …}` for `wallet_sendCalls`). Answer before `expiresAtMs`.

        3. While `status` is `working`: `GET /v3/actions/{id}` every 1–2 s (or
        after `holdUntilMs` when present).

        4. Stop at `done` (read `result`) or `failed` (read `error` and
        `errorCode`).


        **Action types.** `setup` prepares the wallet on each requested venue;
        `order` places one order and runs any missing setup first, in the same
        action; `cancel` cancels orders on every venue with one Polygon
        signature; `login` returns a sign-in token for the private reads. There
        are no other action types.


        **Body rules.** Field names are **camelCase only**: unknown fields —
        including snake_case spellings such as `time_in_force`, `post_only`,
        `client_order_id`, `expires_at`, `order_ids` — are silently ignored, so
        a snake_case order becomes a `GTC`, non-post-only order without a client
        id. `price` and `size` must be JSON **strings**; a JSON number is
        rejected with a plain-text `422`. `expiresAt` is an integer (Unix
        seconds).


        **Idempotency.** `Idempotency-Key` is **not** honoured here. Use
        `clientOrderId` on orders: the same `clientOrderId` with the same terms
        returns the existing order (nothing is signed), and different terms are
        refused with `CLIENT_ORDER_ID_CONFLICT`.


        **Timing.** An action lives 15 minutes from creation and stays readable
        for 10 more minutes, then reads `404`. Polymarket and Predict.fun order
        signatures are accepted for about 60 s, Hyperliquid order signatures for
        300 s, and cancel signatures (every venue) for 300 s; `expiresAtMs` on
        the step is authoritative.


        **Errors before the first signature are HTTP errors.** When the action
        fails before the wallet was ever asked to sign (for example every
        requested venue is unavailable), the response is that error's status and
        body rather than a `failed` step.


        Send `X-Kairos-Wallet-Api: 2`; without it this route serves the original
        Polymarket-only contract.
      operationId: startWalletAction
      parameters:
        - $ref: '#/components/parameters/WalletApiVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WalletActionRequest'
            examples:
              setup:
                summary: Set up Polymarket and Predict.fun
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: setup
                    venues:
                      - polymarket
                      - predictfun
              order:
                summary: GTD limit buy on Predict.fun
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: order
                    market: predictfun:1027194
                    outcome: 'Yes'
                    side: buy
                    price: '0.52'
                    size: '10'
                    timeInForce: GTD
                    expiresAt: 1793300000
                    clientOrderId: acme-7f3a91
              hyperliquidOrder:
                summary: Hyperliquid outcome-market buy
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: order
                    market: hyperliquid:1
                    outcome: 'Yes'
                    side: buy
                    price: '0.45'
                    size: '25'
                    timeInForce: GTC
                    clientOrderId: '0x7d2f0c1e9a4b4f6c8e3d2a1b0c9f8e7d'
              cancel:
                summary: Cancel two orders
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: cancel
                    orderIds:
                      - 3f2b8c1e-5d4a-4e6f-9b7c-1a2d3e4f5a6b
                      - 9c8d7e6f-5a4b-4c3d-8e2f-1a0b9c8d7e6f
              cancelAll:
                summary: Cancel every open order on every venue
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: cancel
                    all: true
              login:
                summary: Get a sign-in token for private reads
                value:
                  wallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
                  action:
                    type: login
      responses:
        '200':
          description: >-
            The action's first step. Usually `needs_signature`; `done` when
            nothing needs signing (a wallet already set up, or a `clientOrderId`
            that already names this exact order).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletActionStep'
              examples:
                needsSignature:
                  summary: Predict.fun order waiting for its signature
                  value:
                    id: 6a1f9e2c-3b4d-4c5e-8f6a-7b8c9d0e1f2a
                    type: order
                    status: needs_signature
                    request:
                      method: eth_signTypedData_v4
                      params:
                        - '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
                        - >-
                          {"types":{"EIP712Domain":[…],"Order":[…]},"domain":{"name":"predict.fun
                          CTF
                          Exchange","version":"1","chainId":56,"verifyingContract":"0x…"},"primaryType":"Order","message":{…}}
                    description: Confirm this Predict.fun order
                    chainId: '0x38'
                    venue: predictfun
                    expiresAtMs: 1793130772999
                    result: null
                    error: null
                alreadyReady:
                  summary: Setup on a wallet that is already ready
                  value:
                    id: 0b7e4d2a-1c3f-4a5b-9d8e-7f6a5b4c3d2e
                    type: setup
                    status: done
                    request: null
                    description: null
                    chainId: null
                    venue: null
                    result:
                      ready: true
                      venues:
                        polymarket:
                          ready: true
                          tradingWallet: '0x2f9c4b8e1d7a6c5b4a3f2e1d0c9b8a7f6e5d4c3b'
                          transactionHash: null
                      tradingWallet: '0x2f9c4b8e1d7a6c5b4a3f2e1d0c9b8a7f6e5d4c3b'
                      transactionHash: null
                    error: null
        '400':
          description: >-
            JSON `{"error": …}` for a request Kairos refuses before any
            signature, for example:

            - `wallet must be a 0x address`; `partner id header is not valid
            ASCII` or a malformed partner id

            - `unknown venue "x"`; `market is required`; `market must be
            venue:market_id, e.g. predictfun:1027194, or a Polymarket condition
            id`; `a Polymarket market is its condition id (0x + 64 hex)`; `a
            Predict.fun market is predictfun:<market_id>`; `a Hyperliquid market
            is hyperliquid:<market_id>`; `unknown venue in market "…"`

            - `unknown Polymarket market; for another venue use
            venue:market_id`; `unknown Predict.fun market`; `unknown Hyperliquid
            market or outcome: market=… outcome="…"`

            - `side must be buy or sell`; `price must be a decimal string`;
            `timeInForce must be GTC, GTD, FOK, FAK, or IOC`; `expiresAt is
            required for GTD`

            - Hyperliquid: `timeInForce on Hyperliquid must be GTC, FAK, or
            IOC`; `postOnly needs timeInForce GTC`; `clientOrderId must be 0x
            followed by 32 lowercase hex characters on this exchange`; price,
            size and minimum-notional refusals

            - `clientOrderId uses a reserved prefix` (ids starting `trade-plan:`
            are reserved); a blank or over-128-character `clientOrderId`

            - `pass either orderIds or all: true`; `at most 100 orderIds per
            cancel`


            A **plain-text** `400` (not JSON) means the body is not valid JSON.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
              example:
                error: 'pass either orderIds or all: true'
            text/plain:
              schema:
                $ref: '#/components/schemas/WalletPlainTextError'
        '401':
          $ref: '#/components/responses/WalletTenantUnauthorized'
        '403':
          description: >-
            Tenant refusals (`unknown partner id`; `this partner id is used with
            its API key; send the key instead of X-Kairos-Partner-Id`; `this API
            key lacks the trade:execute scope`; `the wallet API is not enabled
            for this account`), or a trading refusal: `API-key access to <venue>
            is disabled`, `this wallet is disabled`, `Hyperliquid trading is
            disabled for this wallet`, `external-signing execution is not
            enabled for this account`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
        '409':
          description: >-
            - `this wallet is not set up yet; run a setup action first` —
            `cancel` and `login` need a wallet that has completed setup on at
            least one venue.

            - `this wallet is linked to a Kairos account; trade with it in the
            Kairos app`.

            - `client_order_id already names a different order` with `code` and
            `errorCode` = `CLIENT_ORDER_ID_CONFLICT`.

            - `code: POLYMARKET_V2_TRADING_DISABLED` — the market trades on a
            Polymarket contract version Kairos has not enabled yet.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
              example:
                error: client_order_id already names a different order
                code: CLIENT_ORDER_ID_CONFLICT
                errorCode: CLIENT_ORDER_ID_CONFLICT
        '413':
          $ref: '#/components/responses/WalletPayloadTooLarge'
        '415':
          $ref: '#/components/responses/WalletUnsupportedMediaType'
        '422':
          description: >-
            **Plain text** when the body is valid JSON of the wrong shape: a
            missing required field, an unknown `type`, a non-UUID in `orderIds`,
            `price`/`size` sent as numbers, or `expiresAt` sent as a string.


            JSON when Hyperliquid refuses before signing: `not enough USDC on
            Hyperliquid for this order: it needs N, spot has S and perps can
            release W`.
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/WalletPlainTextError'
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
        '429':
          $ref: '#/components/responses/WalletRateLimited'
        '500':
          $ref: '#/components/responses/WalletInternalError'
        '502':
          description: >-
            A venue did not answer cleanly, e.g. `Hyperliquid is unavailable;
            retry shortly`, `Predict.fun is unavailable; retry shortly`. Nothing
            was signed; retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
        '503':
          description: >-
            `partner registry unavailable; retry shortly`; `rate limiter
            unavailable`; `<venue> is not available right now`; `no venue is
            available right now`; `wallet sign-in is not configured` (login);
            `API-key access to <venue> is unavailable`. Nothing was signed;
            retry later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
        '504':
          description: >-
            `… did not answer in time; GET /v3/actions/{id} before retrying`.
            The action may have progressed: read it before starting another.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletApiError'
      security:
        - walletPartnerId: []
        - apiKeyClientId: []
          apiKeyKey: []
          apiKeySecret: []
components:
  parameters:
    WalletApiVersion:
      name: X-Kairos-Wallet-Api
      in: header
      required: true
      description: >-
        Wallet API contract version. Send `2` — the multi-venue contract this
        reference documents. Without the header (or with any other value)
        `/v3/actions` and `/v3/wallets/*` serve the original Polymarket-only
        contract, which ignores venue prefixes, `expiresAt` and `setup.venues`,
        omits `chainId`, `venue` and `fallbackRequests` from steps, refuses
        orders on a wallet that is not set up (`409`), and returns snake_case
        order and position rows.
      schema:
        type: string
        enum:
          - '2'
      example: '2'
  schemas:
    WalletActionRequest:
      type: object
      description: >-
        Body of `POST /v3/actions`. **camelCase only** — unknown fields,
        including snake_case spellings, are ignored without error.
      required:
        - wallet
        - action
      properties:
        wallet:
          $ref: '#/components/schemas/WalletAddress'
        action:
          $ref: '#/components/schemas/WalletAction'
    WalletActionStep:
      type: object
      description: >-
        The current step of an action. Every response from `/v3/actions` and
        `/v3/actions/{id}` is one of these.
      required:
        - id
        - type
        - status
        - request
        - description
        - chainId
        - venue
        - result
        - error
      properties:
        id:
          type: string
          format: uuid
          description: Action id. One id for the whole action, across venues.
        type:
          type: string
          enum:
            - setup
            - order
            - cancel
            - login
        status:
          $ref: '#/components/schemas/WalletStepStatus'
        request:
          description: What the wallet must run. Non-null only while `needs_signature`.
          oneOf:
            - $ref: '#/components/schemas/WalletEip1193Request'
            - type: 'null'
        description:
          type:
            - string
            - 'null'
          description: >-
            Text to show with the wallet prompt (e.g. `Confirm this Hyperliquid
            order`), or progress text while `working`.
        chainId:
          type:
            - string
            - 'null'
          description: >-
            EIP-155 chain id (hex) the wallet must be on to answer — `0x89`
            Polygon, `0x38` BNB Chain, `0xa4b1` Arbitrum One (`0x66eee` Arbitrum
            Sepolia on Hyperliquid testnet). `null` for `personal_sign` and when
            nothing is asked.
          example: '0x38'
        venue:
          oneOf:
            - $ref: '#/components/schemas/WalletVenue'
            - type: 'null'
          description: Venue of the current prompt; `null` when finished and on cancels.
        fallbackRequests:
          type: array
          description: >-
            Present with `wallet_sendCalls` requests only — the same calls as
            individual `eth_sendTransaction` requests, for wallets without
            EIP-5792. Send them in order and answer with `{"result": ["0xhash",
            …]}`.
          items:
            $ref: '#/components/schemas/WalletEip1193Request'
        expiresAtMs:
          type: integer
          format: int64
          description: >-
            Last accepted millisecond (Unix ms) for answering this prompt.
            Answers after it fail the action with `SIGNATURE_EXPIRED`.
          example: 1793130772999
        holdUntilMs:
          type: integer
          format: int64
          description: >-
            On a `working` order the venue is still matching — poll again after
            this time (Unix ms). `result` then holds a provisional outcome with
            `final: false`.
        result:
          description: >-
            Outcome on `done` (and the provisional outcome of a held order).
            Shape depends on `type`.
          oneOf:
            - $ref: '#/components/schemas/WalletSetupResult'
            - $ref: '#/components/schemas/WalletOrderResult'
            - $ref: '#/components/schemas/WalletCancelResult'
            - $ref: '#/components/schemas/WalletLoginResult'
            - type: 'null'
        error:
          type:
            - string
            - 'null'
          description: Why the action failed; non-null on `failed`.
        errorCode:
          $ref: '#/components/schemas/WalletErrorCode'
    WalletApiError:
      type: object
      description: >-
        Wallet API error body. `error` is a human-readable message; `code` is
        set on a few structured refusals (e.g. `CLIENT_ORDER_ID_CONFLICT`,
        `POLYMARKET_V2_TRADING_DISABLED`, `SIGNATURE_EXPIRED`); `errorCode`
        mirrors `code` where the same refusal can also surface on a failed step.
        Most `/v3` errors carry only `error`. Framework rejections (malformed
        JSON, wrong shape, missing content type) are plain text, not this body.
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable message.
        code:
          type: string
          description: Stable code, present on structured refusals only.
        errorCode:
          type: string
          description: >-
            Same as `code`; present where the refusal can also appear as a
            failed step's `errorCode`.
      example:
        error: unknown or expired action
    WalletPlainTextError:
      type: string
      description: >-
        A plain-text (non-JSON) rejection from request parsing — malformed JSON,
        a body of the wrong shape, a missing `Content-Type`, a malformed path id
        or query. Do not JSON-parse it.
      example: >-
        Failed to deserialize the JSON body into the target type: action.price:
        invalid type: floating point `0.52`, expected a string
    WalletAddress:
      type: string
      description: EVM address, `0x` + 40 hex.
      pattern: ^0x[0-9a-fA-F]{40}$
      example: '0x8ba1f109551bd432803012645ac136ddd64dba72'
    WalletAction:
      description: The action to run, tagged by `type`.
      oneOf:
        - $ref: '#/components/schemas/WalletSetupAction'
        - $ref: '#/components/schemas/WalletOrderAction'
        - $ref: '#/components/schemas/WalletCancelAction'
        - $ref: '#/components/schemas/WalletLoginAction'
      discriminator:
        propertyName: type
        mapping:
          setup: '#/components/schemas/WalletSetupAction'
          order: '#/components/schemas/WalletOrderAction'
          cancel: '#/components/schemas/WalletCancelAction'
          login: '#/components/schemas/WalletLoginAction'
    WalletStepStatus:
      type: string
      description: >-
        `needs_signature` — run `request` and answer; `working` — poll; `done` —
        read `result`; `failed` — read `error` and `errorCode`.
      enum:
        - needs_signature
        - working
        - done
        - failed
    WalletEip1193Request:
      type: object
      description: >-
        An EIP-1193 request to run through the wallet's provider exactly as
        given (`provider.request({method, params})`).

        - `eth_signTypedData_v4`: `params` = `[address, typedData]` —
        `typedData` is a **JSON string**; pass it unchanged.

        - `personal_sign`: `params` = `[hexMessage, address]`.

        - `wallet_sendCalls` (EIP-5792): `params` = `[{version, chainId, from,
        atomicRequired, calls}]`; the wallet pays gas.

        - `eth_sendTransaction`: `params` = `[{from, to, data, value,
        chainId?}]`; the wallet pays gas.
      required:
        - method
        - params
      properties:
        method:
          type: string
          enum:
            - eth_signTypedData_v4
            - personal_sign
            - wallet_sendCalls
            - eth_sendTransaction
        params:
          type: array
          items: {}
      example:
        method: personal_sign
        params:
          - 0x6b6169726f732e7472616465207761…
          - '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
    WalletVenue:
      type: string
      description: Venue id. `predict.fun` is accepted as an input alias of `predictfun`.
      enum:
        - polymarket
        - predictfun
        - hyperliquid
    WalletSetupResult:
      type: object
      description: '`result` of a `setup` action.'
      required:
        - ready
      properties:
        ready:
          type: boolean
          description: Every requested venue is ready.
        venues:
          type: object
          description: Outcome per requested venue.
          additionalProperties:
            $ref: '#/components/schemas/WalletSetupVenueResult'
        tradingWallet:
          type:
            - string
            - 'null'
          description: >-
            Polymarket's trading wallet, when Polymarket was set up; `null` when
            Polymarket's setup failed.
        transactionHash:
          type:
            - string
            - 'null'
          description: Polymarket's approval transaction, when Polymarket was set up.
      example:
        ready: false
        venues:
          polymarket:
            ready: true
            tradingWallet: '0x2f9c4b8e1d7a6c5b4a3f2e1d0c9b8a7f6e5d4c3b'
            transactionHash: '0x7c3e9a1b5d2f4c6e8a0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c'
          predictfun:
            ready: false
            next: fund
            tradingWallet: '0x8ba1f109551bd432803012645ac136ddd64dba72'
            venueDetail:
              reason: >-
                needs about 0.0004 BNB on BNB Chain to pay gas for 2 Predict.fun
                approvals
              approvals: 2
              gasWei: '400000000000000'
        tradingWallet: '0x2f9c4b8e1d7a6c5b4a3f2e1d0c9b8a7f6e5d4c3b'
        transactionHash: '0x7c3e9a1b5d2f4c6e8a0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c'
    WalletOrderResult:
      type: object
      description: >-
        `result` of an `order` action. No fee fields: Kairos and venue fees are
        not reported here.
      required:
        - orderId
        - status
        - final
        - success
      properties:
        orderId:
          type: string
          format: uuid
          description: Kairos order id — use it to cancel.
        status:
          type: string
          description: >-
            Order status: Polymarket's answer (`matched`, `live`, `delayed`,
            `unmatched`) or the Kairos order status on Hyperliquid (`executing`,
            `live`, `partial`, `filled`, `cancelled`, `failed`, …). Read `final`
            and `success` rather than matching on this.
          example: matched
        venueOrderId:
          type:
            - string
            - 'null'
        venueStatus:
          type:
            - string
            - 'null'
          description: The venue's first answer.
        filledSize:
          type:
            - string
            - 'null'
          description: Shares filled; `null` when unknown.
          example: '7.140348'
        filledNotional:
          type:
            - string
            - 'null'
          description: Collateral spent or received, before venue fees.
          example: '4.069998'
        avgPrice:
          type:
            - string
            - 'null'
          example: '0.57'
        final:
          type: boolean
          description: Matching is finished.
        success:
          type: boolean
          description: '`false` when the order did nothing, failed, or is not confirmed yet.'
        message:
          type:
            - string
            - 'null'
          description: >-
            Set when `success` is false:

            - `No fill: the venue matched none of this order`

            - `The order failed at the venue; any fills shown were reverted`

            - `The venue rejected the order`

            - `Outcome not confirmed yet; check this wallet's orders before
            placing it again`
        venue:
          $ref: '#/components/schemas/WalletVenue'
        market:
          type: string
          description: '`venue:market_id`.'
    WalletCancelResult:
      type: object
      description: >-
        `result` of a `cancel` action. If nothing was confirmed and a venue may
        have acted, the action fails with `CANCEL_OUTCOME_UNKNOWN` and keeps
        this result.
      required:
        - cancelled
        - failed
        - venueDetail
      properties:
        cancelled:
          type: array
          items:
            type: string
          description: Order ids cancelled.
        alreadyClosed:
          type: array
          items:
            type: string
          description: By id — orders already filled, cancelled or expired.
        failed:
          description: By id — the failures; with `all` — the number of failures.
          oneOf:
            - type: array
              items:
                $ref: '#/components/schemas/WalletCancelFailure'
            - type: integer
        venueDetail:
          type: object
          description: >-
            Per venue: `{cancelled, failed}` counts for `all`, or `{error}` when
            the venue could not be reached (`{error, code:
            "CANCEL_OUTCOME_UNKNOWN"}` when it may have cancelled). Empty when
            every venue answered on a by-id cancel.
          additionalProperties:
            type: object
            properties:
              cancelled:
                type: integer
              failed:
                type: integer
              error:
                type: string
              code:
                type: string
      example:
        cancelled:
          - 3f2b8c1e-5d4a-4e6f-9b7c-1a2d3e4f5a6b
        alreadyClosed: []
        failed:
          - orderId: 9c8d7e6f-5a4b-4c3d-8e2f-1a0b9c8d7e6f
            reason: order not found
        venueDetail: {}
    WalletLoginResult:
      type: object
      description: '`result` of a `login` action. Send it as `Authorization: SIWE <token>`.'
      required:
        - token
        - expiresAt
      properties:
        token:
          type: string
          description: Opaque sign-in token (at most 4096 bytes).
        expiresAt:
          type: string
          format: date-time
          description: 24 hours after issue.
    WalletErrorCode:
      type: string
      description: >-
        Machine-readable reason on a `failed` step; the key is omitted when the
        failure has no code. Codes may be added; treat an unknown code as a
        failure whose order was not placed.


        | Code | Meaning | Order placed? |

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

        | `NO_MATCH` | An immediate order (FOK/FAK/IOC) found nothing to match.
        | No |

        | `VENUE_REJECTED` | The venue refused the order. | No |

        | `ORDER_OUTCOME_UNKNOWN` | The submission's outcome is unknown. Read
        the wallet's orders before placing again. | Maybe |

        | `CANCEL_OUTCOME_UNKNOWN` | Some venue may have cancelled. Read the
        wallet's orders. | Maybe |

        | `SIGNATURE_EXPIRED` | The signing window closed before a signature was
        accepted. Nothing was sent. | No |

        | `CLIENT_ORDER_ID_CONFLICT` | The `clientOrderId` names an order with
        different terms. | No |

        | `HYPERLIQUID_AGENT_MISSING` | Hyperliquid no longer lists this
        wallet's trading approval. Run `setup` again. | No |
      enum:
        - NO_MATCH
        - VENUE_REJECTED
        - ORDER_OUTCOME_UNKNOWN
        - CANCEL_OUTCOME_UNKNOWN
        - SIGNATURE_EXPIRED
        - CLIENT_ORDER_ID_CONFLICT
        - HYPERLIQUID_AGENT_MISSING
    WalletSetupAction:
      type: object
      description: >-
        Prepare the wallet to trade. Per venue:

        - **Polymarket** (Polygon, `0x89`): one or two signatures, no gas —
        connect the wallet, then approve its trading wallet (a smart wallet
        owned by the user's address) when it is not approved yet.

        - **Predict.fun** (BNB Chain, `0x38`): when approvals are missing, one
        `wallet_sendCalls` batch the wallet pays BNB gas for; then a sign-in
        signature, no gas. Without enough BNB the venue ends `ready: false,
        next: "fund"`.

        - **Hyperliquid** (Arbitrum, `0xa4b1`; `0x66eee` on testnet): one or two
        signatures, no gas — allow Kairos to place trades for the account (it
        cannot withdraw), plus a builder-fee approval where one applies. An
        account that was never funded on Hyperliquid ends `ready: false, next:
        "fund"`.


        Setup continues past a venue that fails and fails only when every venue
        failed. A wallet already ready returns `done` at once.
      required:
        - type
      properties:
        type:
          type: string
          const: setup
        venues:
          type: array
          description: >-
            Venues to set up. Omitted or empty means every venue offered on this
            deployment. Case-insensitive; duplicates are ignored.
          items:
            $ref: '#/components/schemas/WalletVenue'
    WalletOrderAction:
      type: object
      description: >-
        Place one order. Missing setup on the order's venue runs first, inside
        the same action. Signatures per order: Polymarket and Predict.fun one;
        Hyperliquid one, plus a perps-to-spot transfer prompt when spot USDC is
        short and perps can cover it.


        Hyperliquid (HIP-4 outcome markets): `price` above 0 and below 1 with at
        most 5 decimals, whole-share `size`, `timeInForce` `GTC`, `FAK` or `IOC`
        only, `postOnly` only with `GTC`, a minimum notional on buys, and
        `clientOrderId` as `0x` + 32 lowercase hex.
      required:
        - type
        - market
        - outcome
        - side
        - price
        - size
      properties:
        type:
          type: string
          const: order
        market:
          type: string
          description: >-
            `venue:market_id` — `polymarket:0x<64-hex condition id>`,
            `predictfun:<digits>`, `hyperliquid:<digits>` — or a bare Polymarket
            condition id.
          example: predictfun:1027194
        outcome:
          type: string
          description: Outcome name as the venue lists it, e.g. `Yes`.
          example: 'Yes'
        side:
          type: string
          description: Case-insensitive.
          enum:
            - buy
            - sell
        price:
          type: string
          description: >-
            Limit price on the 0–1 scale, as a **string**. A JSON number is
            rejected (`422`).
          example: '0.52'
        size:
          type: string
          description: Shares, as a **string**.
          example: '10'
        timeInForce:
          type: string
          description: >-
            Case-insensitive. `GTD` requires `expiresAt`. On Predict.fun a `GTC`
            order rests until shortly before the venue session ends.
          enum:
            - GTC
            - GTD
            - FOK
            - FAK
            - IOC
          default: GTC
        postOnly:
          type: boolean
          default: false
        clientOrderId:
          type: string
          maxLength: 128
          description: >-
            Your idempotency key for this order. The same id with the same terms
            returns the existing order without a new signature; different terms
            fail with `CLIENT_ORDER_ID_CONFLICT`. Must not be blank or start
            with `trade-plan:`. Hyperliquid requires `0x` + 32 lowercase hex.
          example: acme-7f3a91
        expiresAt:
          type: integer
          format: int64
          description: >-
            Expiry for `GTD`, Unix **seconds** (an integer, not a string).
            Predict.fun caps it shortly before the venue session ends.
          example: 1793300000
    WalletCancelAction:
      type: object
      description: >-
        Cancel orders on every venue with one signature. Send exactly one of
        `orderIds` (1–100 Kairos order ids) or `all: true`. The cancel is signed
        as Kairos typed data on Polygon (`chainId 0x89`) for every venue, so the
        wallet may be asked to switch to Polygon.
      required:
        - type
      properties:
        type:
          type: string
          const: cancel
        orderIds:
          type: array
          description: >-
            Kairos `orderId`s (UUIDs), not venue order ids. A non-UUID is a
            plain-text `422`.
          minItems: 1
          maxItems: 100
          items:
            type: string
            format: uuid
        all:
          type: boolean
          description: Cancel every open order of this wallet on every venue.
    WalletLoginAction:
      type: object
      description: >-
        Sign in with Ethereum (`personal_sign`, no gas) to get a 24-hour token
        for `Authorization: SIWE <token>`. The wallet must have completed setup
        on at least one venue. The token is stateless: a second login does not
        revoke the first.
      required:
        - type
      properties:
        type:
          type: string
          const: login
    WalletSetupVenueResult:
      type: object
      description: One venue's setup outcome.
      required:
        - ready
      properties:
        ready:
          type: boolean
        tradingWallet:
          $ref: '#/components/schemas/WalletAddress'
        transactionHash:
          type:
            - string
            - 'null'
          description: >-
            Polymarket — the approval transaction, or `null` when none was
            needed.
        sessionExpiresAt:
          type: string
          format: date-time
          description: Predict.fun — when the venue session ends.
        next:
          type: string
          enum:
            - fund
            - blocked
          description: >-
            Why a venue that finished setup is not ready: `fund` (deposit, or
            BNB for gas) or `blocked`.
        venueDetail:
          $ref: '#/components/schemas/WalletVenueDetail'
        error:
          type: string
          description: This venue failed; the others may still have succeeded.
    WalletCancelFailure:
      type: object
      required:
        - orderId
        - reason
      properties:
        orderId:
          type: string
        reason:
          type: string
          description: >-
            The venue's reason, `cancel failed; retry`, `order not found` (also
            for ids that are not this wallet's), `venue unavailable; retry`, or
            an outcome-unknown message.
        code:
          type: string
          description: '`CANCEL_OUTCOME_UNKNOWN` when the venue may have cancelled.'
    WalletVenueDetail:
      type: object
      description: Venue-specific detail. Keys appear only where they apply.
      properties:
        chainId:
          type: string
          description: Chain the venue settles on (hex).
          example: '0x89'
        gasBalance:
          type:
            - string
            - 'null'
          description: Predict.fun — BNB balance for gas, whole units.
        missingApprovals:
          type: integer
          description: Predict.fun — approvals still to send.
        sessionExpiresAt:
          type:
            - string
            - 'null'
          format: date-time
          description: Predict.fun — when the venue session ends.
        network:
          type: string
          description: Hyperliquid — `Mainnet` or `Testnet`.
        transferFromPerps:
          type: object
          description: >-
            Hyperliquid — USDC in perps the next order can move to spot (with an
            extra prompt).
          properties:
            available:
              type: string
              example: '12.5'
            note:
              type: string
        code:
          type: string
          description: >-
            Hyperliquid — `HYPERLIQUID_AGENT_MISSING` when the trading approval
            must be renewed by `setup`.
        reason:
          type: string
          description: Why the venue is not ready (fund, blocked, or approval renewal).
        blocked:
          type: boolean
          description: Hyperliquid — this account cannot be enrolled.
        accountMode:
          type: string
          description: Hyperliquid — the account mode that blocks enrollment.
        approvals:
          type: integer
          description: Setup result, Predict.fun — approvals the wallet needs gas for.
        gasWei:
          type: string
          description: Setup result, Predict.fun — estimated BNB gas needed, in wei.
      additionalProperties: true
  responses:
    WalletTenantUnauthorized:
      description: >-
        `send X-Kairos-Partner-Id or your API key (X-Client-Id, X-Api-Key,
        X-Api-Secret)` — no partner id and no API key (a delegation header never
        qualifies); `invalid API key`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/WalletApiError'
          example:
            error: >-
              send X-Kairos-Partner-Id or your API key (X-Client-Id, X-Api-Key,
              X-Api-Secret)
    WalletPayloadTooLarge:
      description: >-
        Plain text (`Failed to buffer the request body: length limit exceeded`)
        when the body is over the limit: 16 KiB for fee settlements, 64 KiB for
        trade plans and on-chain intents, the server-wide 2 MiB for actions.
        Above 64 KiB the answer may instead be `{"error": "request body too
        large"}`.
      content:
        text/plain:
          schema:
            $ref: '#/components/schemas/WalletPlainTextError'
        application/json:
          schema:
            $ref: '#/components/schemas/WalletApiError'
    WalletUnsupportedMediaType:
      description: 'Plain text — the request has no `Content-Type: application/json` header.'
      content:
        text/plain:
          schema:
            $ref: '#/components/schemas/WalletPlainTextError'
    WalletRateLimited:
      description: >-
        `rate limit exceeded for this endpoint; retry later`. The default limit
        is 120 requests per minute per client IP per path (1000 when
        `X-Client-Id` is sent), over a sliding 60 s window; `GET` and `POST` on
        one path share it. No `Retry-After` header is sent.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/WalletApiError'
          example:
            error: rate limit exceeded for this endpoint; retry later
    WalletInternalError:
      description: '`internal error`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/WalletApiError'
          example:
            error: internal error
  securitySchemes:
    walletPartnerId:
      type: apiKey
      in: header
      name: X-Kairos-Partner-Id
      description: >-
        Wallet API partner id, issued by Kairos together with the partner's base
        URL. Identifies the partner (tenant) on every `/v3` call; sent instead
        of an API key. Letters, digits and `._:-`, at most 64 characters,
        case-insensitive. A partner id issued for an individual account cannot
        be sent this way — that account sends its API key triple instead.
    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.