> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnibook.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders

> Idempotency, order states, and reading execution outcomes correctly.

## `client_order_id` is the contract

You supply `client_order_id` on every order. It must be unique per order and is
the idempotency key for the whole placement path.

The rule that matters: **on any failure where you are unsure, retry with the
same `client_order_id`.** At most one order ever results. Retrying with a new id
after a timeout is how duplicate orders happen.

## Order states

| `status`           | Meaning                        |
| ------------------ | ------------------------------ |
| `resting`          | On the book                    |
| `partially_filled` | Some filled, remainder resting |
| `executed`         | Fully filled                   |
| `canceled`         | Cancelled, with a `reason`     |
| `expired`          | `gtt` expiry passed            |
| `rejected`         | Refused — see the error code   |

## Cancellations arrive as success

<Warning>
  A `201` does **not** mean your order is on the book.

  Post-only orders that would have crossed, unfillable `fok` orders, `ioc`
  remainders, self-trade prevention, and market buys that would breach `max_cost`
  all return a successful response with `status: "canceled"` and a `reason`.

  They are execution outcomes, not errors. Always read `status` on a success
  response instead of assuming placement succeeded.
</Warning>

## Placement responses are real outcomes

The API holds your request until the order is sequenced, so the response
describes what actually happened — `filled_qty`, `resting_qty`, and a `fills`
array in stream order. There is no separate "pending" state to poll through.

`seq` on the response is the sequencer stamp, which is your audit join back to
canonical history.

## Balances are not in the response

Reservation and fee accounting are deliberately absent from placement responses
— they arrive on a different lane. Get them from
`GET /v1/portfolio/balance` or, better, the `user` WebSocket channel.

## Reading your orders

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /v1/portfolio/orders?status=resting,partially_filled
GET /v1/portfolio/orders?client_order_id=42
GET /v1/portfolio/orders/{order_id}
```

`status` takes a comma-separated list. Querying by `client_order_id` is how you
resolve a timeout.

## Cancelling

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
DELETE /v1/portfolio/orders/{order_id}
DELETE /v1/portfolio/orders
POST   /v1/portfolio/orders/{order_id}/decrease
```

`decrease` shrinks resting quantity without losing queue priority — use it
instead of cancel-and-replace when you only want to reduce size.

Cancels charge the write bucket, same as placements.

## Batching

`POST /v1/portfolio/orders/batch` places several orders in one request. It
charges **one write token per order**, so batching saves round trips, not rate
limit budget.
