---
title: "Prepare a split, merge or redeem on a Polymarket deposit wallet"
method: POST
path: "/v3/onchain-intents"
tags: ["On-chain intents (preview)"]
---

# Prepare a split, merge or redeem on a Polymarket deposit wallet

`POST /v3/onchain-intents`

## Request

- Base URL: `https://execution.kairos.trade`
- URL: `https://execution.kairos.trade/v3/onchain-intents`
- Auth: one of:
  - API key in header `X-Kairos-Partner-Id` and API key in header `Authorization`
  - API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret` and API key in header `Authorization`
  - API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`
  - API key in header `X-Kairos-Partner-Id` and API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`
  - API key in header `X-Kairos-Partner-Id` and HTTP bearer

Preview: available on staging (`https://staging-execution.kairos.trade`); production returns `503` until release.

Runs one conditional-token operation — `split`, `merge` or `redeem` — on the wallet's Polymarket deposit wallet on Polygon. Kairos builds the exact calls and returns two typed-data requests for the owner to sign; the wallet pays **no gas**. Kairos marks the intent `confirmed` only after it verifies a finalized receipt whose token transfers equal `intent.terms.effects` exactly.

**Scope.** Polymarket on Polygon (`137`), binary markets only. `redeem` needs a resolved market and an `outcome`; markets on the legacy conditional-token contract cannot be redeemed here. Each batch is `[approval, operation]`: merge and redeem leave a standing operator approval (`setApprovalForAll`) for the operating contract on the deposit wallet. The wallet must already be set up through a `setup` action.

**Addresses** `wallet` and `account` must be **lowercase**; a checksummed address is refused.

**Flow.** Prepare → sign `consentRequest` (and `nativeRequest` when `submissionOwner` is `kairos`) → `POST /v3/onchain-intents/{id}/execute` before `executeBefore` (at most 5 minutes out) → poll `GET /v3/onchain-intents/{id}` until `confirmed`.

**Do not broadcast the batch from the wallet or the deposit wallet**: a transaction sent from either never confirms. With `submissionOwner: "client"` (for integrators that have arranged submission access with Kairos), POST `submission.body` plus `"signature": <native signature>` to `submission.endpoint`, then report the transaction hash.

**Chaining.** `dependencies` lists earlier intents on the same deposit wallet (for example a split, then a merge); an intent executes only after its dependencies are `confirmed`. A trade plan can also depend on a confirmed intent through `ctfDependencies`.

**Idempotency.** `Idempotency-Key` is required. The same key with the same body returns the original intent in its current state; with a different body it is `409`.

## Headers

- `Idempotency-Key` string, required

## Request body

- WalletOnchainIntentPrepareRequest: Body of `POST /v3/onchain-intents`. Unknown fields (calls, nonce, chain, recipient, gas) are refused.
  - `wallet` string, required: The owner that signs — **lowercase** `0x` address.
  - `account` string, required: The owner's Polymarket deposit wallet (its trading wallet) — **lowercase**, and not equal to `wallet`.
  - `market` string, required: `polymarket:0x<64-hex condition id>` or a bare condition id. Binary markets only.
  - `operation` 'split' | 'merge' | 'redeem', required: `split` — pUSD into one of each outcome token; `merge` — one of each outcome token back into pUSD; `redeem` — winning outcome tokens into pUSD on a resolved market.
  - `amountRaw` string, required: Canonical integer string > 0, 6-decimal base units (`"1000000"` = 1 pUSD or 1 share). Split and merge: the collateral and the per-outcome amount. Redeem: outcome tokens burned.
  - `outcome` 0 | 1: Outcome index to redeem. Required for `redeem`, refused otherwise.
  - `submissionOwner` 'kairos' | 'client', required: Who submits the signed batch. Use `kairos` unless you have arranged submission access with Kairos.
  - `executeBefore` string, date-time, required: In the future, at most 300 s out, whole seconds. Also the batch's on-chain deadline.
  - `dependencies` string[]: Earlier intents on the same deposit wallet that must confirm first. At most 16, distinct.

## Response `200`

