Follow an order from placement to fill
POST /api/v1/oms/intents answers 201 { intentId } and says nothing about execution —
the verdict streams over /ws/risk. Subscribe before you place and you watch the order
composed, dispatched, and filled without polling anything.
-
Subscribe to the order topics first
Section titled “Subscribe to the order topics first”On an authenticated socket — see Stream account and position updates for the handshake. Four topics cover the whole lifecycle, and subscribing before you place is what makes it real-time.
{ "type": "Subscribe","topics": ["Order-Intent-Changed", "Order-Changed", "Fill", "Trade-Changed"] }import type { RiskTopic } from '@tronchartsxyz/api-client'// 'Fill' is live on the server but has not landed in the SDK's RiskTopic union yet.const topics = ['Order-Intent-Changed', 'Order-Changed', 'Trade-Changed', 'Fill'] as RiskTopic[]ws.send({ type: 'Subscribe', topics }) -
Place the order
Section titled “Place the order”Send
clientOrderId— your own correlation id, echoed back on the201and on theOrder-Intent-UpdateandOpen-Orders-Updateframes, so you can adopt the order before its intent id reaches you.Idempotency-Key(8–200 characters) makes a retry replay the cached response instead of placing twice.Terminal window curl -s https://api.troncharts.xyz/api/v1/oms/intents \-H "authorization: Bearer $TOKEN" \-H "x-tenant-slug: $TC_TENANT_SLUG" \-H 'content-type: application/json' \-H "idempotency-key: $(uuidgen)" \-d '{"venue":"paper","symbol":"BTC.HL","side":"buy","type":"limit","qty":"0.01","price":"60000","clientOrderId":"my-order-1","accountId":"'"$ACCOUNT_ID"'"}'const { intentId } = await sdk.oms.composeIntent({ venue: 'paper', symbol: 'BTC.HL', side: 'buy', type: 'limit',qty: '0.01', price: '60000', clientOrderId: 'my-order-1', accountId },crypto.randomUUID(),) -
Read the lifecycle off the frames
Section titled “Read the lifecycle off the frames”Order-Intentlands first, carrying the composedactionToSign.Order-Intent-Updatefollows on every transition —intent_pending,signed_pending_dispatch,dispatched,acknowledged,partially_filled,filled, and the terminalcanceled/rejected/expired— and bindsvenueOrderIdonce the venue answers.Order-Updaterestates the same thing in the external vocabulary, keyed on the intent id:{ "type": "Order-Update", "orderId": "3f2a…", "venueOrderId": "88214417","venue": "paper", "symbol": "BTC.HL", "side": "long", "orderType": "limit","status": "partially_filled", "qty": "0.01", "filledQty": "0.004","price": "60000", "updatedAt": "2026-08-04T12:00:00.000Z" }Fillfires once per execution and is never coalesced, so itsqtyandpriceare that execution’s own and summing them is safe.Trade-Updatefires only on a closed round trip — it never fires on an entry. -
Reconcile over REST when you need to
Section titled “Reconcile over REST when you need to”GET /api/v1/oms/intents/{id}is the authoritative read, scoped to the credential’s own account — an intent belonging to another account is404. That includes one you placed with an explicitaccountIdfor a paper child, so read it back with the credential that owns that account.GET /api/v1/oms/intentsreturns the last 50.Discovery advertises
GET /api/v1/orders/{id}. That path is not mounted; use the OMS one.Terminal window curl -s https://api.troncharts.xyz/api/v1/oms/intents/$INTENT_ID \-H "authorization: Bearer $TOKEN" \-H "x-tenant-slug: $TC_TENANT_SLUG"
Next: Place your first order · Order lifecycle & signing · Risk channel