Skip to main content
This page covers the account and credential half of the external execution lane: how a wallet logs in, how signing wallets are registered and revoked, how Polymarket L2 credentials are self-provisioned without Turnkey, how a partner app is delegated authority, and how to read your own readiness. The order flow itself is in Signing & Order Lifecycle; where to connect is in Regional Execution Nodes. Every endpoint here is on the regional node’s /v2 prefix, like the order lane.

Credentials you can present

The lane accepts three credential shapes, all resolved by the same auth layer: Any of these authenticates the lane, which is open to any authenticated account — no allow-list approval. A credential never bypasses scope or signature checks, and authorization is re-evaluated per request rather than carried forward from a prior call. An API key additionally earns a higher rate limit.

SIWE wallet login

Log in with a wallet using EIP-4361 (Sign-In With Ethereum). On success Kairos mints a scoped API key bound to the user that already owns the wallet — no new token type, no Turnkey session, no admin step. These two endpoints are unauthenticated by design: the wallet signature is the authentication. A credential is only minted for an address that is already registered to a Kairos account; account creation by wallet is out of scope.

1. Request a challenge

The message is the exact EIP-4361 string to sign — pass it through verbatim, do not reconstruct it. The challenge lives for 5 minutes and the nonce is single-use. Only chain 137 (Polygon) is accepted, the lane’s chain.

2. Sign and verify

Sign message with personal_sign (EIP-191), then:
Use the returned triple as X-Client-Id / X-Api-Key / X-Api-Secret. The secret is shown once — only hashes are stored server-side, so capture it now. The minted key is scoped to trade:execute, trade:read and position:read.

Self-serve wallet registration

Kairos only accepts signatures that recover to a wallet registered to your account. Register, replace and revoke signing wallets yourself — no account-manager round-trip. These endpoints are authenticated (JWT or API key): you are proving ownership of a wallet and attaching it to the authenticated account.

Register

  1. POST /v2/wallets/challenge with { "provider": "polymarket", "address": "0xYourEOA" }. provider is optional and defaults to polymarket; supported values are polymarket (Polygon) and predictfun (BSC). The response returns nonce, message and expires_at_us.
  2. Sign message with personal_sign. The message is the canonical form Kairos external signer registration\nProvider: …\nAddress: …\nNonce: ….
  3. POST /v2/wallets/register with { provider, address, nonce, signature_hex, message }.
The challenge is single-use (claimed atomically on submit) and bound to your user id. If the address is already registered to another account, registration is rejected with 409 this address is already registered to another account rather than silently reassigned. Registering again for the same provider simply replaces the stored address.

Revoke

POST /v2/wallets/revoke with { "provider": "polymarket", "address": "0xYourEOA" } returns { "revoked": true }. Revocation is immediate and load-bearing: a revoked wallet stops resolving for order submission, SIWE login and credential provisioning at once. If you revoke the only registered wallet for a provider, that provider’s order lane has nothing it will accept until you register another.

Polymarket credential provisioning

Polymarket order placement and cancellation need L2 API credentials (apiKey / secret / passphrase), normally derived from a signed L1 auth message. On this lane you provision them yourself with your own EOA — no Turnkey wallet. Two round trips, mirroring the order intent flow:
  1. POST /v2/credentials/polymarket/intent with { "address": "0xYourEOA" }. The address must already be a registered wallet. The response returns a single-use payload_id, the timestamp and nonce used, the digest_hex to sign, a human-readable message, and expires_at_us.
  2. Sign digest_hex with your EOA (EIP-712 L1 auth) and POST /v2/credentials/polymarket/submit with { "payload_id", "signature_hex" }.
An account holds one set of Polymarket credentials. If they were provisioned for a different signing wallet, submit returns 409 rather than silently switching; pass "replace_existing": true to move the account’s credentials to the new wallet deliberately. Orders signed by the old wallet will then be rejected by the venue.
The L2 secret and passphrase are stored encrypted server-side and are never returned. Kairos uses them to submit and cancel on your behalf, so your client never holds them. The intent is single-use and claimed before verification; ownership is re-checked at submit, so a wallet revoked between intent and submit is rejected.
You sign an L1 auth message, not an order. This step only mints the venue credentials; it moves no funds and places no order. It is idempotent in effect — if the credentials already exist at the venue, Kairos derives them instead of erroring.

Deposit wallet (required to trade Polymarket)

Polymarket’s CLOB now requires orders to come from a deposit wallet — a per-user ERC-1967 proxy it deploys for you — rather than a raw EOA. Create and approve yours yourself; no account manager and no Turnkey wallet involved. The deposit-wallet address is deterministic from your signing EOA, so you can derive it before deploying.
  1. Derive. GET /v2/deposit-wallet?owner_address=… returns deposit_wallet_address. owner_address must be a registered signing wallet.
  2. Create. POST /v2/deposit-wallet/create with { "owner_address": "0xYourEOA" } deploys the proxy via Polymarket’s relayer. No signature is required and it is idempotent — re-running returns the same address.
  3. Approve. POST /v2/deposit-wallet/approvals/intent returns the missing trading approvals as an EIP-712 Batch (domain DepositWallet, verifyingContract = your deposit wallet) plus eip712_digest_hex. Sign that digest with your EOA — a plain 65-byte signature, not ERC-7739. Post { "payload_id", "signature_hex" } to POST /v2/deposit-wallet/approvals/submit; Kairos forwards the signed batch to the relayer and waits for the on-chain transaction.
Then fund the deposit wallet (send the settlement asset, e.g. pUSD on Polygon, to deposit_wallet_address) and trade from it.

