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

# Orders

> Place, cancel and cancel-all, safe retries with clientOrderId, and reading orders.

All trading endpoints need a key with the `mm:trade` scope. Placing a new order also needs trading to be available for your account. See [Trading availability](https://docs.d1.exchange/market-makers/trading-rules.md#trading-availability).

| Method | Path | Purpose |
| - | - | - |
| `POST` | `/mm/v1/orders` | Place a limit order |
| `POST` | `/mm/v1/orders/{orderId}/cancel` | Cancel one order |
| `POST` | `/mm/v1/orders/cancel-all` | Cancel all your resting orders, optionally for one market |
| `GET` | `/mm/v1/orders` | List your orders |
| `GET` | `/mm/v1/orders/{orderId}` | Read one order |

Trading requests take a JSON body of at most 4 KiB and no query string. Send `Content-Type: application/json` whenever the request has a body.

## Place an order

`POST /mm/v1/orders`

```bash theme={null}
curl -X POST "$D1_API_URL/mm/v1/orders" \
  -H "Authorization: Bearer $D1_MM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clientOrderId": "q-20270115-000123",
    "instrumentId": "<instrument-id>",
    "side": "SELL",
    "type": "LIMIT",
    "price": { "units": "110", "decimals": 0 },
    "quantity": { "units": "2", "decimals": 0 }
  }'
```

**clientOrderId** `string` *required*

Your identifier for this order. 1–64 characters: a letter or digit, then letters, digits, `.`, `_`, `:` or `-`. Unique per account, permanently. See [clientOrderId and idempotency](#clientorderid-and-idempotency).

**instrumentId** `string` *required*

The market, from `GET /mm/v1/markets`.

**side** `string` *required*

`BUY` or `SELL`, exactly. `SELL` is [sell-to-close](https://docs.d1.exchange/market-makers/trading-rules.md#sell-to-close) only.

**type** `string` *required*

`LIMIT`, exactly. It is the only order type.

**price** `Exact` *required*

Limit price as `{ units, decimals }`, per the market's `premiumUnit`. It must be a multiple of the market's `priceTick`, written with the same `decimals`. `units` is a positive integer string with no sign or leading zeros.

**quantity** `Exact` *required*

Quantity in contracts as `{ units, decimals }`. It must be a multiple of the market's `quantityStep`, written with the same `decimals`.

Every field is required, and nothing else is accepted. An unknown field anywhere in the body, a JSON number instead of a string for `units`, or a lower-case `side` returns `400 MALFORMED_REQUEST`. You never send an account, an order ID or a nonce.

Orders are good until cancelled. There is no time-in-force, post-only, reduce-only or amend. To change an order, cancel it and place a new one with a new `clientOrderId`.

### Response

| Status | Meaning |
| - | - |
| `201 Created` | D1 created a new order. `duplicate: false`. |
| `200 OK` | This `clientOrderId` already has an order with the same terms. Nothing new was created. `duplicate: true`, and `order` is that order's **current** state. |

```json theme={null}
{
  "order": {
    "orderId": "<order-id>",
    "clientOrderId": "q-20270115-000123",
    "instrumentId": "<instrument-id>",
    "side": "SELL",
    "type": "LIMIT",
    "price": { "units": "110", "decimals": 0 },
    "quantity": { "units": "2", "decimals": 0 },
    "requestedQuantity": { "units": "2", "decimals": 0 },
    "acceptedQuantity": { "units": "2", "decimals": 0 },
    "filledQuantity": { "units": "1", "decimals": 0 },
    "remainingQuantity": { "units": "1", "decimals": 0 },
    "cancelledQuantity": { "units": "0", "decimals": 0 },
    "reductionReason": null,
    "status": "PARTIALLY_FILLED",
    "cancelReason": null,
    "createdAtMs": "1800000000500",
    "updatedAtMs": "1800000000500",
    "filledAtMs": null,
    "cancelledAtMs": null
  },
  "duplicate": false,
  "serverTimeMs": "1800000000510"
}
```

* **No silent resizing.** D1 accepts the full quantity you request or refuses the order. `acceptedQuantity` always equals `requestedQuantity` and `reductionReason` is `null`.
* **Immediate matching.** An order that crosses the book fills immediately, fully or partly, and any remainder rests. The response shows the order after matching.
* **Fills are not in the response.** Fills from the placement are already on [`/executions`](https://docs.d1.exchange/market-makers/executions) when the response arrives. Take them from there.
* **Self-trade prevention.** If your order would match one of your own resting orders, it fills against other participants first. Then the rest of the incoming order is cancelled with `cancelReason: SELF_TRADE_PREVENTION`. Your resting order is left alone. In that case a `201` response can show `status: CANCELLED`.

### Placement errors

| HTTP | `error` | Meaning | What to do |
| - | - | - | - |
| 400 | `MALFORMED_REQUEST` | Body is not valid for this endpoint. | Fix the request. |
| 400 | `INVALID_PRICE` | Price is not valid for this market, for example not on the tick. | Fix the price. |
| 400 | `INVALID_QUANTITY` | Quantity is not valid for this market, for example not on the step. | Fix the quantity. |
| 403 | `TRADING_DISABLED` | Trading is not enabled for your account right now. | Stop placing. Cancel and reconcile. See [Trading availability](https://docs.d1.exchange/market-makers/trading-rules.md#trading-availability). |
| 403 | `DEALER_SUSPENDED` | Your account is suspended. | Stop placing. You can still cancel. |
| 403 | `INSUFFICIENT_SCOPE` | The key cannot trade. | Use a trading key. |
| 404 | `UNKNOWN_INSTRUMENT` | The instrument does not exist. | Refresh your market catalog. |
| 409 | `MARKET_NOT_TRADING` | The market is not accepting orders, for example because it reached expiry. | Stop quoting this market. |
| 409 | `MARKET_PAUSED` | Trading on this market is paused. | Wait for `tradingPaused: false`. |
| 409 | `IDEMPOTENCY_CONFLICT` | This `clientOrderId` was already used with different terms. | Treat as a bug in your ID generation. Do not retry. |
| 422 | `INSUFFICIENT_FUNDS` | Available USDC does not cover a `BUY`. | Reduce size or add funds. |
| 422 | `INSUFFICIENT_POSITION` | A `SELL` exceeds your unreserved long position. | See [Sell-to-close](https://docs.d1.exchange/market-makers/trading-rules.md#sell-to-close). |
| 422 | `RISK_LIMIT` | The order would exceed an account limit. The body names it in `limit`. | See [Order limits](https://docs.d1.exchange/market-makers/trading-rules.md#order-limits). |

A refused placement creates nothing, so its `clientOrderId` is not used up. Authentication, `429` and `500` responses are described in [Errors](https://docs.d1.exchange/market-makers/errors.md).

## clientOrderId and idempotency

A `clientOrderId` identifies one **economic intent**: "buy 2 of this market at 105". D1 keeps at most one order per `clientOrderId` on your account, forever, no matter how many times or from how many places the request is sent. Both of your keys share the same `clientOrderId` space.

| You send | D1 answers |
| - | - |
| A new `clientOrderId` | A new order (`201`), or a refusal that creates nothing. |
| The same `clientOrderId` with the same terms | The existing order's current state (`200`, `duplicate: true`). Nothing is re-checked or created. |
| The same `clientOrderId` with different terms | `409 IDEMPOTENCY_CONFLICT`. The stored terms are not returned. |

"Terms" means `instrumentId`, `side`, `type`, `price` and `quantity`. A retry is answered as a duplicate even if the market has since closed, trading has been disabled, or your account has been suspended. Replays count against the placement [rate limit](https://docs.d1.exchange/market-makers/rate-limits.md).

### When you do not know the result

A placement can time out, lose its connection or return `5xx` after D1 has already created the order. It can also still be in flight and be created **after** you gave up waiting.

> [!WARNING]
>
> **Never re-send the same intent under a new `clientOrderId` while the first one's outcome is unknown.** It is the only way to end up with two orders, and D1 cannot detect it for you.

Do this instead:

**1. Keep the ID**

Persist the `clientOrderId` and its terms **before** sending. After a timeout, mark the ID as "possibly live".

**2. Retry with the same ID**

Send the identical request again. A `201` or `200` is final: that is your order. A refusal only answers that attempt. An earlier attempt with the same ID can still commit if the condition that refused this one changes.

**3. Reconcile**

Use `GET /mm/v1/orders?state=resting` and `GET /mm/v1/executions` to find the order and its fills. If the order shows up after all, cancel it or book its fills.

Not finding an order right after a timeout, in `GET /mm/v1/orders` or anywhere else, does **not** prove that it will never be created. Keep treating the ID as possibly live until a retry with the same ID returns the order, or until your reconciliation finds it.

If you want a different order, for example at a new price, use a new `clientOrderId` **and** keep treating the old one as possibly live.

## Cancel an order

`POST /mm/v1/orders/{orderId}/cancel`

```bash theme={null}
curl -X POST "$D1_API_URL/mm/v1/orders/$ORDER_ID/cancel" \
  -H "Authorization: Bearer $D1_MM_API_KEY"
```

The body is empty or `{}`.

```json theme={null}
{
  "order": { "orderId": "<order-id>", "status": "CANCELLED", "cancelReason": "USER", "…": "…" },
  "result": "CANCELLED",
  "serverTimeMs": "1800000060000"
}
```

| Order state | Response |
| - | - |
| Resting (`OPEN` or `PARTIALLY_FILLED`) | `200`, `result: CANCELLED`. The remaining quantity is cancelled with reason `USER`. |
| Already cancelled, for any reason | `200`, `result: ALREADY_CANCELLED`. Nothing changes. |
| Any other final state, such as `FILLED` | `409 ORDER_NOT_CANCELLABLE`. |
| Unknown, or another account's order | `404 ORDER_NOT_FOUND`. The two cases look the same. |

Cancelling is safe to retry: a second cancel returns `ALREADY_CANCELLED`. If a fill and your cancel race, whichever commits first wins. A cancel removes only what is left at that moment, so check `filledQuantity`.

## Cancel all orders

`POST /mm/v1/orders/cancel-all`

```bash theme={null}
# Every resting order on your account
curl -X POST "$D1_API_URL/mm/v1/orders/cancel-all" \
  -H "Authorization: Bearer $D1_MM_API_KEY"

# Only one market
curl -X POST "$D1_API_URL/mm/v1/orders/cancel-all" \
  -H "Authorization: Bearer $D1_MM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "instrumentId": "<instrument-id>" }'
```

The body is empty, `{}`, or `{ "instrumentId": "…" }`.

```json theme={null}
{
  "cancelledOrderIds": ["<order-id>", "<order-id>"],
  "cancelled": 2,
  "more": false,
  "serverTimeMs": "1800000060000"
}
```

* It cancels only **your own resting orders**, with reason `USER`. It never affects other participants' orders.
* It cancels the orders that existed when it ran. An order placed after it is not cancelled.
* `more: true` means some resting orders were left. Call it again until `more` is `false`.
* With nothing to cancel it returns `cancelled: 0`, so it is safe to retry.

Use `cancel-all` as your emergency control: on shutdown, on a lost connection to your pricing, when `trading.available` turns `false`, or whenever you are unsure what is resting.

## Cancellation stays available

Cancelling reduces risk, so it remains available when placing new orders is not:

| Situation | Cancel and cancel-all |
| - | - |
| Trading not enabled for your account (`TRADING_DISABLED`) | Allowed |
| Account suspended (`DEALER_SUSPENDED` on reads and new orders) | Allowed. Suspension itself already cancels your resting orders. |
| Key has `mm:trade` but is not authorized to place orders | Allowed |
| Key has only `mm:read` | `403 INSUFFICIENT_SCOPE` |
| Key revoked | `401 API_KEY_REVOKED` |

Cancellation has its own rate-limit budgets, separate from placement, so heavy quoting cannot use them up. See [Rate Limits](https://docs.d1.exchange/market-makers/rate-limits.md).

## Read orders

`GET /mm/v1/orders`: your orders, newest first.

**state** `string` (default: all)

`all` or `resting`. `resting` returns only orders still on the book (`OPEN` or `PARTIALLY_FILLED`).

**instrumentId** `string`

Only return orders for this market.

**limit** `integer` (default: 50)

1–100.

**cursor** `string`

`nextCursor` from the previous page, with the same `state` and `instrumentId`. See [Pagination](https://docs.d1.exchange/market-makers/market-data.md#pagination).

```bash theme={null}
curl "$D1_API_URL/mm/v1/orders?state=resting&limit=100" \
  -H "Authorization: Bearer $D1_MM_API_KEY"
```

Returns `{ orders, nextCursor, limit, serverTimeMs }`.

`GET /mm/v1/orders/{orderId}`: one order. Returns `{ order, serverTimeMs }`. An order that does not exist and an order that belongs to another account both return `404 NOT_FOUND`.

There is no lookup by `clientOrderId`. To find an order by your own ID, re-send the identical placement (it returns the existing order) or match `clientOrderId` in `GET /mm/v1/orders`.

### Order fields

| Field | Description |
| - | - |
| `orderId` | D1's order identifier. |
| `clientOrderId` | The `clientOrderId` the order was placed with. |
| `instrumentId`, `side`, `type`, `price` | As placed. `type` is always `LIMIT`. |
| `status` | See [Order status](#order-status). |
| `quantity` | Same as `acceptedQuantity`. Kept for compatibility. |
| `requestedQuantity` | Quantity you asked for. `null` only on older orders that did not record it. |
| `acceptedQuantity` | Quantity D1 accepted. Equal to `requestedQuantity` for every order placed through this API. |
| `filledQuantity` | Total executed so far. |
| `remainingQuantity` | Still resting and executable. `0` once the order is filled or cancelled. |
| `cancelledQuantity` | `accepted − filled` on a cancelled order, otherwise `0`. |
| `reductionReason` | Why D1 accepted less than requested. Always `null` in V1. |
| `cancelReason` | Why the order was cancelled, or `null`. See [Cancel reasons](#cancel-reasons). |
| `createdAtMs` | Always set. |
| `updatedAtMs`, `filledAtMs`, `cancelledAtMs` | `null` if the event has not happened or was not recorded. |

### Order status

| `status` | Meaning |
| - | - |
| `OPEN` | Resting, nothing filled yet. |
| `PARTIALLY_FILLED` | Resting, part filled. `remainingQuantity` is still executable. |
| `FILLED` | Fully executed. Final. |
| `CANCELLED` | Removed from the book. Final. Any fills before the cancel stay. |
| `PENDING_VALIDATION`, `CANCEL_PENDING`, `REJECTED`, `EXPIRED` | Published lifecycle values you may see on older orders. Treat `REJECTED` and `EXPIRED` as final. |

### Partial fills

```text theme={null}
acceptedQuantity  = 10
  ├── filledQuantity     = 4    executed (see /executions for each fill)
  ├── remainingQuantity  = 6    still resting
  └── cancelledQuantity  = 0

after a cancel:      filled 4, remaining 0, cancelled 6, status CANCELLED
```

Each partial fill is a separate execution on `/executions`. Book fills from there, not by diffing `filledQuantity`.

> [!WARNING]
>
> **`0` and `null` mean different things.** `0` is a confirmed zero. `null` means D1 never recorded the value, which happens only on orders created before lifecycle tracking began. D1 never fills in a missing value with a guess.

### Cancel reasons

`cancelReason` is `null` unless the order was cancelled. On orders placed through this API you can expect:

| `cancelReason` | Meaning |
| - | - |
| `USER` | You cancelled it, with cancel or cancel-all. |
| `EXPIRY` | The market reached expiry and trading closed. |
| `SELF_TRADE_PREVENTION` | The order would have matched one of your own resting orders. |
| `ADMIN_PULL` | D1 removed the order, for example when the account was suspended. |

The full set of published values is `USER`, `DEALER`, `DEALER_MASS_CANCEL`, `WATCHDOG`, `LOSS_LIMIT`, `ADMIN_PULL`, `ADMIN_HALT`, `EXPIRY_SOLVENCY`, `EXPIRY` and `SELF_TRADE_PREVENTION`. D1 will announce any new reason as an API change. If you receive a value you do not recognise, treat the order as cancelled for an unknown reason. Do not fail.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.