Build pinned unsigned on-chain transactions for self-signing (step 1 of 2)
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.
curl --request POST \
--url https://execution.kairos.trade/v2/onchain/intent \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>' \
--data '
{
"op": "redeem",
"owner_address": "<string>",
"nonce": 123,
"gas_price_wei": "<string>",
"condition_id": "<string>",
"neg_risk": true,
"collateral_address": "<string>",
"amount": "<string>",
"gas_limit": 123,
"wcol_amount": "<string>"
}
'import requests
url = "https://execution.kairos.trade/v2/onchain/intent"
payload = {
"op": "redeem",
"owner_address": "<string>",
"nonce": 123,
"gas_price_wei": "<string>",
"condition_id": "<string>",
"neg_risk": True,
"collateral_address": "<string>",
"amount": "<string>",
"gas_limit": 123,
"wcol_amount": "<string>"
}
headers = {
"X-Client-Id": "<api-key>",
"X-Api-Key": "<api-key>",
"X-Api-Secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Client-Id': '<api-key>',
'X-Api-Key': '<api-key>',
'X-Api-Secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
op: 'redeem',
owner_address: '<string>',
nonce: 123,
gas_price_wei: '<string>',
condition_id: '<string>',
neg_risk: true,
collateral_address: '<string>',
amount: '<string>',
gas_limit: 123,
wcol_amount: '<string>'
})
};
fetch('https://execution.kairos.trade/v2/onchain/intent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://execution.kairos.trade/v2/onchain/intent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'op' => 'redeem',
'owner_address' => '<string>',
'nonce' => 123,
'gas_price_wei' => '<string>',
'condition_id' => '<string>',
'neg_risk' => true,
'collateral_address' => '<string>',
'amount' => '<string>',
'gas_limit' => 123,
'wcol_amount' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>",
"X-Api-Secret: <api-key>",
"X-Client-Id: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://execution.kairos.trade/v2/onchain/intent"
payload := strings.NewReader("{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Client-Id", "<api-key>")
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("X-Api-Secret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://execution.kairos.trade/v2/onchain/intent")
.header("X-Client-Id", "<api-key>")
.header("X-Api-Key", "<api-key>")
.header("X-Api-Secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://execution.kairos.trade/v2/onchain/intent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Client-Id"] = '<api-key>'
request["X-Api-Key"] = '<api-key>'
request["X-Api-Secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"transactions": [
{
"description": "<string>",
"to": "<string>",
"data_hex": "<string>",
"value": "<string>",
"nonce": 123,
"gas": "<string>",
"gas_price": "<string>",
"chain_id": 137,
"signing_digest_hex": "<string>",
"unsigned_rlp_hex": "<string>"
}
],
"expires_at_us": 123,
"requires_followup": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Unauthorized"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}Authorizations
Credential client id (kairos_ck_...). Must be sent together with X-Api-Key and X-Api-Secret.
64-char hex API key.
64-char hex API secret.
Body
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).
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.
approvals, redeem, split, merge, unwrap_wcol "redeem"
The EOA that will sign and broadcast. Must be a wallet registered to the authenticated caller.
The signing EOA's next transaction nonce. Caller-supplied — the server never queries the chain for it.
Gas price in wei, decimal string. Must be > 0. Caller-supplied.
CTF conditionId. Required for redeem, split and merge.
Whether the market is a neg-risk market (selects the adapter contract).
Amount in wei, decimal string. Required for split and merge; must be > 0.
Amount in wei, decimal string. Required for unwrap_wcol; must be > 0.
Response
Pinned unsigned transactions plus the digests to sign.
Response for POST /v2/onchain/intent.
Single-use handle. Pass back to POST /v2/onchain/submit.
The transactions to sign, in the order they must be broadcast (ascending nonce).
Show child attributes
Show child attributes
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).
Reserved. Always absent on a newly-created intent.
Was this page helpful?
curl --request POST \
--url https://execution.kairos.trade/v2/onchain/intent \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>' \
--data '
{
"op": "redeem",
"owner_address": "<string>",
"nonce": 123,
"gas_price_wei": "<string>",
"condition_id": "<string>",
"neg_risk": true,
"collateral_address": "<string>",
"amount": "<string>",
"gas_limit": 123,
"wcol_amount": "<string>"
}
'import requests
url = "https://execution.kairos.trade/v2/onchain/intent"
payload = {
"op": "redeem",
"owner_address": "<string>",
"nonce": 123,
"gas_price_wei": "<string>",
"condition_id": "<string>",
"neg_risk": True,
"collateral_address": "<string>",
"amount": "<string>",
"gas_limit": 123,
"wcol_amount": "<string>"
}
headers = {
"X-Client-Id": "<api-key>",
"X-Api-Key": "<api-key>",
"X-Api-Secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Client-Id': '<api-key>',
'X-Api-Key': '<api-key>',
'X-Api-Secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
op: 'redeem',
owner_address: '<string>',
nonce: 123,
gas_price_wei: '<string>',
condition_id: '<string>',
neg_risk: true,
collateral_address: '<string>',
amount: '<string>',
gas_limit: 123,
wcol_amount: '<string>'
})
};
fetch('https://execution.kairos.trade/v2/onchain/intent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://execution.kairos.trade/v2/onchain/intent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'op' => 'redeem',
'owner_address' => '<string>',
'nonce' => 123,
'gas_price_wei' => '<string>',
'condition_id' => '<string>',
'neg_risk' => true,
'collateral_address' => '<string>',
'amount' => '<string>',
'gas_limit' => 123,
'wcol_amount' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>",
"X-Api-Secret: <api-key>",
"X-Client-Id: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://execution.kairos.trade/v2/onchain/intent"
payload := strings.NewReader("{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Client-Id", "<api-key>")
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("X-Api-Secret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://execution.kairos.trade/v2/onchain/intent")
.header("X-Client-Id", "<api-key>")
.header("X-Api-Key", "<api-key>")
.header("X-Api-Secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://execution.kairos.trade/v2/onchain/intent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Client-Id"] = '<api-key>'
request["X-Api-Key"] = '<api-key>'
request["X-Api-Secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"op\": \"redeem\",\n \"owner_address\": \"<string>\",\n \"nonce\": 123,\n \"gas_price_wei\": \"<string>\",\n \"condition_id\": \"<string>\",\n \"neg_risk\": true,\n \"collateral_address\": \"<string>\",\n \"amount\": \"<string>\",\n \"gas_limit\": 123,\n \"wcol_amount\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"transactions": [
{
"description": "<string>",
"to": "<string>",
"data_hex": "<string>",
"value": "<string>",
"nonce": 123,
"gas": "<string>",
"gas_price": "<string>",
"chain_id": 137,
"signing_digest_hex": "<string>",
"unsigned_rlp_hex": "<string>"
}
],
"expires_at_us": 123,
"requires_followup": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Unauthorized"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}
