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

# Quoting API

> Post and refresh a multi-level ladder of two-sided quotes with one atomic bulk call, and pull it with cancel-all

The core of a market-making loop is one call: **replace your entire ladder of
two-sided quotes on a market in a single atomic operation**. This page covers
authentication, posting a ladder, and cancelling.

All traffic goes through the OCX API gateway (one HTTPS base URL, referred to
below as `{API_BASE}`). Prices and quantities are JSON strings (fixed-decimal).

## Authentication

<Steps>
  <Step title="Get a nonce">
    ```
    POST {API_BASE}/auth/nonce
    { "walletAddress": "0x…" }  →  { "nonce": "…" }
    ```
  </Step>

  <Step title="Sign in with SIWE">
    Sign the EIP-4361 message (including the nonce) and verify:

    ```
    POST {API_BASE}/auth/siwe
    { "message": "<SIWE message>", "signature": "0x…" }
    ```

    This opens a session (cookie / JWT).
  </Step>

  <Step title="Mint an API key for automation">
    ```
    POST {API_BASE}/perps/me/api-keys
    { "name": "mm-bot-1", "scope": "trade", "allowedIps": ["203.0.113.7"], "expiresInDays": 90 }
    ```

    The response includes a plaintext `secret` — **shown once only**. Store it and
    send `x-api-key: <secret>` on every automated request.
  </Step>
</Steps>

<Warning>
  The API-key secret is returned exactly once at creation and is never retrievable
  again. Store it in a secret manager. Pin `allowedIps` to your egress addresses and
  set `expiresInDays` for rotation. Keys carry a scope: use `trade` for quoting.
  Key creation and revocation are session-only — a key cannot manage keys.
</Warning>

The bulk-quote, cancel-all, and preview endpoints accept **either** a session
cookie **or** an API key — the programmatic quoting path is fully API-key enabled.

## Preview before you quote (optional)

Dry-run a prospective order to check sizing, fee, and margin with no side effects.

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST "{API_BASE}/perps/orders/preview" \
    -H "x-api-key: $OCX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"marketId":"BTC-PERP","side":"buy","type":"limit","price":"64990","quantity":"0.5"}'
  ```

  ```js JavaScript theme={null}
  const res = await fetch(`${API_BASE}/perps/orders/preview`, {
    method: "POST",
    headers: { "x-api-key": OCX_API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ marketId: "BTC-PERP", side: "buy", type: "limit", price: "64990", quantity: "0.5" }),
  });
  const preview = await res.json();
  ```
</CodeGroup>

<ResponseField name="notional" type="string">Order notional in quote currency.</ResponseField>
<ResponseField name="marginRequired" type="string">Margin the order would consume.</ResponseField>
<ResponseField name="estFee" type="string">Estimated fee for this fill.</ResponseField>
<ResponseField name="feeBps" type="string">Fee in basis points at your tier.</ResponseField>
<ResponseField name="feeRole" type="string">`maker` or `taker`.</ResponseField>
<ResponseField name="user30dVolume" type="string">Your rolling 30-day volume (drives your tier).</ResponseField>
<ResponseField name="approved" type="boolean">Whether the order would pass the margin gate.</ResponseField>
<ResponseField name="reason" type="string">Rejection reason when `approved` is false.</ResponseField>

## Post and refresh a ladder

`POST {API_BASE}/perps/quotes/bulk` — **Auth: session or API key.**

Build a symmetric set of bid/ask levels around your fair value and submit them in
one call. Set `cancelAll: true` to make the call an atomic **replace**: your prior
resting quotes on that market are cancelled and the new ladder is placed in one
operation. This is the standard way to re-quote each cycle without leaving stale
orders behind.

<ParamField body="marketId" type="string" required>The market to quote.</ParamField>
<ParamField body="quoteId" type="string">Tags this quoting cycle so you can group/track/replace the batch.</ParamField>
<ParamField body="cancelAll" type="boolean">When `true`, cancels your existing quotes on this market before placing the new ladder (atomic replace).</ParamField>

<ParamField body="quotes" type="array" required>
  The ladder levels. Each entry accepts `side` (`buy`|`sell`), `price`, `quantity`, and optional `clientOrderId`, `timeInForce`, `postOnly`, `reduceOnly`, `marginMode`.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST "{API_BASE}/perps/quotes/bulk" \
    -H "x-api-key: $OCX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "marketId": "BTC-PERP",
      "quoteId": "mm-cycle-000123",
      "cancelAll": true,
      "quotes": [
        { "side": "buy",  "price": "64990.0", "quantity": "0.50", "postOnly": true },
        { "side": "sell", "price": "65010.0", "quantity": "0.50", "postOnly": true },
        { "side": "buy",  "price": "64980.0", "quantity": "1.00", "postOnly": true },
        { "side": "sell", "price": "65020.0", "quantity": "1.00", "postOnly": true }
      ]
    }'
  ```

  ```js JavaScript theme={null}
  async function requote(fair) {
    await fetch(`${API_BASE}/perps/quotes/bulk`, {
      method: "POST",
      headers: { "x-api-key": OCX_API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({
        marketId: "BTC-PERP",
        quoteId: `mm-cycle-${Date.now()}`,
        cancelAll: true,
        quotes: [
          { side: "buy",  price: (fair - 10).toFixed(1), quantity: "0.50", postOnly: true },
          { side: "sell", price: (fair + 10).toFixed(1), quantity: "0.50", postOnly: true },
          { side: "buy",  price: (fair - 20).toFixed(1), quantity: "1.00", postOnly: true },
          { side: "sell", price: (fair + 20).toFixed(1), quantity: "1.00", postOnly: true },
        ],
      }),
    });
  }
  ```