The prepared intent (`needs_signatures`) and the two requests to sign.

- WalletOnchainIntentView: An on-chain intent with its two signing requests, both `eth_signTypedData_v4` by `terms.signer` on Polygon (`params[1]` is a JSON string — pass it unchanged): - `consentRequest` — `KairosOnchainIntent` typed data binding `signer`, `partnerId`, `audience`, `intentId`, `intentHash`, `executeBefore`. Always required. - `nativeRequest` — the deposit wallet's `Batch` authorization over `terms.batch`. Send it to Kairos when `submissionOwner` is `kairos`; keep it for your own submission when `client`. Signatures must be plain 65-byte ECDSA (low-s) from the owner key; smart-account signatures are refused.
  - `intent` WalletOnchainIntent, required
    - `terms` WalletOnchainIntentTerms, required: The immutable terms; `intentHash` is their hash. `audience`, `executionRegion` and `submissionEndpoint` are opaque values pinned into the signed terms — do not change them.
      - `contractVersion` 1, required
      - `id` string, uuid, required
      - `partnerId` string, required
      - `audience` string, required: Opaque signed field.
      - `executionRegion` string, required: Opaque signed field.
      - `signer` string, required
      - `account` string, required
      - `chainId` '137', required
      - `market` string, required
      - `operation` 'split' | 'merge' | 'redeem', required
      - `submissionOwner` 'kairos' | 'client', required
      - `submissionEndpoint` string, required: Opaque pinned submission URL. Equals `submission.endpoint`.
      - `batch` WalletOnchainBatch, required: The deposit-wallet batch the owner signs. Calls are `[approval, operation]`.
        - `factory` string, required
        - `nonce` string, required: Decimal.
        - `deadline` string, date-time, required: Equals `executeBefore`.
        - `calls` WalletOnchainCall[], required
          - `target` string, required
          - `value` string, required
          - `data` string, required
      - `effects` WalletOnchainEffect[], required: Exactly what the batch moves on the deposit wallet — the receipt must match.
        - `token` union, required: A token, tagged by `kind`.
          - WalletOnchainTokenErc20
            - `kind` 'erc20', required
            - `contract` string, required
          - WalletOnchainTokenErc1155
            - `kind` 'erc1155', required
            - `contract` string, required
            - `tokenId` string, required: Decimal token id.
        - `direction` 'debit' | 'credit', required
        - `amountRaw` string, required
      - `dependencies` WalletOnchainDependency[], required
        - `intentId` string, uuid, required
        - `intentHash` string, required
        - `account` string, required
        - `chainId` string, required
        - `effects` WalletOnchainEffect[], required
          - `token` union, required: A token, tagged by `kind`.
            - WalletOnchainTokenErc20
              - …
            - WalletOnchainTokenErc1155
              - …
          - `direction` 'debit' | 'credit', required
          - `amountRaw` string, required
      - `maxWalletGasCostRaw` '0', required: The wallet pays no gas.
      - `createdAt` string, date-time, required
      - `executeBefore` string, date-time, required
    - `intentHash` string, required
    - `status` 'needs_signatures' | 'awaiting_client' | 'ready' | 'reconciling' | 'confirmed', required: `needs_signatures` → (`/execute`) `ready` (Kairos submits) or `awaiting_client` (you submit) → `reconciling` → `confirmed` (the only terminal status). A reverted transaction returns the intent to `reconciling`; the signed batch stays valid until `executeBefore`. There is no expired status: an intent whose batch was not mined by `executeBefore` stays where it is.
    - `committed` boolean, required
    - `dispatchClaimed` boolean, required: Kairos has submitted (or is submitting) the batch.
    - `transactionHash` string, nullable, required: The candidate transaction, once known (a report or Kairos's own submission); verified only when `proof` is set.
    - `proof` WalletOnchainProof, required: The verified, finalized transaction.
      - `transactionHash` string, required
      - `blockHash` string, required
      - `blockNumber` string, required: Decimal.
      - `intentHash` string, required
      - `effects` WalletOnchainEffect[], required
        - `token` union, required: A token, tagged by `kind`.
          - WalletOnchainTokenErc20
            - `kind` 'erc20', required
            - `contract` string, required
          - WalletOnchainTokenErc1155
            - `kind` 'erc1155', required
            - `contract` string, required
            - `tokenId` string, required: Decimal token id.
        - `direction` 'debit' | 'credit', required
        - `amountRaw` string, required
  - `consentRequest` WalletEip1193Request, required: 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.
    - `method` 'eth_signTypedData_v4' | 'personal_sign' | 'wallet_sendCalls' | 'eth_sendTransaction', required
    - `params` unknown[], required
      - unknown
  - `nativeRequest` WalletEip1193Request, required: 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.
    - `method` 'eth_signTypedData_v4' | 'personal_sign' | 'wallet_sendCalls' | 'eth_sendTransaction', required
    - `params` unknown[], required
      - unknown
  - `submission` WalletOnchainSubmission, required: For `submissionOwner: client`: add `"signature": <native signature>` to `body` and POST it to `endpoint`.
    - `endpoint` string, required
    - `method` 'POST', required
    - `kind` 'deposit_wallet_batch', required
    - `body` object, required: `{type: "WALLET", from, to, nonce, depositWalletParams: {depositWallet, deadline, calls}}` — no signature.

## Other responses

- `400`: `invalid on-chain intent request` (field rules: lowercase addresses, `account` ≠ `wallet`, canonical `amountRaw` > 0, `outcome` exactly when redeeming, `executeBefore` within 300 s with whole seconds, at most 16 distinct dependencies); `a valid Idempotency-Key is required`; `wallet must be a 0x address`; `account must be the registered signer's Polymarket deposit wallet`; `CTF intents require a Polymarket market`; `CTF intents require a binary market`; `unsupported CTF operation: legacy CTF redeem cannot enforce a signed token debit limit`; `deposit wallet is not deployed at finality`; `redemption must produce a positive bounded payout` (a losing outcome); `unknown CTF prerequisite`; `CTF prerequisite belongs to another account`; other market and condition checks. Plain text for malformed JSON.
- `401`: Tenant failures (`send X-Kairos-Partner-Id or your API key (X-Client-Id, X-Api-Key, X-Api-Secret)`, `invalid API key`), or the wallet proof failed: - `malformed sign-in token` — not `SIWE <token>` as issued, over 4096 bytes, or an invalid lifetime - `sign-in token was issued for another partner or node` — issued for another partner or environment - `sign-in token has expired; run a login action` - `sign-in token signature is not from its wallet` - `Missing authorization` — no `Authorization` header and no API key to prove the wallet; `Invalid API credentials`, `API credential has been revoked` or `Invalid token` when the proving credential is not `SIWE` and fails
- `403`: `API key missing trade:execute scope`; `wallet partner is disabled`; `wallet dispatch authority was revoked`; `external-signing execution is not enabled for this account`; `API-key access to polymarket is disabled`; or a tenant refusal.
- `404`: `wallet not found`; `on-chain intent not found`.
- `409`: `Idempotency-Key already names another intent`; `Polymarket V2 trading is disabled`; `this wallet is not set up yet; run a setup action first`.
- `413`: 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"}`.
- `415`: Plain text — the request has no `Content-Type: application/json` header.
- `422`: Plain text — the body is valid JSON of the wrong shape (a missing or unknown field, or a wrong type).
- `429`: `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.
- `500`: `internal error`.
- `503`: Production: the lane is not released. Staging: `canonical Polygon state is unavailable` (an RPC or metadata failure — **or a `redeem` on a market that has not resolved yet**); `execution is disabled for this operation`; `trade plans are disabled`; `partner registry unavailable; retry shortly`; `rate limiter unavailable`.

---

[API](https://skmtc.dev/kairos/apis/kairos-partner-api.md) - [All operations](https://skmtc.dev/kairos/apis/kairos-partner-api/llms.txt) - [OpenAPI document](https://skmtc.dev/kairos/apis/kairos-partner-api/revisions/f4baf0e325ef?raw)
