Skip to main content
This page covers the dedicated endpoints for price- and time-triggered orders: single stops and targets, OCO and OTO brackets, TWAP schedules, and the two-sided market maker. These are the endpoints most integrations use; each one creates a strategy that the Krisis engine evaluates until it fires, expires, or is cancelled. All endpoints require 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.
Gotcha: deactivation never deletes the row. A fired, cancelled, or expired order keeps showing in List conditional orders, so filter on is_active rather than assuming the list is live work.
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

Creates one stop-loss, take-profit, stop-limit, or trailing-stop. Returns 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)

Creates a one-cancels-other pair — a stop-loss and a take-profit on an open position. Whichever fills first, the other is cancelled.

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

Creates a one-triggers-other pair — an entry order plus a single follow-on (SL, TP, or trailing stop) that stays dormant until the entry fills. Distinct from OTO bracket: this attaches exactly one exit leg instead of an SL+TP pair.

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.
Supplying 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” — post kind: "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

An entry order plus both a stop-loss and a take-profit, all dormant until the entry fills. Whichever of SL/TP fills first, the other is cancelled — the same OCO relationship as Bracket, gated behind the entry.

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. Supplying stop_loss_atr_period + stop_loss_atr_mult compiles a trigger of the form
where 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

A time-weighted average price order: a large parent quantity sliced into child orders submitted at a fixed interval across a window.

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

Rests a two-sided quote — a bid at 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

Returns every conditional order owned by the caller, active and inactive.
Gotcha: fire-immediately OTO entries are omitted. An entry compiled with the condition true never appears here, so absence from this list does not mean a strategy was hand-authored. An entry still waiting on its entry_trigger_price does appear, as kind: "bracket_entry".

Response

Array of summary objects.

Modify a conditional order

Edits a single conditional order or one leg of a live OCO bracket in place — the engine picks up the new trigger on its next tick with no cancel/recreate. Not available for TWAP or market-maker strategies. Returns 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

Deactivates the 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: