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

# Submit an order

> One endpoint, three actions. `type` picks between opening a position, closing one (fully or partially), and withdrawing collateral from one. Pick the variant in the request body below to see the fields each needs.

### Reading the response

**`status` is the answer, not the HTTP code.** A `200` means the order was submitted and settled; `status` says how it settled:

| `status` | What happened |
| --- | --- |
| `filled` | It executed. `execution` carries the `tradeId` and the resulting amounts. |
| `reverted` | It reached the chain and changed nothing. `reason` says why. |

### Signing

Every intent is an EIP-712 signature under this domain:

```json
{
  "name": "OstiumAtomicTrading",
  "version": "1",
  "chainId": 42161,
  "verifyingContract": "0x5eB3960C3fd3274cD81fE5972e0de01084bDa325"
}
```

On Arbitrum Sepolia (`421614`) it is a different contract, so the domain differs too:

```json
{
  "name": "OstiumAtomicTrading",
  "version": "1",
  "chainId": 421614,
  "verifyingContract": "0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3"
}
```

The struct you sign is the `intent` object itself, and its `primaryType` is named after the `type` you send:

| `type` | `primaryType` |
| --- | --- |
| `open` | `OpenIntent` |
| `close` | `CloseIntent` |
| `removeCollateral` | `RemoveCollateralIntent` |

Declare the fields in the order the request body lists them, **and with exactly these solidity widths**. EIP-712 hashes the field names AND types into a single typehash, so a different order — or `uint256` where the contract says `uint192` — is a different type: the digest changes and the contract cannot match your signature. The JSON schema below shows `string` and `integer`, which cannot express the widths, so take them from here:

```
OpenIntent(uint256 collateral,uint192 openPrice,uint192 tp,uint192 sl,address trader,
  uint32 leverage,uint16 pairIndex,bool buy,bool isDayTrade,address builder,
  uint32 builderFee,uint256 slippageP,uint256 nonce,uint256 deadline)

CloseIntent(address trader,uint16 pairIndex,uint8 index,uint256 tradeId,
  uint16 closePercentage,uint192 marketPrice,uint32 slippageP,uint256 nonce,
  uint256 deadline)

RemoveCollateralIntent(address trader,uint16 pairIndex,uint8 index,uint256 tradeId,
  uint256 removeAmount,uint256 nonce,uint256 deadline)
```

(Line breaks above are for reading only — the canonical type string has none.)

As a viem `signTypedData` call:

```ts
await account.signTypedData({
  domain,                       // the OstiumAtomicTrading domain above
  primaryType: 'OpenIntent',
  types: {
    OpenIntent: [
      { name: 'collateral', type: 'uint256' },
      { name: 'openPrice',  type: 'uint192' },
      { name: 'tp',         type: 'uint192' },
      { name: 'sl',         type: 'uint192' },
      { name: 'trader',     type: 'address' },
      { name: 'leverage',   type: 'uint32'  },
      { name: 'pairIndex',  type: 'uint16'  },
      { name: 'buy',        type: 'bool'    },
      { name: 'isDayTrade', type: 'bool'    },
      { name: 'builder',    type: 'address' },
      { name: 'builderFee', type: 'uint32'  },
      { name: 'slippageP',  type: 'uint256' },
      { name: 'nonce',      type: 'uint256' },
      { name: 'deadline',   type: 'uint256' },
    ],
  },
  message: intent,
});
```

To check your work without spending anything, the wrapper exposes `hashOpenIntent`, `hashCloseIntent` and `hashRemoveCollateralIntent` as view functions — a local digest that matches those is a signature the contract will accept.

Sign as `trader`: an EOA, or a contract wallet that answers EIP-1271.

**One trap the schema cannot warn you about.** `slippageP` is a `uint256` on `OpenIntent` but a `uint32` on `CloseIntent`. The contract types them differently, and signing the wrong width silently changes the digest.

### Limits

Checked before anything is submitted, and tighter than the contract itself — an intent the contract would accept can still be refused here:

| Field | Allowed | Meaning |
| --- | --- | --- |
| `slippageP` | `0 < slippageP ≤ 100` | scaled by 10000, so `100` is 1% — the most you can tolerate |
| `closePercentage` | `0 < closePercentage ≤ 10000` | scaled by 10000, so `10000` is 100% and `5000` is half |
| `deadline` | `now < deadline ≤ now + 60s` | unix seconds |

Both percentages use the same base of **10000**. Neither may be `0`: a zero `slippageP` cannot fill, and the protocol reads a zero `closePercentage` as a *full* close.

### Before your first order

The trader must be an allowlisted partner, and onboarded once per chain through `POST /v1/onboard`. Without the delegation an order reaches the chain and reverts as `NotDelegate`.

