# HTTP — `POST /v1/exchange`

One request carries one action type: a batch of up to 20 places, modifies, or
cancels, a mass cancel, a deposit request, or a withdrawal request. Every order
names its `venue`; Mosaiq never reroutes it.

Separate HTTP requests can arrive and complete out of order, even for the
same user. Once received, actions on the same user, venue, and client order ID
share dependencies with the [write WebSocket](/api/exchange-websocket#ordering).
Use one write WebSocket when you need the server to receive a placement before
a following cancel or modify.

```http
POST /v1/exchange
Authorization: Bearer <your-api-key>
```

| Action | Auth |
| --- | --- |
| `place`, `modify`, `cancel`, `mass_cancel` | API key |
| `deposit` | API key or Para JWT |
| `withdraw` | Para JWT (`X-Tessera-Wallet` when the JWT holds several EVM wallets) |

## Disconnects and shutdown

REST and WebSocket use the same acceptance and recovery contract for place,
modify, and cancel. Once the server owns an action, a client disconnect does not
cancel its processing. A server crash before durable acceptance may discard the
action, including a cancel. The answer is the acknowledgement. Mosaiq sends no
receipt before it.

If the connection closes before the answer arrives, treat the result as
unknown. The server may already have launched the venue send. Check
`orderUpdates` and `openOrders` by `cloid` before retrying.

## Place

A placement is a limit order when it carries `p` and `t`, and a market order
when it carries `slippage` instead. Mixing `slippage` with `p` or `t`, or
sending only one of `p` and `t`, is rejected with `unsupported`.

Limit `t` is `Ioc` or `Alo` (post-only). `Gtc` parses but is rejected with
`unsupported_tif`.

A market order is a bounded IOC. `slippage` is a fraction in `(0, 0.5]`; the
venue-side limit is the venue mid price moved by that fraction. Margin is
reserved at the Mosaiq oracle price moved by the same fraction.

```jsonc
{
  "type": "place",
  "orders": [
    {
      "a": "BTC",
      "b": "buy",
      "s": "1",
      "slippage": "0.01",
      "venue": "hyperliquid",
      "c": "0x00000000000000000000000000000001"
    }
  ]
}
```

### Reduce-only

`reduceOnly: true` requires a current position on the selected venue and a
reducing side (sell against a long, buy against a short). A flat position or
the wrong side is rejected with `reduce_only` before venue dispatch.

An oversized request is clamped to the reducible position rather than
rejected. Existing reduce-only orders are not subtracted when computing that
clamp. Newer placements and size increases take capacity first and can
permanently trim or cancel older reduce-only orders on the same user, asset,
venue, and side. A position decrease trims them too. A trim is reverted only
when the placement that caused it is rejected before it is sent to the venue.

Mosaiq re-applies the venue size increment and minimums after clamping; if the
result is not a valid venue order the item is rejected with `min_notional`.
This applies to `Ioc` and market reduce-only orders as well, so an immediate
order can displace older resting protection even when it does not fill. The
accepted response returns the clamped `size`; reconcile later values from
`openOrders` or `orderUpdates`.

```jsonc
{
  "type": "place",
  "orders": [
    {
      "a": "BTC",
      "b": "sell",
      "p": "65000",
      "s": "0.5",
      "venue": "hyperliquid",
      "t": "Ioc",
      "reduceOnly": true,
      "c": "0x00000000000000000000000000000005"
    }
  ]
}
```

## Cancel

Cancels identify the order by `venue` and `c`. The asset is resolved from the
original placement. Venue order IDs are never exposed.

A cancel answers `accepted` once Mosaiq launched its venue send. The answer does
not prove that the order is terminal. The order's own `orderUpdates` rows tell
how it ended: `cancelled`, `filled`, or `failed`. A cancel has no row of its
own, and the venue's answer to the cancel changes nothing on the order. A
cancel of a placement that was never sent needs no venue send, so it is accepted
even while the venue reconnects. If the
venue client or the venue refuses the cancel, or the venue does not answer it,
Mosaiq cancels the same order again once the venue can take it, up to five times
within about 60 s. If those fail too, the order stays open. Check it on
`orderUpdates` and cancel it again. A cancel
sent while the venue cannot take it, for example during a reconnect, answers
`venue_unavailable` at once. Nothing was accepted, so send it again. Repeated
cancels share the captured order target and its pending cancellation. A repeated
cancel answers once that cancellation's launch is known, with the same item, or
with that launch's error if it failed. Retrying an uncertain Lighter
cancellation can also send it to the venue again.

```jsonc
{
  "type": "cancel",
  "orders": [
    { "venue": "lighter", "c": "0x00000000000000000000000000000002" }
  ]
}
```

## Modify

`modify` replaces the price and total size of a resting limit order by `c`, on
Hyperliquid or Lighter. `a` and `b` must match the original placement. Order
type and time-in-force cannot change and are read from the existing order;
sending `t` is an unknown field and fails with HTTP `422 invalid_request`.
Modifying a market order fails with `unsupported_order_type`; an `s` below the
already-filled size fails with `bad_request`.

For a reduce-only order, increasing `s` takes new allocation priority and the
requested size is clamped to the reducible position. A price-only modify or a
size decrease keeps its priority, and trimmed size stays trimmed.

A modify answers `accepted` once Mosaiq launched its venue send, and its `size`
is the size that send carries after the reduce-only clamp. The venue's verdict
shows on the order's `orderUpdates` row, with the new price and size or with the
previous price and size and a `failure`. A modify sent right after its place
waits up to two seconds for the venue to answer the place. If the venue has not
confirmed the placement by then, the modify answers
`modify_placement_not_confirmed` and is not sent.

```jsonc
{
  "type": "modify",
  "orders": [
    {
      "venue": "hyperliquid",
      "c": "0x00000000000000000000000000000005",
      "a": "BTC",
      "b": "buy",
      "p": "63000",
      "s": "1"
    }
  ]
}
```

## Mass cancel

`mass_cancel` cancels every open order of the authenticated user. `venues`
restricts it; omit it or pass `[]` for all venues. An unknown venue code fails
the whole request with `unknown_venue`.

```jsonc
{ "type": "mass_cancel", "venues": ["hyperliquid"] }
```

The response `data` holds one item per targeted open order, in no guaranteed
order. Each item answers once Mosaiq launched that order's cancel: `accepted`
with the `orderId` of that cancel, the order's `cloid`, and its `venue`, or that
order's own error. An order that another cancel already holds answers like a
repeated cancel. The answer does not wait for any order to end. Each order then
ends on its own `orderUpdates` rows, as after a single cancel. A user with
nothing open gets `"data": []`. The 20-action cap does not apply.

## Deposit

A deposit is USDC on Arbitrum from the registered wallet to the returned
treasury address. The request is tracked for one hour; the minimum is 1 USDC.
Mosaiq credits the account only after observing a transfer whose sender is the
registered wallet and whose amount equals `amount` exactly, before
`expiresAtMs`. Transfers below 1 USDC are ignored. Other unmatched transfers
and deposits above the automatic-credit limit require manual review.

```jsonc
{ "type": "deposit", "amount": "100" }
```

```jsonc
{
  "status": "ok",
  "data": [
    {
      "status": "accepted",
      "requestId": "...",
      "kind": "deposit",
      "amount": "100",
      "chain": "arbitrum",
      "from": "0x...",
      "to": "0x...",
      "expiresAtMs": 1780003600100
    }
  ],
  "server_time_ms": 1780000000100,
  "server_receive_time_ms": 1780000000088
}
```

## Withdraw

`withdraw` creates a tracked request to the registered wallet; it is not an
instant payout. The amount must have at most 6 decimals, the precision of USDC,
or the request fails with HTTP `400`. The amount must not exceed the lower of
`totalCashUsdc` and `availableFunds`, and only one request per user can be
pending; otherwise the request fails with `insufficient_available_funds`. A rolling per-user cap,
10,000 USDC per hour by default, counts pending and completed requests in the
window and returns HTTP `429 withdraw_limit_exceeded`.

If funding settlement for your account is pending, `withdraw` returns HTTP
`409 funding_settlement_pending`. Retry after settlement completes. Funding
settlement can also delay completion of an existing pending withdrawal.

```http
Authorization: Bearer <para-jwt>
X-Tessera-Wallet: 0x... # required when the JWT has multiple EVM wallets
```

```jsonc
{ "type": "withdraw", "amount": "100" }
```

```jsonc
{
  "status": "ok",
  "data": [
    {
      "status": "accepted",
      "requestId": "...",
      "kind": "withdraw",
      "amount": "100",
      "chain": "arbitrum",
      "destination": "0x..."
    }
  ],
  "server_time_ms": 1780000000100,
  "server_receive_time_ms": 1780000000088
}
```

Track both request kinds through [`GET /v1/funding`](/api/funding).

## Batches

`orders` carries up to 20 actions of one kind: places (limit and market, any
venues), modifies, or cancels. Receipt order follows the array. Unrelated items
can execute independently, so array order does not determine which placement
reserves margin first. `data` answers item by item, by input index. An empty `orders` array fails the request with `bad_request` (place,
modify) or `invalid_cancel` (cancel); more than 20 items fails it with
`batch_too_large` and nothing is processed.

A batch is not atomic and does not guarantee submission together in one
venue request. Each item has its own outcome. On the
[WebSocket API](/api/exchange-websocket#ordering), a later cancel can prevent
an eligible pending placement inside a batch. The other items continue, and
the prevented item answers `accepted` in its original position.

```jsonc
{
  "type": "place",
  "orders": [
    { "a": "BTC", "b": "buy", "p": "65000", "s": "1", "venue": "lighter", "t": "Alo", "expiresInMs": 3600000, "c": "0x00000000000000000000000000000003" },
    { "a": "ETH", "b": "sell", "s": "0.1", "slippage": "0.01", "venue": "hyperliquid", "c": "0x00000000000000000000000000000004" }
  ]
}
```

## Fields

| Field | Type | Description |
| --- | --- | --- |
| `type` | `string` | `place`, `modify`, `cancel`, `mass_cancel`, `deposit`, or `withdraw`. |
| `a` | `string` | Canonical asset from `data.assetData.universe[*].name`. Place and modify. |
| `b` | `string` | `buy` or `sell`. |
| `p` | `string` | Limit price. Required with `t`; absent on market orders. |
| `s` | `string` | Size in base units. |
| `slippage` | `string` | Market-order slippage fraction in `(0, 0.5]`, e.g. `"0.01"`. |
| `venue` | `string` | Venue code from `data.assetData.universe[*].venues[*].venue`. Required. |
| `venues` | `array` | `mass_cancel` filter; empty means all. |
| `t` | `string` | `Ioc` or `Alo`. Required on limit placements; not accepted on modify. |
| `reduceOnly` | `boolean` | Place only. See [Reduce-only](#reduce-only). |
| `c` | `string` | Client order id. Optional on place, required on modify and cancel. |
| `amount` | `string` | USDC amount for `deposit` and `withdraw`. |

`c` is `0x` followed by exactly 32 lowercase hexadecimal characters and must
not be all zeros. Uppercase is rejected. If omitted on a REST placement, Mosaiq
generates one and returns it. A user cannot hold two open orders with the same
`c`; a repeated `c` inside one place or cancel batch fails the later item with
`duplicate_cloid`. Modify batches are not deduplicated.

For placements, this batch rule applies to both REST and WebSocket. A later
duplicate remains rejected if the first placement fails margin admission or
is prevented by a cancel before venue dispatch. The later item does not
become a fallback placement. See [WebSocket ordering](/api/exchange-websocket#ordering)
for cancellation of pending placements. Separate REST requests have no
ordering guarantee.

Decimal values are JSON strings in both directions. Send each decimal field
(`p`, `s`, `slippage`, `priorityFeeBps`, `amount`) as a plain decimal: digits
and an optional `.` followed by digits, for example `"0.5"` or `"65000"`.
Mosaiq refuses every other form and never reads it as another number. This
includes separators (`"0_5"`), exponents (`"1e-1"`, `"5E-3"`), a `+` sign,
spaces, `".5"`, `"5."`, an empty string, a JSON number, and more than 28 digits
after the point. The whole request then fails with HTTP `422 invalid_request`,
and Mosaiq sends no item. A negative value fails the rule of its field instead.

### Venue-specific fields

Some optional fields exist on one venue only. Sending one elsewhere returns
`unsupported`.

Hyperliquid:

* `priorityFeeBps` (`string`, basis points, for example `"2.5"`). An optional
  Hyperliquid priority fee on a Hyperliquid core `Ioc` or market order, when
  Mosaiq enables priority fees. Mosaiq signs the order alone in its action with
  your rate. Each fill bills the rate times your filled notional, in USDC: the
  fill shows it in `fees.priority`. An order that does not fill pays nothing.

  * The rate must be above 0 and at most the limit that Mosaiq sets (currently
    8 bp), in steps of 0.0001 bp. A finer rate is refused, not rounded.
  * Send a plain decimal string, such as `"0.5"`. A separator or an exponent
    (`"0_5"`, `"5e-1"`) fails the whole request with `422 invalid_request`.
  * HIP-3 markets, Lighter, `Alo` orders and modify requests do not accept it.
  * When the stake that pays Hyperliquid cannot cover the fee, the item gets
    `broker_capacity` and nothing is sent: retry shortly, or send the order
    without `priorityFeeBps`.

  See the [Hyperliquid priority fees documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/priority-fees).

Lighter (`lighter` and `lighter:robinhood`):

* `expiresInMs` (`number`). Lifetime of an `Alo` placement, from `610000`
  milliseconds (10 minutes 10 seconds) to `2592000000` milliseconds (30 days).
  Omitting it uses 30 days. An out-of-range value returns `invalid_expiry`.
  Lighter removes the order from the book when it expires.

Lighter trades some markets only in isolated margin. On such a market, Mosaiq
stops a placement before it reaches the venue while the Mosaiq venue account is
not yet set to isolated margin there. The placement still answers `accepted`,
and `orderUpdates` reports the order `failed` with `failure.code`
`market_unavailable` and the reason. Retry later, or send the order to another
venue. Reduce-only placements are not stopped for this reason.

## Precision and constraints

Clients send decimal strings for `p` and `s` and never encode venue-native
formats. Each venue market publishes its constraints in
[`data.assetData.universe[*].venues[*].constraints`](/api/clearinghouse-state):

| Field | Meaning |
| --- | --- |
| `sizeDecimals`, `sizeIncrement` | Size precision and step. |
| `priceDecimals`, `tickSize` | Price precision and grid. |
| `minNotional`, `minBaseAmount` | Minimum quote notional and base size, when the venue has them. |
| `maxBaseAmount`, `maxLimitNotional`, `maxMarketNotional` | Maximums; exceeding one fails with `bad_request`. |
| `supportsReduceOnly`, `supportsPostOnly`, `requiresIsolatedMargin` | Venue capabilities. |

Mosaiq fits the request to the selected venue before sending it:

* `s` rounds down to the venue's size precision and increment;
* a buy limit `p` rounds down and a sell limit `p` rounds up, so the price
  never becomes more aggressive;
* a size that rounds to zero fails with `bad_request`, a price that rounds to
  zero with `invalid_price`, a price that cannot be placed on the tick grid with
  `invalid_precision`, and a notional below the minimum with `min_notional`.

Only the affected item is rejected. Mosaiq remains the authority at acceptance
time; use `constraints` for client-side prevalidation and sizing.

## Response

`data` is an array aligned by index with `orders` (see
[Mass cancel](#mass-cancel) for the exception). Each item is an
acknowledgement, not a venue outcome, and no item waits for the venue. Each item
carries a `status`:

* `accepted`: Mosaiq owns the action and launched its venue send. A placement
  carries `orderId`, `cloid`, `venue`, and `size`, the size after venue fitting
  and any reduce-only clamp. A modify carries the same fields, and its `size` is
  the size its send carries. A cancel carries its own `orderId`, not the order's,
  with `cloid` and `venue` and no `size`. A placement that your own cancel
  stopped before any send also answers `accepted`. Funding requests carry their
  request metadata. `accepted` does not prove that the venue took the order, that
  it rests, or that it filled or ended.
* `error`: Mosaiq refused the action before any venue effect. The item carries
  `code`, `msg`, and `cloid` when known. No `orderUpdates` row follows for that
  `cloid`, and you can use it again. Other items in the batch keep their own
  results.

Every venue outcome reaches you only on [`orderUpdates`](/api/websocket#orderupdates)
and [`userFills`](/api/websocket#userfills), as
[Follow each write on orderUpdates](#follow-each-write-on-orderupdates) shows.

```jsonc
// success
{
  "status": "ok",
  "data": [
    {
      "status": "accepted",
      "orderId": "...",
      "cloid": "0x00000000000000000000000000000002",
      "venue": "hyperliquid",
      "size": "1"
    }
  ],
  "server_time_ms": 1780000000100,
  "server_receive_time_ms": 1780000000088
}

// request-level error: no data[] is returned
{
  "status": "error",
  "data": { "code": "...", "msg": "..." },
  "server_time_ms": 1780000000100
}
```

The top-level `status` is `ok` once the request itself was processed, even if
every item is an `error`. Request-level errors cover authentication, rate
limits, malformed bodies, empty or oversized batches, an unknown
`mass_cancel` venue, and funding failures. Risk-engine, recovery, and halt
rejections are item-level for `place` and `modify`. One Lighter failure mode
is also request-level: when a `place` or `modify` item meets the degraded
Lighter transaction quota before its launch, the whole response becomes HTTP
`503` with `venue_unavailable`. Other items of the batch may already be
accepted and sent, so check them by `cloid`. Cancel batches return their item
results instead.

An unexpected processing failure can also return a request-level error after
earlier actions were sent. Check the batch's orders before retrying it.

`risk_core_unavailable` and `gateway_unavailable` can affect the whole request
or one item. An item-level `gateway_unavailable` means that item was not sent;
other items may already have succeeded.

Two errors can still be followed by an `orderUpdates` row:

* `request_uncertain`: Mosaiq cannot confirm whether it accepted the action.
  The action may still execute after recovery, and a cancel may already have
  stopped a pending placement. Reconcile through `orderUpdates` or `openOrders`
  before retrying. Sibling results remain in `data[]`, including orders already
  accepted. Do not retry an entire batch on the assumption that no item was
  sent.
* A launch that fails after Mosaiq made the action durable, with nothing sent.
  This happens when the venue actor stopped, or when the venue client refuses
  the maintenance that a reduce-only placement needs on another execution
  account. The item answers its error. For a placement, the order then shows
  one `failed` row; for a modify, the order's row shows the modify's
  `failure`. That failure carries the item's error code, such as
  `venue_unavailable`, or `other` when no order failure code has that name. A cancel leaves its order unchanged, and your next cancel of that
  order is a new cancel.

### Follow each write on orderUpdates

Each example below shows the REST answer, then the order's
[`orderUpdates`](/api/websocket#orderupdates) rows, trimmed to the fields that
matter. The write WebSocket answers the same items as `{"accepted":{"cloid"}}`.

A placement is accepted, then rests. An `accepted` row can come first, while the
order is on its way to the venue. `open` means venue evidence shows it resting:

```json
{ "status": "accepted", "orderId": "01a0f6a1-2cfb-7313-93a3-35e8d2902c88", "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "size": "1" }
```

```json
{ "orderId": "01a0f6a1-2cfb-7313-93a3-35e8d2902c88", "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "accepted", "size": "1", "price": "64000", "filledSize": "0" }
{ "orderId": "01a0f6a1-2cfb-7313-93a3-35e8d2902c88", "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "open", "size": "1", "price": "64000", "filledSize": "0" }
```

Hyperliquid refuses a post-only placement that would cross the book. The answer
is still `accepted`. The row is `failed`, and `reason` and `rawCode` are the
Hyperliquid status name. Hyperliquid's own sentence is not shown:

```json
{ "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "failed", "failure": { "code": "invalid_order", "reason": "badAloPxRejected", "rawCode": "badAloPxRejected" } }
```

The venue never answers. Two seconds after the launch, the order becomes
`uncertain`. Mosaiq then reconciles it by `cloid` and publishes its real state.
It never sends the order again:

```json
{ "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "uncertain", "failure": { "code": "transport_unavailable", "reason": "hyperliquid place acknowledgement timed out after 2000ms; venue send status unknown" } }
```

A modify is refused. The answer is `accepted` with the modify's `size`. The
order's row keeps its previous price and size and carries the modify's
`failure`:

```json
{ "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "open", "size": "1", "price": "64000", "failure": { "code": "invalid_order", "reason": "badAloPxRejected", "rawCode": "badAloPxRejected" } }
```

A cancel is accepted with its own `orderId`. The order's next row tells how it
ended. It is `cancelled` when the cancel won, `filled` when a fill came first,
and `failed` when the venue refused the placement. No cancel outcome shows as
`not_on_book`:

```json
{ "status": "accepted", "orderId": "7c1e2a90-55d4-4b0e-9a3f-0d6a1c5e8b21", "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid" }
```

```json
{ "cloid": "0x00000000000000000000000000000005", "venue": "hyperliquid", "status": "cancelled", "size": "1", "filledSize": "0" }
```

Your cancel stops a placement before Mosaiq sends it. The placement and the
cancel both answer `accepted`, and the order shows a plain `cancelled` with no
`failure`. The order never rested.

A `mass_cancel` answers one `accepted` per targeted order, each with the
`orderId` of the cancel launched for it. Each order then ends on its own rows:

```json
{
  "status": "ok",
  "data": [
    { "status": "accepted", "orderId": "3b9d6f12-8c4e-4f71-b2a5-6e0c9d1f7a34", "cloid": "0x00000000000000000000000000000003", "venue": "lighter" },
    { "status": "accepted", "orderId": "a4f08c3e-1d27-4b96-8e5a-2c7b9f0d6e13", "cloid": "0x00000000000000000000000000000004", "venue": "hyperliquid" }
  ],
  "server_time_ms": 1780000000100,
  "server_receive_time_ms": 1780000000088
}
```

### Cancel-only mode

While Mosaiq's risk engine catches up on a backlog, it accepts only cancels:

* Every `place` and `modify` item returns `risk_engine_stalled`, reduce-only
  placements included. Nothing is sent to the venue. In a batch, the refusal is
  item-level and the response is HTTP `200`.
* `withdraw` returns HTTP `503` with `risk_engine_stalled`.
* `cancel` and `mass_cancel` work as usual.

Your resting orders stay on the book and can still fill. Fills, funding, and
liquidations continue. The mode ends by itself once the backlog drains; retry
refused actions after a short delay.

### Startup recovery

After a restart, Mosaiq can recover its state on every venue before it opens
normal trading. While this startup recovery is in progress:

* Every `place` and `modify` item returns `startup_recovery_in_progress`,
  reduce-only placements included. Nothing is sent to the venue. In a batch,
  the refusal is item-level and the response is HTTP `200`.
* `withdraw` returns HTTP `503` with `startup_recovery_in_progress`.
* `cancel` and `mass_cancel` work as usual.

Normal trading opens when every venue has recovered. It stays open: a later
recovery of one venue uses `venue_recovering` and `account_recovering`
instead. Retry refused actions after a short delay.

### Error codes

Request-level:

| Code | HTTP | Meaning |
| --- | --- | --- |
| `unauthorized` | 401 | Missing or invalid bearer token, including `withdraw` sent with an API key. |
| `not_whitelisted`, `not_registered` | 403 | Wallet not allowlisted, or no account yet. |
| `wallet_required`, `wallet_not_connected` | 400 / 403 | `X-Tessera-Wallet` missing or not held by the JWT. |
| `invalid_json`, `unsupported_media_type` | 400 / 415 | Body is not JSON. |
| `invalid_request` | 422 | Body parses but has unknown or ill-typed fields, or a decimal string that is not plain (see [Fields](#fields)). |
| `bad_request` | 400 | Generic validation failure, including an empty batch, deposit amounts below 1 USDC, and withdrawal amounts of zero or less. |
| `invalid_cancel` | 400 | Empty cancel batch. |
| `batch_too_large` | 400 | More than 20 actions, or a request too large for Mosaiq to record durably. Nothing was sent; split the request before sending it again. |
| `unknown_venue` | 400 | Unknown venue code in `mass_cancel.venues`. |
| `insufficient_available_funds` | 403 | Withdrawal exceeds withdrawable funds, or another withdrawal is pending. |
| `funding_settlement_pending` | 409 | Funding settlement is pending for the account. Retry the withdrawal after settlement completes. |
| `withdraw_limit_exceeded` | 429 | Rolling withdrawal cap reached. |
| `rate_limited` | 429 | HTTP or exchange budget exceeded. |
| `venue_unavailable` | 503 | A `place` or `modify` item met the degraded Lighter transaction quota before its launch. Other items of the batch may be accepted; check them by `cloid`. |
| `inflight_limit_exceeded` | 429 | WebSocket only: the client IP already has 128 requests in flight across its connections. Resend once a response arrives. |
| `treasury_not_configured` | 503 | Deposits are unavailable; no receiving address is configured. |
| `operations_halted` | 403 | `withdraw` during a platform-wide trading halt. |
| `risk_engine_stalled` | 503 | `withdraw` while the risk engine is stalled or [cancel-only](#cancel-only-mode); item-level on `place` and `modify`. |
| `startup_recovery_in_progress` | 503 | `withdraw` during [startup recovery](#startup-recovery); item-level on `place` and `modify`. Retryable. |
| `risk_core_unavailable`, `gateway_unavailable` | 503 | Mosaiq cannot process the request. These errors can also appear on individual items. |
| `request_uncertain` | 503 | Mosaiq cannot confirm whether it accepted the request. It may still execute; check `orderUpdates` or `openOrders` before retrying. Also item-level. |
| `internal_error` | 500 | Unexpected failure; also item-level. |

Item-level codes (`data[].code`) are refusals made before any venue effect.
`request_uncertain` and a launch that fails after the action became durable are
the only item errors that an `orderUpdates` row can follow:

| Code | Meaning |
| --- | --- |
| `gateway_unavailable` | Mosaiq could not accept this item, so it was not sent. Check the other batch results separately. |
| `request_uncertain` | Mosaiq cannot confirm whether it accepted the action. It may still execute, so an `orderUpdates` row can follow; check `orderUpdates` or `openOrders` before retrying. |
| `unknown_asset`, `unknown_venue`, `symbol_mapping_missing`, `inactive_market` | The market is not tradable on that venue. |
| `unsupported` | Ambiguous placement: `slippage` with `p`/`t`, `p` or `t` alone, or none of them. Also `expiresInMs` sent on a placement other than `lighter` or `lighter:robinhood` `Alo`, and `priorityFeeBps` when priority fees are off or on an order other than a Hyperliquid core `Ioc` or market order. |
| `unsupported_tif` | `t` is not `Ioc` or `Alo`. |
| `unsupported_order_type` | Modify targets a market order. |
| `invalid_slippage` | `slippage` outside `(0, 0.5]`. |
| `invalid_expiry` | `expiresInMs` outside `610000`–`2592000000`. |
| `invalid_priority_fee` | `priorityFeeBps` is zero, finer than 0.0001 bp, or above the limit that Mosaiq sets. |
| `invalid_cloid` | `c` is not lowercase non-zero 128-bit hex. |
| `duplicate_cloid` | Open order with this `c`, or `c` repeated in the batch. |
| `bad_request`, `invalid_price`, `invalid_precision`, `min_notional` | Size or price failed fitting or limits. `invalid_price` also covers a limit price outside the venue price band around the oracle price. `bad_request` also covers a modify whose new size is below the quantity already filled, including a fill that landed while the modify was being admitted. |
| `market_metadata_missing` | The venue market lacks metadata needed to build the order, or Mosaiq has no fee rate for the market. The item was not sent. Retry later. |
| `cap_exceeded` | Exceeds a `userCaps` limit or available margin. |
| `margin_unavailable`, `oracle_unavailable` | Mosaiq cannot price margin for the market. |
| `venue_unavailable` | Mosaiq cannot send to the venue now. Venue state needed for admission is missing or stale, the venue connection is not ready, the venue queue is full, execution is disabled for the venue, or Lighter execution is degraded after quota exhaustion. Nothing was sent. |
| `venue_busy` | Mosaiq's send capacity for this venue is full. This item was not sent. It is not your rate limit. Retry after a short delay. |
| `maintenance_in_progress` | A fresh venue-status source reports maintenance in progress. Place and modify are temporarily refused, including on HIP-3 venues and Lighter Robinhood. Cancels are not refused for maintenance. See [maintenance announcements](/api/exchange-websocket#maintenance-announcements) for timing and source coverage. |
| `reduce_only` | No reducible position, the reduce-only ceiling is exceeded while another replacement is unresolved, or the reducible position changed while a reduce-only modify was being admitted. |
| `order_not_found_for_cancel` | `c` does not match a live order (cancel and modify). A modify also gets this code when its target filled or was cancelled while the modify was being admitted. |
| `in_flight_admission_conflict` | Another cancel for this order is still being accepted, or ended without cancelling it. Retry the cancel. |
| `modify_in_flight` | An earlier modify on this order remains unresolved after the two-second confirmation wait. This item was not sent. Wait for the earlier outcome on `orderUpdates` before retrying. |
| `modify_placement_not_confirmed` | The venue has not confirmed the placement. This modify was not sent. Check `orderUpdates` before retrying it. |
| `pre_venue_resource_timeout` | Mosaiq could not prepare the action for submission within 2 seconds. Check the order before retrying. |
| `broker_capacity` | The placement carries `priorityFeeBps`, and the Mosaiq HYPE stake on Hyperliquid cannot pay that fee. Nothing was sent. `msg` is `priority fee capacity on hyperliquid is temporarily unavailable; retry shortly, or send the order without priorityFeeBps`. A venue refusal for Mosaiq's pooled margin is not an item code. It shows on `orderUpdates` as `failure.code` `broker_capacity`, possibly with `failure.retryAfterMs`. See [Rebalancing](/concepts/rebalancing). |
| `venue_recovering`, `account_recovering` | Trading is temporarily restricted during recovery; see `data.venueData.meta.recovery`. |
| `startup_recovery_in_progress` | Mosaiq has not finished [startup recovery](#startup-recovery) on every venue. Every `place` and `modify` is refused, reduce-only placements included. The item was not sent. Retry after a short delay. |
| `risk_engine_stalled`, `risk_core_unavailable` | The risk engine cannot admit the item; retry later. In [cancel-only mode](#cancel-only-mode), every `place` and `modify` gets `risk_engine_stalled`. |
| `execution_lane_quarantined` | Trading on that venue account is suspended while Mosaiq checks its order state. |
| `execution_risk_control_blocked` | Risk-increasing execution on that venue is blocked by a venue safety control. |
| `operations_halted` | Trading is halted platform-wide. Place and modify are refused, except immediate reduce-only placements. Cancels remain available. |
| `internal_error` | Unexpected failure. |

These codes no longer answer items, because they described venue outcomes:
`place_rejected`, `place_failed`, `place_uncertain`, `modify_rejected`,
`modify_failed`, `modify_uncertain`, `cancel_rejected`, `cancel_failed`,
`cancel_uncertain`, `cancel_not_on_book`, `cancel_target_filled`,
`cancel_target_rejected`, `cancel_timeout`, and `preemptive_cancel`. Read the
outcome on `orderUpdates` instead. The `cancelled` item status and the
`retryAfterMs` field of an item are gone too.
