---
title: "Build pinned unsigned on-chain transactions for self-signing (step 1 of 2)"
method: POST
path: "/v2/onchain/intent"
tags: ["Orders"]
---

# Build pinned unsigned on-chain transactions for self-signing (step 1 of 2)

`POST /v2/onchain/intent`

## Request

- Base URL: `https://execution.kairos.trade`
- URL: `https://execution.kairos.trade/v2/onchain/intent`
- Auth: API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`

The on-chain counterpart to `POST /v2/orders/intent`, for the operations an order cannot do off-chain: one-time approvals, redemptions of resolved positions, CTF split/merge, and unwrapping wrapped collateral. Kairos builds the exact calldata, pins it under a single-use `payload_id` (300 s TTL), and hands you the EIP-155 signing digest for each transaction. **You** sign with your own EOA and **you** pay the gas; Kairos only broadcasts what you signed via `POST /v2/onchain/submit`.
**Polygon / Polymarket only.** `chain_id` is pinned to `137`; there is no `provider` field.
**No RPC at build time.** You supply `nonce` and `gas_price_wei`; the server does not fetch a nonce, quote gas, or preflight your balance. A stale nonce or an under-priced transaction surfaces at broadcast, not here.
**Multi-leg.** `op=approvals` returns several transactions; sign each `signing_digest_hex` raw (no EIP-191 prefix) and submit the signatures in the same order.
**Auth & scope.** Requires `trade:execute` for `polymarket` AND the `User.executionFastlaneEnabled` allowlist — the same gate as the order lane. `owner_address` must be a wallet registered to the authenticated caller. There is no service-token, admission, circuit-breaker or rate-limit gate on this route.

## Request body

- OrderOnchainIntentRequest: Body for `POST /v2/onchain/intent`. You supply the nonce and gas price yourself — the server makes no RPC call while building the intent (no nonce fetch, no gas quote, no balance preflight).
  - `op` 'approvals' | 'redeem' | 'split' | 'merge' | 'unwrap_wcol', required: Which on-chain operation to build. `approvals` builds the one-time collateral + CTF approvals; `redeem` claims a resolved position; `split`/`merge` convert between collateral and a complete outcome-token set; `unwrap_wcol` unwraps wrapped collateral.
  - `owner_address` string, required: The EOA that will sign and broadcast. Must be a wallet registered to the authenticated caller.
  - `condition_id` string, nullable: CTF conditionId. Required for `redeem`, `split` and `merge`.
  - `neg_risk` boolean, nullable: Whether the market is a neg-risk market (selects the adapter contract).
  - `collateral_address` string, nullable
  - `amount` string, nullable: Amount in wei, decimal string. Required for `split` and `merge`; must be > 0.
  - `nonce` integer, required: The signing EOA's next transaction nonce. Caller-supplied — the server never queries the chain for it.
  - `gas_price_wei` string, required: Gas price in wei, decimal string. Must be > 0. Caller-supplied.
  - `gas_limit` integer, nullable
  - `wcol_amount` string, nullable: Amount in wei, decimal string. Required for `unwrap_wcol`; must be > 0.

## Response `200`

Pinned unsigned transactions plus the digests to sign.

- OrderOnchainIntentResponse: Response for `POST /v2/onchain/intent`.
  - `payload_id` string, uuid, required: Single-use handle. Pass back to `POST /v2/onchain/submit`.
  - `transactions` OrderUnsignedTx[], required: The transactions to sign, in the order they must be broadcast (ascending nonce).
    - `description` string, required: Human-readable label for this leg (e.g. which approval it grants).
    - `to` string, required
    - `data_hex` string, required
    - `value` string, required
    - `nonce` integer, required
    - `gas` string, required
    - `gas_price` string, required
    - `chain_id` integer, required: Always `137` — this lane is Polygon/Polymarket-only.
    - `signing_digest_hex` string, required: The legacy EIP-155 sighash (`keccak256` of the unsigned RLP). Sign these raw 32 bytes — no EIP-191 prefix. The server normalizes `v` to `35 + 2·chain_id + parity` itself.
    - `unsigned_rlp_hex` string, required
  - `expires_at_us` integer, required: Wall-clock expiry, UNIX microseconds. TTL is 300 s here (longer than the 60 s order-intent TTL, because signing several transactions on a hardware wallet takes longer).
  - `requires_followup` string, nullable: Reserved. Always absent on a newly-created intent.

## Other responses

- `400`: Exact `error` strings: `invalid owner_address`; `owner_address is not a registered wallet for this user`; `invalid gas_price_wei (expected a wei decimal string)`; `gas_price_wei must be > 0`; `condition_id is required for op=redeem`; `condition_id is required for op=<op>`; `amount (wei) is required for op=<op>`; `invalid amount (expected a wei decimal string)`; `amount must be > 0`; `wcol_amount (wei) is required for op=unwrap_wcol`; `invalid wcol_amount (expected a wei decimal string)`; `wcol_amount must be > 0`; `unsupported op: <other>`; or the calldata builder's own message.
- `401`: No valid credential presented — missing/invalid API-key headers or an invalid/expired session token.
- `403`: `API key missing trade:execute scope`; `API-key access to polymarket is disabled`; `external-signing execution is not enabled for this account`; `external-signing authorization check failed`.
- `500`: `internal server error` — failed to serialize or store the intent, or a `payload_id` collision.
- `503`: `API-key access to polymarket is unavailable` — the API-key access cache read failed.

---

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