# Smart order routing

When smart order routing is enabled on an account, the platform picks the venue
for an order and records **why**. Both the decision history and its rollup are
readable — the audit trail is the product, not a log.

Routing only happens on the routed-order endpoint below. A normal
[`POST /api/v1/oms/intents`](https://docs.troncharts.xyz/docs/trading/orders/) carries an explicit `venue`
and is dispatched there — it is never re-routed, whatever the account flag
says.

| Endpoint | Returns |
| --- | --- |
| `GET /api/v1/sor/decisions` | The decision history: what was considered, what was chosen, and the cost estimate behind it. Cursor-paginated. |
| `GET /api/v1/sor/decisions/summary` | The rollup — realised savings over a window. |

```bash
curl -s "https://api.troncharts.xyz/api/v1/sor/decisions?limit=50" \
  -H "authorization: Bearer $TOKEN" \
  -H "x-tenant-slug: $TC_TENANT_SLUG"
```

```ts
const decisions = await sdk.sor.decisions({ limit: 50 })
const summary = await sdk.sor.summary()
```

## Placing a routed order

`POST /api/v1/oms/route-intents` composes one intent against the venue the
engine picks. Its body is not the compose body:

```json
{ "asset": "BTC", "side": "buy", "qty": 0.01, "type": "market" }
```

| Field | Notes |
| --- | --- |
| `asset` | Required. The bare asset (`BTC`), not a venue-suffixed symbol — the engine resolves the symbol per venue. |
| `side` | `buy` / `sell`. |
| `qty` | Required, number. |
| `type` | Must be `market`. Routing limit and stop orders is not wired. |
| `reduceOnly`, `leverage`, `clientId`, `validForMs` | Same meaning as on compose. |

A success is `201 { intentId, decision }` — the decision is the same shape the
audit endpoints return, so you can log the reasoning with the order.

The account must have smart routing enabled; otherwise the call is
`400 routing_disabled` and the response `detail` names the toggle. When the
engine can't pick, you get `400` with an `error` of `asset_unknown`,
`no_eligible_venue`, or `no_quotes`, plus the `candidates` it evaluated and why
each was skipped.

The candidate pool is `hyperliquid`, `aster`, and `polymarket`. Anything else
you trade — including paper — has to be placed with an explicit venue.

:::caution[Wallet-rooted only]
This endpoint resolves the caller through a wallet-linked session. A Provider
API bearer credential has no wallet on its session, so it cannot place routed
orders today — use `/oms/intents` with an explicit venue.
:::

## Reading a decision

Each row records the candidate venues, the estimated cost at decision time, and
the selected venue. That makes two things possible that a plain fill record
cannot support:

- **Best-execution reporting** — show a client what the alternatives were.
- **Post-trade review** — compare the estimate against the realised fill and
  find where the model is drifting.

## Routing is per account

Routing is a property of the account, and venue eligibility is further
constrained by the account's trading group (its venue and symbol allowlists).
An order for a venue outside the allowlist is rejected at compose time.