Authorization: Bearer <jwt>.
Gotcha: every conditional order consumes a strategy slot. A bracket
creates two strategies and an OTO bracket creates three, so each counts that
many times against your account’s strategy-count
tier limit. Once the limit is hit, a create call
returns
400 "Strategy limit reached for your tier".Choosing an endpoint
Lifecycle
A conditional order has no separate status field:is_active is the whole
state, and how it got there is told by the other fields on the row.
The market maker is the exception to the fire-once shape: it holds two resting
quotes and stays active across fills, deactivating only on cancel, expiry, or a
stop_loss_pct breach.
Shared fields
These fields appear on most create requests.Gotcha: Polymarket needs
token_id and outcome on every conditional
order. Omit either and the request is rejected 400.Single conditional order
201 Created with an order object.
Request
Plus the shared fields.
What each kind does
"twap" and market-making are not valid here — use the TWAP and
market maker endpoints.
Example
Validation
Bracket (OCO)
Request
Plus the shared fields.
Supplying a limit price that differs from its trigger gives the standard
two-price stop-limit — trigger at 40¢, rest at 38¢.
Validation
Response
201 Created — stop_loss and take_profit are each an
order object.
OTO
Request
follow_on:
Plus the shared fields —
quantity here covers both legs.
Gotcha:
max_slippage_cents applies only to a market-exit follow-on
(stop_loss / trailing_stop), not a take_profit, which always rests a GTC
limit.Validation
Response
201 Created
Stop entries
Gotcha: by default an OTO entry fires on the very next tick. It is
compiled with the condition
true — it does not wait for any market state.
If you meant “enter when price reaches X”, you must supply
entry_trigger_price.entry_trigger_price compiles a book-side wake instead, so the entry
stays dormant until the market reaches that price:
This is a stop entry (“wait for price”) — the whole OTO or OTO bracket stays
armed and dormant until it wakes. The same field, with the same semantics, is
accepted by OTO bracket.
While the wake is still pending, the dormant entry appears in
List conditional orders with
kind: "bracket_entry". Fire-immediately entries are filtered out of that
list entirely.
An entry on its own
An OTO exists to attach an exit. When the entry is the order — “wait until the ask reaches 90¢, then just buy” — postkind: "bracket_entry" to
Create conditional order instead, with the wake
in trigger_price. It carries no follower and ends at its own fill. Editing it
is by replacement: PATCH refuses an entry.
Which way it waits. The side says which book the wake reads; it cannot say
which way that book has to move. “Buy when the ask reaches 35¢” is a breakout
when the ask is at 25¢ and a patient entry when it is at 45¢, and compiling the
wrong one either fires instantly at a price the user never wanted or sits there
forever. wake_direction states it; omitted, the live book implies it:
A wake the book already sits on is refused either way, because it would fire on
the next tick. The row carries the resolved direction back on
wake_direction,
so a client renders the order in the words it was armed with.
OTO bracket
Request
Plus the shared fields.
Gotcha: each leg is exactly one of two forms — fixed-price (
*_price) or
ATR (*_atr_period + *_atr_mult). Sending both, or neither, for the same
leg is rejected.ATR legs
An ATR leg is anchored to the entry’s actual fill price rather than a level picked before the entry filled. Supplyingstop_loss_atr_period +
stop_loss_atr_mult compiles a trigger of the form
entry_price is injected from the entry fill. The take-profit form is
the mirror image (entry_price + mult * atr(...)).
*_atr_timeframe must be one of 1s, 1m, 5m, 15m, 1h, 4h, 1d
and defaults to "1m".
Gotcha: an ATR leg always fires as a market order.
stop_loss_order_type / take_profit_order_type are ignored on it.Fill-anchored legs
On a fixed-price leg,stop_loss_offset / take_profit_offset (positive,
in price units, strictly inside (0, 1)) re-anchor the leg to the actual
entry fill: the stop fires at entry_fill − stop_loss_offset and the target
at entry_fill + take_profit_offset. The accompanying stop_loss_price /
take_profit_price then serves only as the provisional pre-fill level.
Validation
Response
201 Created
TWAP
Request
* Supply exactly one of
slice_count or slice_interval_seconds — the
other is derived from duration_seconds.
Plus the shared fields (fund_id, token_id, outcome,
max_slippage_cents) — quantity and expires_at do not apply.
What style does
Validation
Gotcha: the divisions must be exact. A non-exact
duration_seconds
division is rejected rather than silently truncating the schedule, and a
fractional per-slice quantity is rejected rather than truncated at
placement. Size the window and quantity to divide cleanly.Example
Response
201 Created
Market maker
mid − offset and an ask at
mid + offset — splitting total_capital 50/50 across the two sides, and
auto-cancels/re-quotes on a refresh interval or on fill.
Gotcha: this one does not fire once and stop. Unlike every other kind it
keeps two resting orders simultaneously and does not auto-deactivate when one
side fills. It stops only on cancel, expiry, or a
stop_loss_pct breach.Request
* Supply exactly one of
offset_cents (legacy static spread) or
spread_buffer_cents (buffer relative to the live half-spread).
On Polymarket, a binary market’s two tokens sum to $1, so the ask side quotes
by buying complement_token_id rather than short-selling.
Validation
If
stop_loss_pct is breached (net PnL on held inventory drops to
-(stop_loss_pct/100 × total_capital)), the runner cancels both quotes,
market-closes the position, and deactivates the strategy.
Response
201 Created
List conditional orders
Response
Array of summary objects.Modify a conditional order
200 OK with the updated conditional-order object (see
Order object).
Request
All fields optional; at least one is required.Validation
If the edited leg is a resting limit take-profit — one that’s already
live on the venue — Krisis cancels the resting order and lets the engine
re-place it at the new price; other kinds have nothing resting yet, so the
edit is purely a database update.
Cancel a conditional order
is_active = false) rather than deleting the row, so
it still shows in history. For a live order with a resting venue order, Krisis
additionally sends a best-effort cancel to the exchange so the order cannot
fill after cancellation.
Gotcha: cancelling one leg cancels the whole bracket by default. If the
order is part of an OCO bracket, every leg in the group is deactivated
unless you pass
single_leg=true.Query parameters
Response
204 No Content — returned whether or not the engine had already fired or
deactivated the order, so re-cancelling a live-but-already-dead row is safe.
An id that does not exist (or is not yours) returns 404.
Order object
POST /api/v1/conditional-orders returns one of these directly; the
stop_loss / take_profit / entry / follow_on fields of the bracket and
OTO responses, and the response of Modify, are
each one of these:

