Skip to content
Cirrus home
Sign in

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.

  1. Additive

    Shared data: data age per account, order counts, one view across owners

    GET /v1/shared/consolidated: every account card gains updated_at (positions / margins / holdings: when each was last read from the broker, null when not shared or no current data), missing (each shared kind among positions, margins, holdings without data, with its code and reason), open_orders_count (null without open_orders), failed_orders_count (rejected or never confirmed; null without order_history) and orders_updated_at. Existing fields are unchanged. New operation GET /v1/shared/consolidated/{view} (positions, holdings, margins, open_orders, order_history): the rows of GET /v1/shared/{owner}/{view} for every shared account, each tagged with owner_username / owner_name, plus missing and skipped_owners (not_shared / unavailable). App session only.

  2. Additive

    Alice Blue positions count carried legs in buy / sell value

    Positions of Alice Blue accounts (GET /v1/positions, the positions webhook and /v1/stream): buy_value / sell_value now include a position carried in from an earlier session, valued at its entry price, as they already did for the other brokers and as buy_qty / sell_qty already did. Before, they held only today's trades, so sell_value - buy_value + net_qty x last price left out the carried leg. Rows without a carried leg, day_buy_value / day_sell_value, average_price and pnl are unchanged.

  3. Additive

    Up to 100 orders per request

    POST /v1/orders: a request may carry up to 100 orders (was 50); more is still refused with invalid_request on orders ("at most 100 orders per request"). The per-account order rate limit is unchanged: orders that find no slot within its wait are refused one by one (result reason rate_limited, with retry_after_ms), so a large basket on one account can be placed only in part.

  4. Additive

    Quantities that are not whole lots are rounded down

    POST /v1/orders, order modify, POST /v1/gtt, PATCH /v1/gtt/{trigger_id} and POST /v1/orders/bracket: a quantity in units that is not a multiple of the lot size is rounded down to whole lots instead of refused with quantity must be a multiple of lot size N (lot 20, quantity 28 sends 20). POST /v1/orders reports the units placed in each result's quantity and records them in the order's activity; modify, GTT and bracket answers carry no quantity. With an account multiplier, the units are scaled first and then rounded down per account (as before). Less than one lot is still refused with 422 invalid_request, now worded quantity is smaller than one lot (lot size N) (field orders[i].quantity on place, quantity elsewhere). POST /v1/positions/exit is unchanged: a position that is not whole lots is still refused with its remainder.

  5. Additive

    strategy-level risk limits never flatten

    Where strategy-level risk limits are enabled (OMS_TRIGGERS_V2_STRATEGY_RISK, off), POST /v2/triggers, PATCH /v2/triggers/{trigger_id} and POST /v2/triggers/validate accept them only with on_breach pause or notify; flatten (and no on_breach, which means flatten) is a 422 spec.on_breach. Nothing changes while the switch is off, and account / user limits are unchanged.

  6. Additive

    Nubra adapter; its conformance pack

    Nubra accounts (Broker Nubra; ACCOUNT_LIST_DATA Access Token and the new Device ID, written by broker-auth's Nubra login; a static IP is needed) are served like other brokers: same request and response shapes, GET /v1/accounts reports them supported, updates polling. Orders go to Nubra's REST V3 intent orders (NUBRA_BASE_URL, default https://api.nubra.io); MTF, GTT, bracket / cover and position conversion are refused as for any broker without them. No existing field, type, enum value or status code changes. The broker conformance corpus gains a new nubra pack at replay_green: synthetic recordings of the order book, trades, positions, holdings, funds, place / modify / cancel, margin and the 400 refusal from Nubra's API docs, their expectations, the documented order statuses frozen, and that 400 ("Enter valid inputs to proceed.") classed invalid_field. Live replies (2026-10-13/14) are read as Nubra sends them: positions netQty / buyQty / sellQty, holdings qty (the docs name netQuantity / quantity; both are read), a delivery buy's blocked cash totalMarginBlockedNew; holdings carry the ISIN. Nubra's ProtectionProfile (and the pack's [protection]) is every fact unverified, like Groww. One 2026-10-13 margin quote recording is revoked (a float that hashed differently by build features); a live margin quote replaces it.

  7. Additive

    strategy-level risk limits

    POST /v2/triggers, PATCH /v2/triggers/{trigger_id} and POST /v2/triggers/validate accept a risk_limit with level: strategy (and a strategy) when the server enables it (switch OMS_TRIGGERS_V2_STRATEGY_RISK, off); elsewhere the answer stays 422 spec.level with today's message. While the strategy's numbers cannot be trusted the limit does not fire and new entries of the strategy are refused (the trigger's entry shows failed with the reason); nothing is flattened. Nothing changes while the switch is off.

  8. Additive

    strategy on order rows

    Order rows (OrderData: GET /v1/orders, GET /v1/orders/{order_tag}, every response and /v1/stream message that embeds an order) may carry an optional strategy: the strategy of the v2 trigger that placed the order. Absent otherwise. Order webhooks are unchanged.

  9. Additive

    v2 entry updates and journey kinds (follow-up)

    Recorded for commit 171eaad, which shipped them without a Contract-Change: trailer: with OMS_TRIGGERS_V2_ENTER on, the owner's trigger_update (stream and opted-in webhooks) carries one update per entry outcome in the existing shape (from = to, no actions, reason in words, journey set, event_id {trigger_id}:entry:{event_id}:{leg}:{kind}). The journey (source entry) also gains entry_resting, entry_resting_late, entry_rebase_skipped and entry_held_by_server. Nothing changes while the switch is off.

  10. Additive

    v2 trigger entries

    GET /v2/triggers/{trigger_id} and GET /v2/triggers show an optional entry object (TriggerV2Entry) on triggers whose entry the server placed (switch OMS_TRIGGERS_V2_ENTER, off): status (sending, accepted, filled, partially_filled, failed, outcome_unknown; other values may appear), filled_qty, avg_price, order_ids, reason. Absent otherwise; nothing changes while the switch is off. With it on, the owner's trigger_update push (stream and opted-in webhooks) also carries one update per entry outcome, same shape, from = to, event_id {trigger_id}:entry:{event_id}:{leg}:{kind}, and the journey gains source entry (kinds entry_sent, entry_adopted, entry_filled, entry_filled_late, entry_failed, entry_unknown, entry_not_protected). New example entered on get_trigger_v2.

  11. Additive

    recorded error examples for v2 triggers

    Documentation only: recorded examples for documented error responses of the /v2/triggers operations that had none. not_enabled (403) on get_trigger_v2, cancel_trigger_v2, modify_trigger_v2, pause_trigger_v2, resume_trigger_v2, validate_trigger_v2, get_trigger_capabilities_v2, get_trigger_journey_v2; engine_unavailable (503) on cancel, modify, pause and resume; ended (409) on resume; client_id (400 malformed_json) on modify and validate; invalid_query (400) on the journey. Existing examples and response shapes unchanged.

  12. Additive

    partly filled exits in the v2 trigger journey

    The journey (GET /v2/triggers/{trigger_id}/journey, unreleased) records a new kind exit_partly_filled (source exit) when the exit worker reads part of a v2 exit filled: order_id (the order tags), filled_quantity, quantity (the exit's total) and average_price. One entry per distinct filled quantity of a placement. The kind description in openapi.json lists it. The legacy trigger history is unchanged.

  13. Additive

    Dhan / Zerodha margin requests, Upstox account record recorded

    No request or response changes. New Dhan (BSE F&O, NSE equity) and Zerodha margin-quote request shapes and an Upstox account record seen in production join the conformance corpus, so they no longer alert.

  14. Additive

    Pocketful tenant shapes recorded

    No request or response changes. New Pocketful order book, positions, trades, place request and stream frame shapes seen on the Pocketful tenant join the conformance corpus, so they no longer alert.

  15. Additive

    Paper, Alice Blue, Dhan, 5Paisa, Zerodha shapes recorded

    No request or response changes. New broker reply shapes seen in production (Paper's first full trading session, new Alice Blue / Dhan / 5Paisa / Zerodha order rows) join the conformance corpus, so they no longer alert. Alice Blue "No holdings found" is read as an empty holdings list, and a Paper "Instrument data unavailable" refusal as a passing failure (nothing was placed).

  16. Additive

    Pocketful MTF orders and positions are listed

    Pocketful orders and positions with product MTF (placed in Pocketful's own app) are now listed with product: "MTF", an existing value; they were left out of the order book and positions. Placing MTF through Pocketful is still refused. A Pocketful cancel or modify refused by the gateway's HTML 403 page is a passing gateway block, as reads and placements already are.

  17. Additive

    dead Motilal / 5Paisa logins pause; Dhan margin opening

    A Motilal Oswal login the broker no longer accepts (HTTP 200 MO2032 "Invalid AccessToken") and a 5Paisa login it refuses (head status 2 "Invalid head parameters.") now pause the account and ask for a new login, as an expired session already does at every other broker, instead of being polled every few seconds. Dhan's margin opening_balance is now used + available (both include collateral), as for Alice Blue and 5Paisa; it was the cash-only sodLimit, which made utilised read far above 100. Refusals newly classed from production replies (Zerodha, 5Paisa, Upstox, Pocketful, Alice Blue, Tradejini) keep the reasons users already see. No field, enum value or status code changes.

  18. Additive

    v2 trigger expression reference

    Documentation only: docs/api/trigger-expressions.md (variables incl. prev.* and legs[i].*, functions incl. crosses_above / crosses_below, operators, limits, error messages, examples per kind) and the expression summary in validate_trigger_v2's description. No shape changes.

  19. Additive

    v2 triggers show refused commands and ended protection

    GET /v2/triggers, GET /v2/triggers/{trigger_id} (and trigger_update's protection):

    • new optional last_result (TriggerV2LastResult: op, command_id, outcome applied | duplicate | rejected, reason, at): the engine's answer to the last command it handled, so a refused modify, pause, resume or cancel shows on the trigger with its reason. Absent until the engine answered one (recorded examples unchanged).
    • an ended trigger's protection parts the server held show status ended (an existing value) instead of held_by_server; broker parts keep what the broker shows.
  20. Additive

    exits and broker protection in the v2 trigger journey

    The journey (GET /v2/triggers/{trigger_id}/journey, unreleased) also records exit orders (source exit: exit_placed with the order tags and quantity, exit_filled, exit_refused, exit_unfilled, exit_repriced, exit_retried, exit_failed when dead-lettered) and the broker protection (source protection: gtt_placed, gtt_modified, gtt_cancelled, gtt_triggered, readback_matched, readback_mismatch, readback_unknown, degraded). Reasons are cut to 200 characters; no broker reply bodies. No other shape changes.