### Field notes

- **Send amounts as strings of plain digits** — `"50000000"`, not `5e7` or `50000000`. They are too large for a JSON number, and rounding one changes the digest.
- **Addresses can be any casing.** Checksummed, lowercase or uppercase all work.
- **Every field is signed**, `builder` and `builderFee` included. Nothing can be added or altered between you and the contract.

Rate limit: 30 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/orders
openapi: 3.1.0
info:
  title: Ostium Builder API
  version: 1.0.0
  description: >
    REST API for the Ostium Builder SDK and integrating partners: live prices,
    depth, OHLC, and market hours.


    ## Authentication


    **None required today.**


    ## Quick start


    1. **Snapshot** — `GET /v1/prices` (or `GET /v1/prices/{pair}`) for an
    initial board.

    2. **Stream** — connect to `WS /v1/prices/stream` for live ticks — see the
    **Price Stream** section.

    3. **Candles** — `POST /v1/ohlc` for historical OHLC.

    4. **Sessions** — `GET /v1/market-hours` for calendars (cache it; use tick
    flags for “open right now”). The pair list is under **Markets**.

    5. **Depth** — `GET /v1/depth/{pair}/quote` for the price a given size
    actually gets.

    6. **Status** — `GET /v1/status` to check the price feed is live before
    relying on quotes.


    Typed clients: use `@ostium/builder-sdk`, or generate one from this document
    with any OpenAPI 3.1 client generator.


    ## Rate limits


    Applied per IP. Most limits are per route; where a group of endpoints shares
    one budget the endpoint's own description says so. Every rate-limited
    response carries `x-ratelimit-limit`, `x-ratelimit-remaining`, and
    `x-ratelimit-reset`; 429s add `retry-after`. **Read the headers** — do not
    hard-code the documented figures.


    ## Errors


    Two envelopes:


    - **Framework** — `{ "error": "<Status Name>", "message": "..." }` (optional
    `issues` / `details`).

    - **Upstream proxy** — `{ "error": "<human message>" }` from OHLC failures.


    `error` means different things across the two shapes — do not switch on it
    alone until a future major version unifies them.


    Every response carries `x-request-id`. Quote it when reporting a problem.


    ## Hosts


    - Production: `https://builder.prod.bedrock.ostium.io`
servers:
  - url: https://builder.prod.bedrock.ostium.io
    description: Production
