Changelog
Every change you can see, newest first. Additive changes never break a client that ignores unknown fields; breaking ones are announced before they ship.
- Additive
Webhook schemas declare the legacy names the bodies carry
- Webhooks
order_update,tradeandpositions(and the partner app bodies with the samedata): the schemas now declare the deprecated legacy names every body already carries with the same value as the documented field:cirrus_tag(=order_tag) andcirrus_token(=instrument_token) inOrderData,WebhookTradeDataandSplitFrom,cirrus_tokenin eachPositionRow. They are markeddeprecated: trueand still listed inx-deprecated-aliases. Before, the schemas left them out while declaringadditionalProperties:
false
, so a receiver validating strictly against the document rejected every such delivery. Nothing on the wire changes.OrderDataandPositionRoware shared with the REST order book and positions responses, which send the same names, so those declare them too. - Webhooks
- Additive
Modify and cancel take an order's tag as GET does
PATCH/DELETE /v1/orders/{order_id}andPATCH/DELETE
/v1/orders/bracket/{order_id}
:order_idmay now be the order'sorder_tagorclient_tag, asGET /v1/orders/{order_id}already takes it; the order is modified / cancelled at the broker under its broker order id. Before, a tag was sent to the broker as the order id: the answer was 200status: rejected("Order not found") and nothing changed. A tag the account's order book does not know, an order the broker has not acknowledged yet, or aclient_tagnaming several orders in the account is refused with the routes' existing 422invalid_request(fieldorder_id), nothing sent; an unreadable order book is the existing 503service_unavailable. A broker order id is handled as before. - Additive
Exit notices name their v2 trigger
- Notifications (
GET /v1/notifications, the/v1/streamnotificationstopic, thenotificationwebhook):exit_placed,exit_filledandexit_refusednotices about a v2 trigger's exit now carry itstrigger_id(it was null), so a fill can be matched to its trigger. Exits of v1 triggers keeptrigger_idnull (the field names v2 triggers). No shape change. Exits recorded by a pod running the previous build during a deploy keep null.
- Notifications (
- Additive
A bracket waiting for its entry is refused while entries are off
POST /v2/triggers,PATCH /v2/triggers/{id}andPOST
/v2/triggers/validate
: abracketwithspec.entryis refused with 422invalid_request,fieldspec.entry(validate lists it inerrors) unless automatic entries are on for the user (GET /v2/triggers/capabilitiesentries_enabled). Before, with entries off (production today) such a bracket was accepted, its entry was never placed (dead-lettered astrigger_action_unsupported) and only its stop-loss / target ran. The message says to place the entry by hand and give its price asentry_px. With entries on, the allowlist check is unchanged. Existing triggers are not touched: one such bracket exists in production (created 2026-10-16, still waiting; if its entry condition holds, its entry is dead-lettered as before). Theentries_enableddescription says so. - Additive
Notifications for users who never opened their settings
- Users without saved notification settings now get the default notifications, as
GET /v1/notifications/preferencesalready showed them: notices in the app inbox (GET /v1/notifications) and on the/v1/streamnotificationstopic for every event on by default (trigger fired, exit placed / filled / refused, order rejected, order not confirmed, protection failed, strategy events, session expired...). Telegram only once they link it; thenotificationwebhook stays off until they turn it on. Before, such users got no notices at all (in production 31 of the 33 users active in the last 7 days). No shape changes; nothing is stored for them until they save settings.
- Users without saved notification settings now get the default notifications, as
- Additive
Tradejini order-book row shape with a faked client code
- The Tradejini conformance pack handles one more order-book row shape (
80c811b6, an RMS rejection whose message names a client code, now faked in the recording). No REST, webhook or stream field changes: rows of this shape were already accepted; they no longer raise an unhandled-type entry.
- The Tradejini conformance pack handles one more order-book row shape (
- Additive
Live condition values, order protection, run links on notices
- New
POST /v2/strategies/condition-values(get_condition_values, scopestrategies, 120 a minute per credential): for the strategy builder, a condition's indicator values and whether it is true now (true_now, null with a plain-Englishreasonwhile it cannot be known), each value withtextsuch asRSI(14,15m) = 62.3. Body: the condition as the document writes it (when, optionalon,place), the document'sunderlyingandwatch. Compiled, resolved and evaluated by the same code a running strategy uses; nothing is saved or traded. New schemasConditionValuesInput,ConditionValues,ConditionValue,ConditionUnderlying,ConditionCheckOn,ConditionPlace. - New
GET /v1/orders/{order_id}/protection(get_order_protection, scoperead): the stop-loss / target an order placed withprotectionis protected by (statusnone,waiting_for_fillorarmed,route,rules,broker_id) and the triggers holding it (versionv2orv1, to open at/v2/triggers/{trigger_id}or/v1/triggers/{trigger_id}). New schemasOrderProtection,OrderProtectionTrigger. Notification(the app inboxGET /v1/notifications, thenotificationsstream topic and the opt-innotificationwebhook) gains optionaldeployment_idandcycle({day, n}, schemaNotificationCycle) on automated-strategy notices, so a client can open the run (/v2/deployments/{deployment_id}/cycles/{day}/{n}). Absent on other and older notices.Notificationgains an optionalsubtype(a string, open set: ignore unknown values):strategy_adjustedwhen a strategy adjusted its open trade. Such notices use the existing eventstrategy_entered(routed by its preferences), soNotificationEventgains no value and strict clients see nothing new but the optional field.daily_summarynotices are now sent (one per user per trading day after the close, for users whose strategies ran that day: P&L, trades, notable events);availablefollowsOMS_NOTIFY_DAILY_SUMMARY(on unlessoff). Same event, fields and enum values as before.- Telegram (not an API): strategy notices with a deployment open that run in the app ("Open this run").
- New
- Additive
An account's static IP is the address the broker sees
static_iponGET /v1/accountsandGET /v1/accounts/{account}keeps its name and type. On an IPv6 static-IP seat it is now the seat's IPv6whitelist_ip(the address the broker sees and the user registers) instead of the IPv4 address calls dial through; IPv4 seats are unchanged. The static-IP refusal advice names the same address.
- Additive
Clearer notification wording
- The opt-in
notificationwebhook event, thenotificationsstream topic andGET /v1/notificationskeep every field, type and enum value; only the English intitle,what_happened,what_we_didandwhat_you_should_dois reworded (asked for by the product owner). Examples: a trigger title no longer repeats the strategy in brackets (Stop-loss hit on RELIANCE-EQ; the strategy stays instrategy);what_happenedsays where the market was (RELIANCE-EQ traded at
₹2,474.50.
); exit and order fills are titledExit filled/Order filledwith the fill inwhat_happened; a closed strategy trade's title carries its P&L (…: trade closed, ₹2,555 profit);live_updates(stopped) now haswhat_you_should_do: null(the advice moved intowhat_we_did). Clients must not parse these strings (they never were a contract); match onevent,severityand the ids.- The same wording is shared with the strategy run journey (
GET /v2/deployments/{deployment_id}/journeywhat_we_did, and the journey in the deployment detail), e.g. a closed trade now readsClosed every leg, so nothing is left open: ₹3,637 loss.(wasClosed
every leg; trade P&L -₹3637.
).- Internal: the
oms.strategy.*step record gains an optionalrealized(rupees, closed trades only; absent on older records, ignored by older readers). - Telegram messages (not an API) are redesigned: one headline emoji, "What to do" only when the user must act, the time in IST, a button that opens the app page, and a short quip on good or neutral news only.
- The opt-in
- Additive
Kotak Neo refusals seen live
The
kotakneoconformance pack classes the refusals Kotak gave live, which differ from its docs' examples:1022"order is rejected" on modify / cancel (order_not_modifiable),1018"please provide valid internal order number" on cancel (order_not_found) and HTTP 401100022"invalid session token" on any call (session_expired). Four live recordings back them. No response shape or status code changes. - Additive
Strategy capabilities mark engine-evaluated indicators available
GET /v2/strategies/capabilitiesindicators[].availableis nowtruefor every catalogue function the engine's indicator registry evaluates (the same functions/v2/triggers/capabilitieslists inindicators.functionsand as candlepatterns:rsi,adx,bullish_pattern, ...) while indicator conditions are on; it wasfalsefor all 89, so the app showed them as "coming soon". Functions the engine does not evaluate yet (phase Bvortex_*,fisher,tsi,rvol,hv,yz_vol,regime, session levels, opening range,ltp(sym),atm_iv,iv_rank,iv_pct,expected_move,dte,leg,minutes,bb_mid) stayfalse, and all stayfalsewith indicator conditions switched off.variables[].availableis derived from the engine too (unchanged:cycle.minutes).- Validation and templates were already checked by the engine: no change.
- Same fields, same types; only the values are now accurate.
- Additive
Automated strategies, indicators and notifications for everyone (BETA)
- Automated strategies (
/v2/strategies*,/v2/deployments*, signals on algo mode/v2/signal-modes,/v2/backtests*, sharing), indicator conditions (v2 triggers and strategy documents) and user notifications (/v1/notifications*, thenotificationsstream topic) are on for every user the deployment serves; routes that answered 403 / 422 / 503notifications_disabledfor users outside an allowlist now answer as documented. The server can still switch each off (same answers as before). - Live deployments are open on every broker:
live_not_enablednow means only a credential without thestrategies_livescope (or a broker this build does not trade);GET /v2/strategies/capabilitieslive_brokerslists every broker (same field, more values). POST /v2/backtestsanswers 503 "Backtests aren't available on this server yet" when no backtest worker runs, instead of queueing a job that never starts.- Nothing is removed or renamed; no new field or enum value.
- Automated strategies (
- Additive
Guided adjustments: triggers, picked legs, `cycle.minutes`
GET /v2/strategies/capabilities:adjustment_triggers(eachtrigger.onan adjuster may fire on, withlabel,fields(key,label,help,required,default),phase,deployable; written into the engine plan as is: loss ₹, profit ₹, loss / profit % of premium, minutes after entry (elapsed), a leg's price × its entry (premium_ratio), days to expiry from a time (expiry; expiry day at a time:dte_at_most0 andat), a condition) andadjustment_targets(tested,sold).variableslistscycle.minutes(available: true);labels.leg_targetsis new.- New trigger
leg_delta(leg,delta) and the existingdelta_band(net delta beyond ± a band) are phase C: saved, not deployable (the engine reads no greeks yet). close_leg@1,roll_strike@1:legis optional when the newtarget(tested: the sold option whose price rose most against its entry;sold: every open sold option) picks the legs as the adjustment fires;reduce@1takestargetto close only those legs. Exactly one ofleg/target; withtargeta roll names nomove_hedgeand no relative strike. Left out of normalized documents while absent.- A
roll_strike@1with arelativestrike counts from the rolled leg itself:tomust be that leg (anything else is now an error at validation instead of a skipped adjustment at run time). - Conditions of exits and adjustments may read
cycle.minutes(whole minutes since the trade entered). GET /v2/strategies/components: the adjusters'params_schemashows the above (close_leg@1is nowbare, aswiden@1: no field is required by shape).
- Additive
Several trades at once per deployment (`max_open_trades`)
- Strategy documents:
spec.risk.limits.max_open_trades(limits@1, integer 1 to 10, default 1). Above 1, each entry (time cycle, conditions, alert) opens its own trade with its own stops, exits and adjustments; the day's limits, the kill switch and the breakers count every open trade. Left out of normalized documents and exports while 1, so existing documents read and export exactly as before. New blockeropen_trades_above_day(above "Most trades a day"); the limits summary says "up to N trades open at once".GET /v2/strategies/componentsand the strategy JSON Schema list the field. GET /v2/deployments/{id}/live: each unit'sunit.trades(new optional array,AutoLiveTrade:trade,cycle,day,phase,pnl,floor,entered_at,exit_reason,legs) lists the open trades of a strategy holding several;legsandpnlare then every open trade's together. Absent for one trade at a time (unchanged).- The journey and cycles list stay one entry per cycle; each trade is a cycle of the day.
- Strategy documents:
- Additive
A signal URL never binds to a strategy without legs
PUT /v2/signal-modes/{endpoint_id}also refuses (422invalid_request, same shape as the other binding refusals) a deployment whose strategy has no legs at all (no leg selector, or no leg items and notfrom_signal@1): its alerts could never start a trade. Request and reply shapes unchanged. - Additive
Re-entries that can never happen are a blocker
POST /v2/strategies/validate(and create, update, versions, dry-run'svalidation) report a new blockerreentry_cappedatspec.reentry.maxwhen re-entries are on but "Most trades a day" (spec.risk.limits.max_cycles_per_day) is 1, so the strategy would never re-enter. Response shape unchanged. Thehigh_iv_iron_condortemplate now allows 2 trades a day (GET /v2/strategies/templates: its document'smax_cycles_per_dayandsummary), so its one re-entry after a target can happen. - Additive
Validate reports every deploy refusal first
POST /v2/strategies/validatenow finds, while the user types, everything a deploy would refuse for the document; response shape unchanged.- New blockers (saved, not deployable), each at the document field to change, with the words the deploy refuses with:
engine_refuses(the engine plan, compiled for a stand-in paper account and checked by the engine),not_in_instrument_list(watched symbols, fixed contracts, the underlying's price),underlying_not_open,not_available_yetat awebhook@1source when alerts on signal URLs are off,square_off_in_window(spec.exit.square_offinside the entry window),lots_above_cap(spec.legs.items[i].lotsabove the sizing'smax_lots),hedged_twice,needs_option_leg(spec.entry.expiry_day/spec.exit.expiry_day_exitwithout an option leg). The last four (andreentry_never) also show in create, update and versions; dry-run'svalidationshows them all. reentry_never(re-entries on, no reason chosen) is now a blocker, not a warning: the engine always refused such a plan at deploy.- New warnings:
daily_loss_below_stop(spec.risk.limits.max_loss_per_daybelow one trade's combined stop),signal_url_incompatible(no signal URL could be bound). - New optional request fields
mode(defaultpaper) andaccounts(as for deploy): the deploy's account rules come back as warnings with the deploy's owncode,fieldand message (invalid_request,account_not_found,live_not_enabled,deployment_limit_reached). - Rate limit: validate has its own budget of 240 a minute (was 60, shared with dry-run); dry-run keeps 60.
- New blockers (saved, not deployable), each at the document field to change, with the words the deploy refuses with:
- Additive
Notifications without a Telegram bot
New server switch
OMS_USER_NOTIFICATIONS(andOMS_USER_NOTIFICATIONS_USERS) serves notifications without a Telegram bot;OMS_USER_TELEGRAMbehaves as before.GET /v1/notifications/channels:telegramgainsavailable(boolean) andunavailable_reason(string or null). Without a bot,availableisfalse,unavailable_reasonsays "Telegram isn't set up on this server yet",bot_usernameis empty andpromptsnever containsconnect_telegramortelegram_broken. New exampletelegram_unavailable.POST /v1/notifications/telegram/linkandPOST
/v1/notifications/telegram/test
without a bot: 503notifications_disabled(existing code) with the message "Telegram isn't set up on this server yet". New examplestelegram_unavailable.- Users outside
OMS_USER_NOTIFICATIONS_USERSget the existing 503notifications_disabledon every/v1/notificationsroute, and nonotificationsstream topic.
- Additive
Signals on algo mode trade what the alert names (flag off)
Behind
OMS_SIGNALS_ALGO_MODE(and the automated-strategies allowlist); with it off nothing users see changes. Inbound TradingView, Chartink and Kuberhunt payloads, URLs and replies are unchanged. None of this has shipped.- Strategy documents:
from_signal@1legs are nowpreview(wereplanned), with new parametersinstrument(signal|stock|future|option),side(signal|buy|sell),option_type,strike_offset,lots,max_shares,max_per_alert,max_per_day,skip_held(all defaulted;underlyingsandmax_lotsas before).from_signal@1sizing ispreview.webhook@1sources taketrigger_only(default false). New validation problems (invalid_combination): legs from the signal need awebhook@1entry that is not trigger only and no adjustments; lots abovemax_lots/max_shares; sizing from the signal without legs from the signal.GET /v2/strategies/capabilitieslists two new enums with labels:signal_instruments,signal_sides. Strategy JSON Schema re-exported. PUT /v2/signal-modes/{endpoint_id}: switching toalgois refused (422invalid_request, fielddeployment_id, in plain words) unless the strategy trades what the URL's alerts send: its legs come from the signal, or every alert source istrigger_only(the alert only starts the strategy's own legs); a Chartink URL also needs an alert entry source.- In algo mode a Chartink alert's stocks (each on the Chartink strategy's side, with its shares or ₹ per stock and stop / target) and a TradingView entry's legs (as direct mode maps them) become the strategy's legs, within its limits; an exit closes only what that URL's alerts opened, and only the instruments it names. Journey and notifications say per stock what was bought or skipped and why.
- Strategy documents:
- Additive
More indicators and candle patterns in conditions (flag off)
Behind
OMS_TRIGGERS_V2_INDICATORS(v2 triggers) and the automated strategies flags; with them off nothing users see changes. None of this has shipped.- New condition functions (each also in
GET /v2/triggers/capabilities→indicators.functions, with its settings, ranges and defaults):adx,plus_di,minus_di,aroon_up,aroon_down,aroon_osc,cci,willr,roc,mom,mfi,obv,cmf,keltner_upper/_middle/_lower,donchian_upper/_middle/_lower,hull,wma,dema,tema,kama,psar,ichimoku_tenkan/_kijun/_span_a/_span_b,chop,stochrsi_k,stochrsi_d,stddev. - Candle patterns:
bullish_pattern('engulfing', '15m'),bearish_pattern(...),pattern('doji', '15m'), read at the candle close; newindicators.patternsin capabilities (TriggerV2IndicatorPattern:name,label,directionsboth|bullish|bearish|neither,candles,functions).indicators.functionsdoes not list the three pattern functions. validaterefusesmfi,obvandcmfon an index like VWAP ("indices have no volume, so MFI isn't available; try EMA instead").- Strategy catalogue (
GET /v2/strategies/capabilities→indicators, labels): the new names above that it did not have,kindstreaming,phasea. Existing entries are unchanged. - Strategy templates:
adx_trend_futures(ADX trend futures) andengulfing_call(Bullish engulfing call).
- New condition functions (each also in