---
title: "Merge a complete outcome-token set back into collateral"
method: POST
path: "/exchanges/{exchange_id}/ctf/merge"
tags: ["CTF"]
---

# Merge a complete outcome-token set back into collateral

`POST /exchanges/{exchange_id}/ctf/merge`

## Request

- Base URL: `https://execution.kairos.trade`
- URL: `https://execution.kairos.trade/exchanges/{exchange_id}/ctf/merge`
- Auth: API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`

Burns `amount` YES **and** `amount` NO tokens and returns `amount` collateral — recover capital from matched inventory without waiting for resolution. Requires holding at least `amount` of every outcome token. Recorded as two synthetic SELL fills at price 0.5. NegRisk routing, custodial signing, the Turnkey policy-version gate, the service-token/CSRF/API-key mutation gate, and server-side identity resolution behave exactly as on split.

## Path parameters

- `exchange_id` 'polymarket' | 'predictfun' | 'kalshi' | 'opinion', required

## Request body

- OrderCtfSplitMergeRequest
  - `condition_id` string, required: CTF conditionId of the market (0x…).
  - `market_id` string, nullable: OPTIONAL. Only enriches the synthetic BUY/SELL trade-log legs with the market's YES/NO token ids — the on-chain split/merge itself needs only `condition_id` + `amount`. When omitted the market context is resolved from `condition_id`; if it cannot be resolved either way the on-chain action still succeeds and only the best-effort trade-log enrichment is skipped.
  - `amount` string, required: Decimal string. Units of collateral (split) or complete sets (merge). Must be > 0. An unquoted JSON number is also accepted, but it round-trips through a binary float — send the string form to preserve every digit.
  - `user_id` string, nullable: Optional. Must match the authenticated caller when present — identity is resolved server-side and these fields can never widen scope to another user.
  - `turnkey_org_id` string, nullable: Optional. Must match the authenticated caller when present.
  - `wallet_address` string, nullable: Optional wallet override; honored only if the wallet is owned by the caller. Omit to let the server resolve the signer wallet.

## Response `200`

Merge executed on-chain.

- OrderCtfMergeResponse
  - `tx_hash` string, nullable: On-chain transaction hash. `null` when the action completed without producing one — always check `success`.
  - `amount` string, required: Decimal string — `rust_decimal` always serializes as a string.
  - `condition_id` string, required
  - `action` 'merge', required
  - `success` boolean, required

## Other responses

- `400`: Invalid `condition_id`, non-positive `amount`, an exchange that doesn't support the action (e.g. split/merge on Kalshi → `EXCHANGE_UNSUPPORTED`), or insufficient collateral (split) / outcome tokens (merge) → `FUNDS_INSUFFICIENT_USDC` or `FUNDS_INSUFFICIENT_BALANCE`. An insufficient-funds reject on this service is a `400`; there is no `402` anywhere in this API.
- `401`: No valid credential presented — missing/invalid API-key headers or an invalid/expired session token.
- `403`: Identity fields conflict with the authenticated caller (`AUTH_IDENTITY_MISMATCH`), wallet not owned by the caller, missing `trade:execute` scope, API-key access to this provider disabled, or installed Turnkey policies behind the version this path requires (`AUTH_POLICY_OUTDATED`, action `update_policies` — split, merge and redeem all enforce it).
- `404`: `VALIDATION_MARKET_NOT_FOUND` — the condition/market could not be resolved.
- `500`: `INTERNAL_ERROR` / `DATABASE_ERROR` / `SIGNATURE_ERROR` — bookkeeping or custodial signing failed.
- `502`: On-chain submission or the RPC call failed (`NETWORK_ERROR` / `EXCHANGE_ERROR`) — generally safe to retry.

---

[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)