</CodeGroup>

<Tip>
  Keep `postOnly: true` on every quote level to guarantee you stay on the maker
  side — a `postOnly` order that would cross the book and take liquidity is rejected
  rather than executed as a taker.
</Tip>

### Single-market convenience form

To place just one bid and one ask, use `POST {API_BASE}/perps/quotes`:

```json theme={null}
{
  "marketId": "BTC-PERP",
  "bid": { "price": "64990.0", "quantity": "0.5" },
  "ask": { "price": "65010.0", "quantity": "0.5" },
  "postOnly": true,
  "quoteId": "mm-single-1"
}
```

## Cancel

Pull quotes on shutdown, on feed loss, or as a risk kill-switch.

<Tabs>
  <Tab title="Cancel all">
    `POST {API_BASE}/perps/orders/cancel-all` — **Auth: session or API key.**

    Cancel all your resting orders, optionally scoped by `marketId`, `quoteId`,
    `clientOrderId`, or `marginMode`. Omit the body to cancel everything.

    ```bash theme={null}
    curl -s -X POST "{API_BASE}/perps/orders/cancel-all" \
      -H "x-api-key: $OCX_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "marketId": "BTC-PERP" }'
    ```
  </Tab>

  <Tab title="Cancel one">
    `POST {API_BASE}/perps/orders/{id}/cancel` — cancel a single resting order by
    its id.

    ```bash theme={null}
    curl -s -X POST "{API_BASE}/perps/orders/ORDER_ID/cancel" \
      -H "x-api-key: $OCX_API_KEY"
    ```
  </Tab>
</Tabs>

<Note>
  The typical re-quote cycle is a single `quotes/bulk` call with `cancelAll: true` —
  you rarely need standalone cancels except as an emergency stop. Wire
  `cancel-all` to your dead-man switch (see
  [Best practices](/market-makers/best-practices)).
</Note>

## Options quoting

The options CLOB exposes an equivalent bulk path at `POST {API_BASE}/options/quotes/bulk`
(with `POST {API_BASE}/quotes/bulk` as an alias), accepting a session or API key.
The single-quote form is `POST {API_BASE}/options/quotes`. The ladder shape and
`cancelAll` replace semantics match the perp path above.