security: []
tags:
  - name: Prices
    description: Live prices and historical candles
  - name: Price Stream
    description: >-
      Live prices over a WebSocket. This section is the contract — everything
      below is what the

      server actually sends.


      ### Connecting


      ```

      wss://builder.prod.bedrock.ostium.io/v1/prices/stream?pairs=BTC-USD,ETH-USD

      ```


      `?pairs=` is optional; omit it to receive every asset. No authentication,
      though the handshake is

      capped — see **Limits**. Browser clients are subject to an origin
      allowlist; a rejected upgrade is

      closed with a bare `403`.


      ### Server messages


      One snapshot on connect, carrying the `seq` baseline for every asset:


      ```json

      { "type": "snapshot", "seq": { "BTC-USD": 41 }, "data": [{ "pair":
      "BTC-USD", "bid": 1, "mid": 1, "ask": 1 }] }

      ```


      Then one frame per tick:


      ```json

      { "type": "tick", "seq": 42, "data": { "pair": "BTC-USD", "bid": 1, "mid":
      1, "ask": 1 } }

      ```


      `data` is byte-identical to a row from `GET /v1/prices` — `seq` sits on
      the frame, not inside it,

      so the tick payload stays the same shape as REST.


      | `type` | Sent when |

      | --- | --- |

      | `snapshot` | Once, on connect |

      | `tick` | A price updates |

      | `gap` | Ticks were dropped for you — precedes the next frame you receive
      |

      | `ack` | Your `subscribe`/`unsubscribe` was applied |

      | `error` | Your message was rejected; the filter is unchanged |


      ### Client messages


      ```json

      { "type": "subscribe",   "pairs": ["EUR-USD"] }

      { "type": "unsubscribe", "pairs": ["EUR-USD"] }

      ```


      Every message gets exactly one reply — an `ack` carrying `pairCount` (the
      number of assets you now

      receive; `null` is all of them, `0` is none), or an `error` carrying a
      `code` of `malformed`,

      `unknown_type`, `invalid_pairs`, `no_filter` or `rate_limited`.


      ```json

      { "type": "ack",   "for": "subscribe", "pairCount": 2 }

      { "type": "error", "code": "no_filter", "message": "..." }

      ```


      Filters only widen. `subscribe` on an unfiltered connection is a no-op
      acked with

      `"pairCount": null` — to narrow, reconnect with `?pairs=`. `unsubscribe`
      needs a filtered

      connection and is rejected with `no_filter` otherwise. Unsubscribing your
      last asset leaves you

      receiving nothing (`"pairCount": 0`), which `subscribe` recovers without
      reconnecting.


      ### Detecting loss


      Delivery is best effort, but loss is **detectable**. If more than 1 MB is
      buffered for a slow

      client, ticks are dropped rather than queued — and you are told:


      ```json

      { "type": "gap", "dropped": 17 }

      ```


      `seq` is a per-asset counter, so a jump between consecutive `tick` frames
      for one asset means you

      missed that many. Baseline each asset from the `snapshot`, then compare.


      Two limits on what `seq` can tell you. It is **per connection** —
      process-wide, not global, so it

      does not survive a reconnect and is not comparable across replicas. And
      there is **no replay**: the

      stream tells you that you fell behind, not what you missed, so recover by
      re-reading

      `GET /v1/prices`.


      Assets with no trading schedule are omitted entirely, from the snapshot
      and from ticks. A missing

      asset is not a signal that its market is closed.


      ### Limits


      Per pod, so the effective ceiling scales with replica count. Read the
      headers on a rejected

      upgrade rather than hard-coding these.


      | Limit | Default | Rejected with |

      | --- | --- | --- |

      | Connections per IP | 20 | `429` |

      | Connections in total | 500 | `503` |

      | Upgrades per IP per minute | 60 | `429` |

      | Client messages per socket per minute | 120 | `error`, `code:
      "rate_limited"` |


      Rejected upgrades carry `Retry-After`.


      ### Heartbeat


      The server pings every 30 seconds and terminates a connection that has not
      ponged since the

      previous ping. Browsers pong automatically; other clients need a library
      that does. On shutdown the

      server closes with `1001 going away` after draining.
    x-traitTag: true
  - name: Liquidity
    description: >-
      What a given trade size actually costs, priced against the live impact
      model
  - name: Markets
    description: >-
      What is listed and when it trades.


      ### Pairs


      Every pair on Ostium. Use `id` wherever a request takes a `pairIndex`, and
      the symbol wherever

      one takes a `pair`. Do not infer the id from a row's position — read the
      column.


      A pair marked `not yet` in **Tradeable** is listed on chain but has no
      open-interest ceiling, so

      it cannot be traded and `GET /v1/depth/{pair}` answers `503` for it (the
      `/quote`

      sub-resource still prices it, with `availableNotionalUsd: 0`). It is shown
      rather than hidden so

      the ids either side of it stay right.


      Note `GET /v1/pairs` returns the subgraph's own spelling in `from`/`to`,
      which for renamed

      assets is the legacy one — `SPX` where this table says `US500`. The table
      uses the symbol the

      price and depth endpoints expect.


      <!-- MARKETS:START -->

      | id | Pair | Tradeable |

      | -- | ---- | --------- |

      | 0 | `BTC-USD` | yes |

      | 1 | `ETH-USD` | yes |

      | 2 | `EUR-USD` | yes |

      | 3 | `GBP-USD` | yes |

      | 4 | `USD-JPY` | yes |

      | 5 | `XAU-USD` | yes |

      | 6 | `XCU-USD` | yes |

      | 7 | `WTI-USD` | yes |

      | 8 | `XAG-USD` | yes |

      | 9 | `SOL-USD` | yes |

      | 10 | `US500-USD` | yes |

      | 11 | `US30-USD` | yes |

      | 12 | `US100-USD` | yes |

      | 13 | `JP225-JPY` | yes |

      | 14 | `UK100-GBP` | yes |

      | 15 | `GER40-EUR` | yes |

      | 16 | `USD-CAD` | yes |

      | 17 | `USD-MXN` | yes |

      | 18 | `NVDA-USD` | yes |

      | 19 | `GOOG-USD` | yes |

      | 20 | `AMZN-USD` | yes |

      | 21 | `META-USD` | yes |

      | 22 | `TSLA-USD` | yes |

      | 23 | `AAPL-USD` | yes |

      | 24 | `MSFT-USD` | yes |

      | 25 | `USD-CHF` | yes |

      | 26 | `AUD-USD` | yes |

      | 27 | `NZD-USD` | yes |

      | 28 | `XPD-USD` | yes |

      | 29 | `XPT-USD` | yes |

      | 30 | `HK50-HKD` | yes |

      | 31 | `COIN-USD` | yes |

      | 32 | `HOOD-USD` | yes |

      | 33 | `MSTR-USD` | yes |

      | 34 | `CRCL-USD` | yes |

      | 35 | `BMNR-USD` | yes |

      | 36 | `SBET-USD` | yes |

      | 37 | `GLXY-USD` | yes |

      | 38 | `BNB-USD` | yes |

      | 39 | `XRP-USD` | yes |

      | 40 | `TRX-USD` | yes |

      | 41 | `HYPE-USD` | yes |

      | 42 | `LINK-USD` | yes |

      | 43 | `ADA-USD` | yes |

      | 44 | `PLTR-USD` | yes |

      | 45 | `AMD-USD` | yes |

      | 46 | `NFLX-USD` | yes |

      | 47 | `ORCL-USD` | yes |

      | 48 | `RIVN-USD` | yes |

      | 49 | `COST-USD` | yes |

      | 50 | `XOM-USD` | yes |

      | 51 | `CVX-USD` | yes |

      | 52 | `URA-USD` | yes |

      | 53 | `USD-KRW` | not yet |

      | 54 | `KR2550-USD` | yes |

      | 55 | `BRENT-USD` | yes |

      | 56 | `GEV-USD` | yes |

      | 57 | `SHEL-USD` | yes |

      | 58 | `UNG-USD` | yes |

      | 59 | `XLE-USD` | yes |

      | 60 | `ARM-USD` | yes |

      | 61 | `ASML-USD` | yes |

      | 62 | `AVGO-USD` | yes |

      | 63 | `CAT-USD` | yes |

      | 64 | `INTC-USD` | yes |

      | 65 | `SMCI-USD` | yes |

      | 66 | `TSM-USD` | yes |

      | 67 | `MU-USD` | yes |

      | 68 | `SNDK-USD` | yes |

      | 69 | `HYG-USD` | yes |

      | 70 | `TLT-USD` | yes |

      | 71 | `MP-USD` | yes |

      | 72 | `DRAM-USD` | yes |

      | 73 | `REMX-USD` | yes |

      | 74 | `BB-USD` | yes |

      | 75 | `CRWV-USD` | yes |

      | 76 | `DELL-USD` | yes |

      | 77 | `MRNA-USD` | yes |

      | 78 | `MRVL-USD` | yes |

      | 79 | `NBIS-USD` | yes |

      | 80 | `LLY-USD` | yes |

      | 81 | `SKHY-USD` | yes |

      | 82 | `SPCX-USD` | yes |

      <!-- MARKETS:END -->
  - name: Portfolio
    description: A wallet's open positions, resting orders and executed history
  - name: Orders
    description: >-
      Partner-signed intents executed as a single transaction — the position
      opens or closes and fills, or the whole call reverts. Onboarding lives
      here too: it is the one-time step every order depends on.
  - name: Status
    description: Whether the API is answering usefully right now
