Submit your externally-signed order signature (step 2 of 2)
Completes the self-custody order flow started by POST /v2/orders/intent: hand back payload_id and the 65-byte signature you produced over the returned digest. The server atomically claims the stored intent with a Redis GETDEL (single-use — a replayed or concurrent second submit for the same payload_id gets 400 payload_id not found or expired), recomputes the EIP-712 digest itself (never trusting anything in this request beyond the signature), ecrecovers the signer, confirms it matches the intent’s declared owner AND is a wallet registered to the authenticated caller, then forwards the assembled signed order to the venue CLOB.
The claim happens BEFORE any verification, so the payload_id is consumed even when the request goes on to fail — a bad signature burns the intent.
Synchronous result. status is the venue’s immediate result string, passed through verbatim. For a marketable order that crossed immediately this IS your fill confirmation; for a resting order it confirms the order is live. exchange_order_id is the venue’s handle — alongside the internal order_id it is the durable identifier for cancels.
Async fills. A resting order’s later fills are NOT returned here — they stream over your authenticated /ws connection as partially_filled / filled / status_changed events (deduped on the venue trade id; automatic reconciliation ensures fills are never silently lost, just occasionally a beat slower without /ws). Poll GET /orders/{order_id} if you are not holding a WebSocket open.
A persistence failure does NOT fail the request. If the order lands at the venue but the Kairos row cannot be written, the 200 is still returned and the failure is logged — treat the venue as authoritative.
Retries. The claimed intent is single-use and deleted on claim — a retry or a reprice requires a brand-new POST /v2/orders/intent (fresh salt/timestamp_ms → fresh digest → fresh signature). There is no silent server-side re-sign. The rate-limit slot was already charged at /intent and is not charged again here.
Auth & scope. Requires the fastlane allowlist (re-checked here, independent of the check at /intent) and trade:execute for the provider recorded on the stored intent. No service token / CSRF token is required.
curl --request POST \
--url https://execution.kairos.trade/v2/orders/submit \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>' \
--data '
{
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"signature_hex": "0x1234...1b"
}
'import requests
url = "https://execution.kairos.trade/v2/orders/submit"
payload = {
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"signature_hex": "0x1234...1b"
}
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({
payload_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
signature_hex: '0x1234...1b'
})
};
fetch('https://execution.kairos.trade/v2/orders/submit', 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/orders/submit",
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([
'payload_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'signature_hex' => '0x1234...1b'
]),
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/orders/submit"
payload := strings.NewReader("{\n \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\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/orders/submit")
.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 \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://execution.kairos.trade/v2/orders/submit")
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 \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\n}"
response = http.request(request)
puts response.read_body{
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "matched",
"exchange_order_id": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Unauthorized"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"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
Response
Venue's synchronous placement result.
Response for POST /v2/orders/submit (and the equivalent one-RTT WSS submit_signed_order command).
Internal Kairos order id — usable for GET /orders/{order_id} and the cancel endpoints.
The venue's immediate result string, passed through verbatim — Kairos does not normalize or validate it. Polymarket's observed values are matched, live and delayed; unmatched appears on the fill-polling path. Treat this as an open string, not a closed enum.
"matched"
Venue-assigned order handle. An EMPTY venue id is treated as a failed submission and returned as 400 venue returned an empty order id; order not tracked, so on a 200 this is in practice always populated; it is null only on the idempotent-replay response path.
Was this page helpful?
curl --request POST \
--url https://execution.kairos.trade/v2/orders/submit \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>' \
--data '
{
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"signature_hex": "0x1234...1b"
}
'import requests
url = "https://execution.kairos.trade/v2/orders/submit"
payload = {
"payload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"signature_hex": "0x1234...1b"
}
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({
payload_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
signature_hex: '0x1234...1b'
})
};
fetch('https://execution.kairos.trade/v2/orders/submit', 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/orders/submit",
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([
'payload_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'signature_hex' => '0x1234...1b'
]),
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/orders/submit"
payload := strings.NewReader("{\n \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\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/orders/submit")
.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 \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://execution.kairos.trade/v2/orders/submit")
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 \"payload_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"signature_hex\": \"0x1234...1b\"\n}"
response = http.request(request)
puts response.read_body{
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "matched",
"exchange_order_id": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Unauthorized"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}{
"error": "Insufficient scope",
"code": "<string>"
}