Ordering from the deposit wallet

Orders use signature_type: 3 (POLY_1271) with owner_address = the deposit wallet; omit signer_address (it defaults to the wallet, which is what the CLOB validates via ERC-1271). The intent returns unsigned_payload.erc7739_digest_hex; sign that with the wallet’s owner EOA and submit the plain 65-byte signature — Kairos builds the ERC-7739 envelope.
Polymarket deposit-wallet orders are currently blocked upstream. Polymarket’s CLOB binds an L2 API key only to the owner EOA, never to the deposit wallet, so it rejects the order with the order signer address has to be the address of the API KEY. This is an acknowledged bug on Polymarket’s own SDK tracker (py-clob-client-v2 #64/#70/#75/#77) with no server-side workaround. Onboarding and order construction work today; the order will settle once Polymarket fixes the binding.
The intent response carries an extra unsigned_payload.erc7739_digest_hex. Sign that with your EOA — a plain 65-byte raw-hash signature — and submit it as signature_hex. Kairos wraps it into the ERC-7739 envelope the deposit wallet’s isValidSignature expects and forwards the order; the Polymarket CLOB performs the ERC-1271 check. Kairos makes no on-chain call. Do not sign eip712_digest_hex for a deposit-wallet order: that is the bare order digest the wallet contract validates, not what the owner signs.
If your wallet stack already produces the ERC-7739 envelope (for example the Rust polymarket_client_sdk_v2), submit that instead; anything longer than 65 bytes is forwarded verbatim. Kairos only wraps when owner_address is a deposit wallet derived from one of your registered signing wallets — the one it created for you — so it never rewrites a signature meant for some other contract wallet. The TypeScript SDK’s digestToSign(intent) picks the right digest, and polymarketDepositWalletDigest(order, negRisk) reproduces it locally for the one-shot WebSocket path.
The intent is single-use and short-lived. Each approvals/intent is claimed on the first submit; if it expires, request a new one. calls: [] means every approval is already in place — nothing to sign.
A user can authorize a partner app to act on their behalf with a stateless EIP-712 grant: scopes plus an expiry, signed off-chain by the user’s wallet. There is no server-side store; revocation is by short expiry. This is how a platform integrates on behalf of its users without holding their Kairos credentials. The signed Delegation struct: POST /v2/partner/delegation/verify takes { "delegation": { …the struct… }, "signature_hex": "0x…" } and returns the resolved owner, the scopes and the expiry:

Authenticating with a grant

To use a grant directly as a credential, send it on the request instead of a JWT:
  • X-Kairos-Delegation: the delegation JSON ({ user, delegate, scopes, issued_at, expires_at, nonce })
  • X-Kairos-Delegation-Signature: the 0x-prefixed signature over that struct
Kairos verifies the grant, resolves the owning user, and synthesizes an identity carrying exactly the granted scopes. A grant cannot exceed trade:execute, trade:read, position:read, and the grant’s expires_at is enforced on every request. Verify a grant up front with /v2/partner/delegation/verify before you wire it into a flow.
A delegation grant is a bearer credential for its lifetime. It is not bound to the presenting party at the transport layer and there is no per-request revocation list — treat it like a short-lived API key and keep expires_at tight. Rotating the user’s key does not invalidate an outstanding grant; only the expiry does.

Readiness and balances

Two read-only endpoints let you self-diagnose your readiness: GET /v2/onboarding/status (authenticated) returns, per provider, everything needed to place a first order and the single next step:
next_action is the ordered next thing to do — register a wallet, provision credentials, set approvals, fund gas — or null when the provider is ready. Balance fields are omitted when they cannot be read. GET /v2/wallets/balances?provider=polymarket (authenticated) returns the same per-wallet view: native gas in wei, the settlement asset and balance, and whether the required approvals are in place. provider is optional and defaults to polymarket; it is validated, so an unsupported value is a 400 rather than a silently-ignored parameter. Predict.fun has no external on-chain lane, so its wallets are reported with the settlement asset (USDT) but null gas, balance and approval fields instead of probing a chain the venue does not settle on. Run these two reads after a change instead of discovering state from a failed order: GET /v2/onboarding/status tells you what to do next, GET /v2/wallets/balances tells you whether the chain is ready. (allowlisted reflects the optional kill-switch; the lane is open by default.)

Capabilities, identity and health

Three more reads round out the surface:
  • GET /v2/providers (authenticated) — per-venue capability descriptor: accepted signature_schemes (eoa, poly1271), account_abstraction flags, order entry points (two-round-trip REST, one-round-trip WSS, batch), supported time_in_force, post_only, cancel/amend capability, and whether your account is enabled. Read this instead of hard-coding venue behavior.
  • GET /v2/regions (unauthenticated) — deployment identity: role (cell/central), cell_id, region, exchanges and whether the /v2 lane is mounted.
  • GET /v2/health (unauthenticated) — external-lane status: status (ok/degraded), the providers actually registered on this deployment, and feature flags (Polygon RPC, idempotency, admission mode).

Attribution

If you resell or front access, tag your requests with X-Kairos-Partner-Id (REST) or a partner_id payload field (WebSocket) so Kairos can attribute volume, transactions and RPS to you. It is optional, never grants access, and is documented in full under Partner attribution.

See also

  • Signing & Order Lifecycle — the intent → sign → submit flow, Poly1271 smart-wallet orders, the WebSocket one-round-trip path, and cancels.
  • Overview — the access gate, the account model, and what’s live today.
  • Regional Execution Nodes — where to connect, and the live-position source-of-truth caveat.
  • API Reference — full request/response schemas.