paths:
  /v1/orders:
    post:
      tags:
        - Orders
      summary: Submit an order
      description: >-
        One endpoint, three actions. `type` picks between opening a position,
        closing one (fully or partially), and withdrawing collateral from one.
        Pick the variant in the request body below to see the fields each needs.


        ### Reading the response


        **`status` is the answer, not the HTTP code.** A `200` means the order
        was submitted and settled; `status` says how it settled:


        | `status` | What happened |

        | --- | --- |

        | `filled` | It executed. `execution` carries the `tradeId` and the
        resulting amounts. |

        | `reverted` | It reached the chain and changed nothing. `reason` says
        why. |


        ### Signing


        Every intent is an EIP-712 signature under this domain:


        ```json

        {
          "name": "OstiumAtomicTrading",
          "version": "1",
          "chainId": 42161,
          "verifyingContract": "0x5eB3960C3fd3274cD81fE5972e0de01084bDa325"
        }

        ```


        On Arbitrum Sepolia (`421614`) it is a different contract, so the domain
        differs too:


        ```json

        {
          "name": "OstiumAtomicTrading",
          "version": "1",
          "chainId": 421614,
          "verifyingContract": "0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3"
        }

        ```


        The struct you sign is the `intent` object itself, and its `primaryType`
        is named after the `type` you send:


        | `type` | `primaryType` |

        | --- | --- |

        | `open` | `OpenIntent` |

        | `close` | `CloseIntent` |

        | `removeCollateral` | `RemoveCollateralIntent` |


        Declare the fields in the order the request body lists them, **and with
        exactly these solidity widths**. EIP-712 hashes the field names AND
        types into a single typehash, so a different order — or `uint256` where
        the contract says `uint192` — is a different type: the digest changes
        and the contract cannot match your signature. The JSON schema below
        shows `string` and `integer`, which cannot express the widths, so take
        them from here:


        ```

        OpenIntent(uint256 collateral,uint192 openPrice,uint192 tp,uint192
        sl,address trader,
          uint32 leverage,uint16 pairIndex,bool buy,bool isDayTrade,address builder,
          uint32 builderFee,uint256 slippageP,uint256 nonce,uint256 deadline)

        CloseIntent(address trader,uint16 pairIndex,uint8 index,uint256 tradeId,
          uint16 closePercentage,uint192 marketPrice,uint32 slippageP,uint256 nonce,
          uint256 deadline)

        RemoveCollateralIntent(address trader,uint16 pairIndex,uint8
        index,uint256 tradeId,
          uint256 removeAmount,uint256 nonce,uint256 deadline)
        ```


        (Line breaks above are for reading only — the canonical type string has
        none.)


        As a viem `signTypedData` call:


        ```ts

        await account.signTypedData({
          domain,                       // the OstiumAtomicTrading domain above
          primaryType: 'OpenIntent',
          types: {
            OpenIntent: [
              { name: 'collateral', type: 'uint256' },
              { name: 'openPrice',  type: 'uint192' },
              { name: 'tp',         type: 'uint192' },
              { name: 'sl',         type: 'uint192' },
              { name: 'trader',     type: 'address' },
              { name: 'leverage',   type: 'uint32'  },
              { name: 'pairIndex',  type: 'uint16'  },
              { name: 'buy',        type: 'bool'    },
              { name: 'isDayTrade', type: 'bool'    },
              { name: 'builder',    type: 'address' },
              { name: 'builderFee', type: 'uint32'  },
              { name: 'slippageP',  type: 'uint256' },
              { name: 'nonce',      type: 'uint256' },
              { name: 'deadline',   type: 'uint256' },
            ],
          },
          message: intent,
        });

        ```


        To check your work without spending anything, the wrapper exposes
        `hashOpenIntent`, `hashCloseIntent` and `hashRemoveCollateralIntent` as
        view functions — a local digest that matches those is a signature the
        contract will accept.


        Sign as `trader`: an EOA, or a contract wallet that answers EIP-1271.


        **One trap the schema cannot warn you about.** `slippageP` is a
        `uint256` on `OpenIntent` but a `uint32` on `CloseIntent`. The contract
        types them differently, and signing the wrong width silently changes the
        digest.


        ### Limits


        Checked before anything is submitted, and tighter than the contract
        itself — an intent the contract would accept can still be refused here:


        | Field | Allowed | Meaning |

        | --- | --- | --- |

        | `slippageP` | `0 < slippageP ≤ 100` | scaled by 10000, so `100` is 1%
        — the most you can tolerate |

        | `closePercentage` | `0 < closePercentage ≤ 10000` | scaled by 10000,
        so `10000` is 100% and `5000` is half |

        | `deadline` | `now < deadline ≤ now + 60s` | unix seconds |


        Both percentages use the same base of **10000**. Neither may be `0`: a
        zero `slippageP` cannot fill, and the protocol reads a zero
        `closePercentage` as a *full* close.


        ### Before your first order


        The trader must be an allowlisted partner, and onboarded once per chain
        through `POST /v1/onboard`. Without the delegation an order reaches the
        chain and reverts as `NotDelegate`.


        ### Field notes


        - **Send amounts as strings of plain digits** — `"50000000"`, not `5e7`
        or `50000000`. They are too large for a JSON number, and rounding one
        changes the digest.

        - **Addresses can be any casing.** Checksummed, lowercase or uppercase
        all work.

        - **Every field is signed**, `builder` and `builderFee` included.
        Nothing can be added or altered between you and the contract.


        Rate limit: 30 requests per 10 seconds per IP. Read `x-ratelimit-*` for
        the live budget rather than assuming this figure.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    type:
                      type: string
                      enum:
                        - open
                    chainId:
                      anyOf:
                        - type: number
                          enum:
                            - 42161
                        - type: number
                          enum:
                            - 421614
                      description: >-
                        Which chain to trade on. Arbitrum One is 42161, Arbitrum
                        Sepolia 421614.
                      example: 421614
                    intent:
                      type: object
                      properties:
                        collateral:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Margin you are putting up in USDC, scaled by 1e6 —
                            10 USDC is `"10000000"`. Example: `"50000000"`.
                          example: '50000000'
                        openPrice:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            The price you are willing to open at, scaled by 1e18
                            — 79680.28 is `"79680280000000000000000"`. This is a
                            bound, not the fill — the executed price may be
                            better, and worse is rejected by `slippageP`. Use
                            the current ask for a long and the bid for a short;
                            `GET /v1/prices/{pair}` returns both.
                          example: '79680280000000000000000'
                        tp:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Take-profit price, scaled by 1e18 — 79680.28 is
                            `"79680280000000000000000"`. Send `"0"` for none.
                          example: '0'
                        sl:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Stop-loss price, scaled by 1e18 — 79680.28 is
                            `"79680280000000000000000"`. Send `"0"` for none.
                          example: '0'
                        trader:
                          type: string
                          pattern: ^0x[a-fA-F0-9]{40}$
                          description: >-
                            The address signing this intent, and the one that
                            will hold the trade.
                          example: '0x98279066957A9eAF627D33983409A548CF3e2207'
                        leverage:
                          type: integer
                          description: >-
                            Leverage, scaled by 100 — 10x is `1000`. The maximum
                            is per-pair: read `group.maxLeverage` from the pairs
                            list, or see the Markets section.
                          minimum: 1
                          example: 1000
                        pairIndex:
                          type: integer
                          description: >-
                            The pair's id, as returned by `GET /v1/pairs` in
                            `id` — BTC-USD is `0`. It is not a position in that
                            list: do not derive it by counting rows. See the
                            Markets section for the full set.
                          minimum: 0
                          example: 0
                        buy:
                          type: boolean
                          description: '`true` opens a long, `false` a short.'
                          example: true
                        isDayTrade:
                          type: boolean
                          description: >-
                            Marks the trade as intraday. Send `false` unless you
                            are opening a day trade.
                          example: false
                        builder:
                          type: string
                          pattern: ^0x[a-fA-F0-9]{40}$
                          description: >-
                            Address credited for this trade. Send the zero
                            address `0x0000000000000000000000000000000000000000`
                            unless you are attributing flow to a builder.
                          example: '0x98279066957A9eAF627D33983409A548CF3e2207'
                        builderFee:
                          type: integer
                          description: >-
                            Fee paid to `builder`, scaled by 10000 — `100` is
                            1%. Send `0` when `builder` is the zero address.
                          minimum: 0
                          example: 0
                        slippageP:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            How much worse than `openPrice` you will accept,
                            scaled by 10000 — `100` is 1%. Must be `0 <
                            slippageP <= 100`. A fill worse than this reverts
                            instead.
                          example: '100'
                        nonce:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Any number this trader has not used before — a
                            random uint256 is fine. There is no counter to
                            follow, so pre-signed intents can be sent in any
                            order. Reusing one that already executed reverts as
                            `NonceAlreadyUsed`.
                          example: >-
                            84305382736775539472473887403939913494969546473727073051743279873203688380042
                        deadline:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Unix seconds after which this intent can no longer
                            be used. Must be in the future and at most 60
                            seconds ahead — `Math.floor(Date.now() / 1000) + 45`
                            is a good default. Keep it short: until it passes,
                            anyone holding the signature can submit it.
                          example: '1787916350'
                      required:
                        - collateral
                        - openPrice
                        - tp
                        - sl
                        - trader
                        - leverage
                        - pairIndex
                        - buy
                        - isDayTrade
                        - builder
                        - builderFee
                        - slippageP
                        - nonce
                        - deadline
                    signature:
                      type: string
                      pattern: ^0x([a-fA-F0-9]{2})+$
                      description: >-
                        EIP-712 signature: 65-byte ECDSA, or opaque bytes for an
                        ERC-1271 contract wallet
                  required:
                    - type
                    - chainId
                    - intent
                    - signature
                  title: Open
                - type: object
                  properties:
                    type:
                      type: string
                      enum:
                        - close
                    chainId:
                      anyOf:
                        - type: number
                          enum:
                            - 42161
                        - type: number
                          enum:
                            - 421614
                      description: >-
                        Which chain to trade on. Arbitrum One is 42161, Arbitrum
                        Sepolia 421614.
                      example: 421614
                    intent:
                      type: object
                      properties:
                        trader:
                          type: string
                          pattern: ^0x[a-fA-F0-9]{40}$
                          description: >-
                            The address signing this intent. Must be the trade's
                            owner.
                          example: '0x98279066957A9eAF627D33983409A548CF3e2207'
                        pairIndex:
                          type: integer
                          description: >-
                            The pair's id, as returned by `GET /v1/pairs` in
                            `id` — not a position in that list. See the Markets
                            section.
                          minimum: 0
                          example: 0
                        index:
                          type: integer
                          minimum: 0
                          maximum: 255
                          description: >-
                            The trade's slot on that pair —
                            `execution.tradeIndex` from the open.
                          example: 1
                        tradeId:
                          type: string
                          pattern: ^[1-9]\d*$
                          description: >-
                            The trade to close — `execution.tradeId` from the
                            open.
                          example: '151066'
                        closePercentage:
                          type: integer
                          minimum: 1
                          maximum: 10000
                          description: >-
                            How much of the position to close, scaled by 10000 —
                            `10000` is the whole thing and `5000` is half. Must
                            be `0 < closePercentage <= 10000`. Never send `0`:
                            the protocol reads it as a full close, so it is
                            refused here.
                          example: 10000
                        marketPrice:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            The price you are willing to close at, scaled by
                            1e18 — 79680.28 is `"79680280000000000000000"`. Use
                            the bid to close a long and the ask to close a
                            short.
                          example: '79547890000000000000000'
                        slippageP:
                          type: integer
                          minimum: 1
                          maximum: 4294967295
                          description: >-
                            How much worse than `marketPrice` you will accept,
                            scaled by 10000 — `100` is 1%. Must be `0 <
                            slippageP <= 100`. Note this is a number here and a
                            string on `OpenIntent`; the contract types them
                            differently and signing the wrong width changes the
                            digest.
                          example: 100
                        nonce:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Any number this trader has not used before — a
                            random uint256 is fine. There is no counter to
                            follow, so pre-signed intents can be sent in any
                            order. Reusing one that already executed reverts as
                            `NonceAlreadyUsed`.
                          example: >-
                            84305382736775539472473887403939913494969546473727073051743279873203688380042
                        deadline:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Unix seconds after which this intent can no longer
                            be used. Must be in the future and at most 60
                            seconds ahead — `Math.floor(Date.now() / 1000) + 45`
                            is a good default. Keep it short: until it passes,
                            anyone holding the signature can submit it.
                          example: '1787916350'
                      required:
                        - trader
                        - pairIndex
                        - index
                        - tradeId
                        - closePercentage
                        - marketPrice
                        - slippageP
                        - nonce
                        - deadline
                    signature:
                      type: string
                      pattern: ^0x([a-fA-F0-9]{2})+$
                      description: >-
                        EIP-712 signature: 65-byte ECDSA, or opaque bytes for an
                        ERC-1271 contract wallet
                  required:
                    - type
                    - chainId
                    - intent
                    - signature
                  title: Close
                - type: object
                  properties:
                    type:
                      type: string
                      enum:
                        - removeCollateral
                    chainId:
                      anyOf:
                        - type: number
                          enum:
                            - 42161
                        - type: number
                          enum:
                            - 421614
                      description: >-
                        Which chain to trade on. Arbitrum One is 42161, Arbitrum
                        Sepolia 421614.
                      example: 421614
                    intent:
                      type: object
                      properties:
                        trader:
                          type: string
                          pattern: ^0x[a-fA-F0-9]{40}$
                          description: >-
                            The address signing this intent. Must be the trade's
                            owner.
                          example: '0x98279066957A9eAF627D33983409A548CF3e2207'
                        pairIndex:
                          type: integer
                          description: >-
                            The pair's id, as returned by `GET /v1/pairs` in
                            `id` — not a position in that list. See the Markets
                            section.
                          minimum: 0
                          example: 0
                        index:
                          type: integer
                          minimum: 0
                          maximum: 255
                          description: >-
                            The trade's slot on that pair —
                            `execution.tradeIndex` from the open.
                          example: 1
                        tradeId:
                          type: string
                          pattern: ^[1-9]\d*$
                          description: The trade to withdraw from.
                          example: '151066'
                        removeAmount:
                          type: string
                          pattern: ^[1-9]\d*$
                          description: >-
                            Collateral to withdraw in USDC, scaled by 1e6 — 10
                            USDC is `"10000000"`. Example: `"2000000"`.
                            Withdrawing raises the position's leverage, so it
                            must stay inside the pair's maximum — the new value
                            comes back as `execution.newLeverage`.
                          example: '2000000'
                        nonce:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Any number this trader has not used before — a
                            random uint256 is fine. There is no counter to
                            follow, so pre-signed intents can be sent in any
                            order. Reusing one that already executed reverts as
                            `NonceAlreadyUsed`.
                          example: >-
                            84305382736775539472473887403939913494969546473727073051743279873203688380042
                        deadline:
                          type: string
                          pattern: ^\d+$
                          description: >-
                            Unix seconds after which this intent can no longer
                            be used. Must be in the future and at most 60
                            seconds ahead — `Math.floor(Date.now() / 1000) + 45`
                            is a good default. Keep it short: until it passes,
                            anyone holding the signature can submit it.
                          example: '1787916350'
                      required:
                        - trader
                        - pairIndex
                        - index
                        - tradeId
                        - removeAmount
                        - nonce
                        - deadline
                    signature:
                      type: string
                      pattern: ^0x([a-fA-F0-9]{2})+$
                      description: >-
                        EIP-712 signature: 65-byte ECDSA, or opaque bytes for an
                        ERC-1271 contract wallet
                  required:
                    - type
                    - chainId
                    - intent
                    - signature
                  title: Remove collateral
              discriminator:
                propertyName: type
            example:
              type: open
              chainId: 421614
              intent:
                collateral: '1000000000'
                openPrice: '2500000000000000000000'
                tp: '0'
                sl: '0'
                trader: '0x2222222222222222222222222222222222222222'
                leverage: 10000
                pairIndex: 1
                buy: true
                isDayTrade: false
                builder: '0x0000000000000000000000000000000000000000'
                builderFee: 0
                slippageP: '50'
                nonce: '42'
                deadline: '1800000030'
              signature: '0x3045022100abcdef'
      responses:
        '200':
          description: Terminal outcome of the submission
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - filled
                      - reverted
                    description: >-
                      `filled` means the order executed. `reverted` means it
                      reached the chain and changed nothing.
                  userOpHash:
                    type: string
                    description: >-
                      Identifies the ERC-4337 user operation we submitted for
                      you. Quote it if you need to ask us about a specific
                      order.
                    example: >-
                      0x8c8b184c3208dd11c0275a0cd5a62d0ef0fb4578d1f9b36569b2d346048af4f7
                  transactionHash:
                    type: string
                    description: The transaction it landed in — look it up on Arbiscan.
                    example: >-
                      0x9cae473bf1c8d54da1d32138e7bbe1d2ca05213c98fe70945b402634aac13279
                  reason:
                    type: string
                    description: >-
                      Why it reverted, decoded where we recognise the error.
                      Present only when `status` is `reverted`.
                    example: >-
                      NotDelegate(0x98279066…, 0x32C06a3e…) — the trader has not
                      delegated
                  execution:
                    type: object
                    properties:
                      tradeId:
                        type: string
                        description: >-
                          The trade this order acted on. On an open it is the
                          new trade's id — keep it, you need it to close.
                        example: '151066'
                      tradeIndex:
                        type: number
                        description: open only — the slot the protocol assigned
                      fillPrice:
                        type: string
                        description: >-
                          open and close — the price actually executed, which is
                          not the price you signed: that was the bound, this is
                          the fill.
                      collateral:
                        type: string
                        description: >-
                          open only — collateral after fees, so lower than what
                          you signed
                      tradeNotional:
                        type: string
                        description: >-
                          open only — how much of the asset the position holds,
                          e.g. `0.0124` BTC. A quantity, not a USD amount.
                      usdcSentToTrader:
                        type: string
                        description: close only — what the trader actually received
                      percentProfit:
                        type: string
                        description: close only — signed; negative on a loss
                      percentClosed:
                        type: string
                        description: close only — `100` for a full close
                      removedAmount:
                        type: string
                        description: >-
                          removeCollateral only — the collateral withdrawn, in
                          USDC
                      newLeverage:
                        type: string
                        description: >-
                          removeCollateral only — the position's leverage after
                          the withdrawal
                    required:
                      - tradeId
                    description: >-
                      What the order executed as. Present only when `status` is
                      `filled`, and the fields depend on the `type`. Amounts are
                      decimal strings, already scaled.
                required:
                  - status
                  - userOpHash
                  - transactionHash
              example:
                status: filled
                userOpHash: '0xaaaa'
                transactionHash: '0xbbbb'
                execution:
                  tradeId: '150885'
                  tradeIndex: 0
                  fillPrice: '79680.281023459830864072'
                  collateral: '98.9'
                  tradeNotional: '0.01241210481811446'
        '400':
          description: Validation failed
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Bad Request
                message: Validation failed
                issues: []
        '401':
          description: The signature does not prove control of the intent's `trader`
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
                message: signature does not match the intent
        '403':
          description: The `trader` is not a permitted partner
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Forbidden
                message: trader is not a permitted partner
        '413':
          description: Request body is larger than the parser limit
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Payload Too Large
                message: request entity too large
        '429':
          description: Rate limit exceeded
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
            retry-after:
              schema:
                type: string
              description: Seconds to wait before retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Too Many Requests
                message: Rate limit exceeded. Try again in 7s.
        '500':
          description: Unexpected error
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Internal Server Error
                message: Internal Server Error
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: HTTP status name, e.g. "Bad Request"
          example: Bad Request
        message:
          type: string
          description: Human-readable detail
          example: Validation failed
        issues:
          type: array
          items: {}
          description: Raw zod issue array — present on validation failures only
        details:
          description: Optional structured detail from the thrower
      required:
        - error
        - message

````