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

# Create Order Book

> Open a trading venue for an imported ERC-3643 security

Creates an order book for one imported security token quoted in one settlement asset. Three properties are fixed by this venue and cannot be overridden: settlement is always on-chain, the token type is always an external security, and the trading mode is always continuous.

The book cannot be created until the four on-chain prerequisites hold. See [Custody setup](/endpoints/external-securities-trading/introduction#custody-setup).

One instance may hold a single open book per pair. If an `ACTIVE` or `PAUSED` book already quotes this pair, the request returns `ORDER_BOOK_EXISTS`.

## Body Parameters

<ParamField body="tokenAddress" type="string" required>
  The ERC-3643 security token. Must be imported into your instance and readable on chain.
</ParamField>

<ParamField body="quoteTokenAddress" type="string" required>
  Settlement asset the security is quoted in. Must be authorized for this security on the custody contract, which is one of the four setup steps. Must differ from `tokenAddress`.
</ParamField>

<ParamField body="name" type="string" required>Display name, 1 to 64 characters.</ParamField>

<ParamField body="symbol" type="string" required>Display symbol, 1 to 16 characters.</ParamField>

<ParamField body="settlementOperator" type="string" required>
  Wallet that will sign settlement transactions for this book. It must be authorized on the custody contract for this security, which is the fourth setup step. Naming a wallet you control is what lets settlements be driven through this API.
</ParamField>

<ParamField body="underlyingIdentifier" type="string">
  Instrument identifier, 1 to 31 characters, trimmed and upper-cased. An ISIN, a WKN, or an issuer's own reference.

  Required when the security is not yet registered on the custody contract, because registration writes this identifier on chain. Optional afterwards, and if supplied it must match what is already registered, otherwise `ISIN_MISMATCH`. The stored value always comes from the chain.
</ParamField>

<ParamField body="custodyContract" type="string">
  Custody contract to use. Defaults to the address from [Get Custody Contract](/endpoints/external-securities-trading/get-custody-contract). Any other address is refused with `INVALID_CONFIG`.
</ParamField>

<ParamField body="minOrderSize" type="string" default="0">Smallest accepted quantity, in order book units.</ParamField>

<ParamField body="maxOrderSize" type="string">Largest accepted quantity, in order book units.</ParamField>

<ParamField body="tickSize" type="string" default="1">
  Price increment, in order book units. Enforced when an order is submitted: a price that is not a multiple is refused with `INVALID_PRICE`. [Prepare Order](/endpoints/external-securities-trading/prepare-order) does not check it, so a payload can be built at an off-tick price and then rejected on submission.
</ParamField>

<ParamField body="makerFeeBps" type="integer" default="0">Maker fee in basis points, 0 to 1000.</ParamField>

<ParamField body="takerFeeBps" type="integer" default="0">Taker fee in basis points, 0 to 1000.</ParamField>

<ParamField body="mmMakerRebateBps" type="integer">
  Fee applied in place of `makerFeeBps` when the resting side is a market maker account, −1000 to 1000. A negative value pays the market maker.
</ParamField>

<ParamField body="allowImport" type="boolean" default="false">
  Publish the book so other instances on the same network can import it.
</ParamField>

<ParamField body="allowSharedLiquidity" type="boolean" default="false">
  Pool depth with other books quoting the same instrument identifier. See [Shared liquidity](/endpoints/external-securities-trading/introduction#shared-liquidity).
</ParamField>

<ParamField body="priceRefEnabled" type="boolean" default="false">Enforce a reference price band on incoming orders.</ParamField>

<ParamField body="priceRefMode" type="string" default="MANUAL">`MANUAL`, `STOCK_FEED` or `EXTERNAL_FEED`.</ParamField>

<ParamField body="priceRefFeedUrl" type="string">HTTPS feed endpoint, up to 2048 characters. Must resolve to a public host.</ParamField>

<ParamField body="priceRefFeedHeaders" type="string">Feed request headers as a JSON string, up to 4096 characters.</ParamField>

<ParamField body="priceRefFeedPath" type="string">JSON path to the price, or the ticker symbol in `STOCK_FEED` mode.</ParamField>

<ParamField body="priceRefEnforcement" type="string" default="EVERY_TRADE">Refresh interval, from `EVERY_TRADE` to `1DAY`.</ParamField>

<ParamField body="priceRefSpreadBps" type="integer">Band half-width in basis points, 1 to 10000.</ParamField>

<ParamField body="priceRefHaltOnStale" type="boolean" default="false">Refuse orders when the reference is stale.</ParamField>

<ParamField body="priceRefStaleTolerance" type="integer">Staleness window in seconds, 60 to 604800.</ParamField>

<ParamField body="priceRefCircuitBreakerBps" type="integer">Reference move in basis points that pauses the book, 10 to 10000.</ParamField>

<ParamField body="custodySetupTxs" type="object">
  Hashes of the setup transactions you signed after a `CUSTODY_SETUP_REQUIRED` refusal, keyed by the step key. Unknown keys are rejected.

  <Expandable>
    <ParamField body="agentRole" type="string">Hash of `addAgent` or `addController` on the security token.</ParamField>
    <ParamField body="security" type="string">Hash of `registerSecurity` on the custody contract.</ParamField>
    <ParamField body="quoteAsset" type="string">Hash of `setQuoteAsset` on the custody contract.</ParamField>
    <ParamField body="operator" type="string">Hash of `setOperator` on the custody contract.</ParamField>
  </Expandable>
</ParamField>

## What is read from the chain

Both tokens' decimals are read at creation and recorded on the book, so every later conversion between order book units and on-chain amounts uses the real numbers rather than a default. A token that reports unusable decimals is refused with `TOKEN_METADATA_UNAVAILABLE`.

If the RPC endpoint for your instance does not answer, the request fails with `CHAIN_UNAVAILABLE` rather than reporting the prerequisites as unmet. No book is created and nothing is changed.

## Response Fields

Returns `201` with the created book.

<ResponseField name="data" type="object">
  <Expandable>
    <ResponseField name="id" type="string">Order book ID. Use this as `orderBookId` everywhere else.</ResponseField>
    <ResponseField name="instanceId" type="string">Owning instance.</ResponseField>
    <ResponseField name="tokenAddress" type="string">Security token, lower-cased.</ResponseField>
    <ResponseField name="quoteTokenAddress" type="string">Settlement asset, lower-cased.</ResponseField>
    <ResponseField name="name" type="string">Display name.</ResponseField>
    <ResponseField name="symbol" type="string">Display symbol.</ResponseField>
    <ResponseField name="status" type="string">`ACTIVE` on creation.</ResponseField>
    <ResponseField name="tokenType" type="string">`EXTERNAL_SECURITY`.</ResponseField>
    <ResponseField name="settlementMode" type="string">`ON_CHAIN`.</ResponseField>
    <ResponseField name="custodyContract" type="string">Custody contract the book settles through.</ResponseField>
    <ResponseField name="settlementOperator" type="string">Wallet authorized to sign settlements, lower-cased.</ResponseField>
    <ResponseField name="underlyingIdentifier" type="string">Instrument identifier as recorded on chain.</ResponseField>
    <ResponseField name="baseDecimals" type="integer">Security token decimals read from chain.</ResponseField>
    <ResponseField name="quoteDecimals" type="integer">Settlement asset decimals read from chain.</ResponseField>
    <ResponseField name="minOrderSize" type="string">Minimum quantity.</ResponseField>
    <ResponseField name="maxOrderSize" type="string">Maximum quantity, or `null`.</ResponseField>
    <ResponseField name="tickSize" type="string">Price increment.</ResponseField>
    <ResponseField name="makerFeeBps" type="integer">Maker fee in basis points.</ResponseField>
    <ResponseField name="takerFeeBps" type="integer">Taker fee in basis points.</ResponseField>
    <ResponseField name="mmMakerRebateBps" type="integer">Market maker fee override, or `null`.</ResponseField>
    <ResponseField name="allowImport" type="boolean">Whether other instances may import the book.</ResponseField>
    <ResponseField name="allowSharedLiquidity" type="boolean">Whether the book pools depth.</ResponseField>
    <ResponseField name="tradingMode" type="string">`CONTINUOUS`.</ResponseField>
    <ResponseField name="priceRefEnabled" type="boolean">Whether the band is enforced.</ResponseField>
    <ResponseField name="priceRefMode" type="string">Reference source.</ResponseField>
    <ResponseField name="priceRefEnforcement" type="string">Refresh interval.</ResponseField>
    <ResponseField name="priceRefSpreadBps" type="integer">Band half-width, or `null`.</ResponseField>
    <ResponseField name="createdAt" type="string">ISO 8601 timestamp.</ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.trusset.org/orderbooks/external-securities/api/order-books" \
    -H "X-API-Key: trusset_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "tokenAddress": "0x51f2b9d0e77c4a1b83ce6d4a9271e5f3a0c8b912",
      "quoteTokenAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
      "name": "Helvetia Growth Fund",
      "symbol": "HELG",
      "underlyingIdentifier": "CH0012032048",
      "settlementOperator": "0x5ad87a0621175206b72d10e4b8577b192e7f40ab",
      "tickSize": "10000",
      "minOrderSize": "1000000",
      "makerFeeBps": 0,
      "takerFeeBps": 10,
      "allowSharedLiquidity": true
    }'
  ```

  ```typescript TypeScript theme={null}
  const payload = {
    tokenAddress,
    quoteTokenAddress,
    name: 'Helvetia Growth Fund',
    symbol: 'HELG',
    underlyingIdentifier: 'CH0012032048',
    settlementOperator: await operator.getAddress(),
    tickSize: '10000',
    minOrderSize: '1000000'
  };

  let res = await fetch(
    'https://api.trusset.org/orderbooks/external-securities/api/order-books',
    {
      method: 'POST',
      headers: {
        'X-API-Key': 'trusset_your_key_here',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    }
  );
  let body = await res.json();

  if (!body.success && body.error.code === 'CUSTODY_SETUP_REQUIRED') {
    const custodySetupTxs: Record<string, string> = {};
    for (const step of body.error.setup.steps) {
      const tx = await wallet.sendTransaction({ to: step.to, data: step.data });
      await tx.wait();
      custodySetupTxs[step.key] = tx.hash;
    }
    res = await fetch(
      'https://api.trusset.org/orderbooks/external-securities/api/order-books',
      {
        method: 'POST',
        headers: {
          'X-API-Key': 'trusset_your_key_here',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ ...payload, custodySetupTxs })
      }
    );
    body = await res.json();
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "id": "clx_ob_extsec_001",
      "instanceId": "inst_abc123",
      "tokenAddress": "0x51f2b9d0e77c4a1b83ce6d4a9271e5f3a0c8b912",
      "quoteTokenAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
      "name": "Helvetia Growth Fund",
      "symbol": "HELG",
      "status": "ACTIVE",
      "tokenType": "EXTERNAL_SECURITY",
      "settlementMode": "ON_CHAIN",
      "custodyContract": "0x489aee4ae9546081d55848f157e03192e826988c",
      "settlementOperator": "0x5ad87a0621175206b72d10e4b8577b192e7f40ab",
      "underlyingIdentifier": "CH0012032048",
      "baseDecimals": 18,
      "quoteDecimals": 6,
      "minOrderSize": "1000000",
      "maxOrderSize": null,
      "tickSize": "10000",
      "makerFeeBps": 0,
      "takerFeeBps": 10,
      "mmMakerRebateBps": null,
      "allowImport": false,
      "allowSharedLiquidity": true,
      "tradingMode": "CONTINUOUS",
      "priceRefEnabled": false,
      "priceRefMode": "MANUAL",
      "priceRefEnforcement": "EVERY_TRADE",
      "priceRefSpreadBps": null,
      "createdAt": "2025-06-15T12:00:00.000Z"
    }
  }
  ```

  ```json Error - Custody Setup Required theme={null}
  {
    "success": false,
    "data": null,
    "error": {
      "code": "CUSTODY_SETUP_REQUIRED",
      "message": "This external securities order book needs 3 on-chain transactions signed before it can be created. Sign the returned transactions with the indicated wallet and submit the request again with their transaction hashes in custodySetupTxs.",
      "setup": {
        "action": "SIGN_TRANSACTIONS",
        "custodyContract": "0x489aee4ae9546081d55848f157e03192e826988c",
        "confirmWith": { "field": "custodySetupTxs", "keyedBy": "key" },
        "steps": [
          {
            "key": "agentRole",
            "to": "0x51f2b9d0e77c4a1b83ce6d4a9271e5f3a0c8b912",
            "data": "0x...",
            "functionName": "addAgent",
            "description": "Grant the T-REX agent role on 0x51f2... to the custody contract 0x489a...",
            "requiredSigner": "0x7712db30a7b4ffd9c445793089118b60e56d752a",
            "requiredRole": null
          },
          {
            "key": "quoteAsset",
            "to": "0x489aee4ae9546081d55848f157e03192e826988c",
            "data": "0x...",
            "functionName": "setQuoteAsset",
            "description": "Authorize 0x98ad... as a settlement asset for 0x51f2...",
            "requiredSigner": "0x7712db30a7b4ffd9c445793089118b60e56d752a",
            "requiredRole": null
          },
          {
            "key": "operator",
            "to": "0x489aee4ae9546081d55848f157e03192e826988c",
            "data": "0x...",
            "functionName": "setOperator",
            "description": "Authorize 0x5ad8... as a settlement operator for 0x51f2...",
            "requiredSigner": "0x7712db30a7b4ffd9c445793089118b60e56d752a",
            "requiredRole": null
          }
        ]
      }
    }
  }
  ```

  ```json Error - Identifier Conflict theme={null}
  {
    "success": false,
    "data": null,
    "error": {
      "code": "ISIN_MISMATCH",
      "message": "This security is already registered on the custody contract under \"CH0012032048\". Use that identifier, or ask the token issuer to update it on chain."
    }
  }
  ```
</ResponseExample>

## Error Codes

| Code                         | HTTP  | Cause                                                                                                                   |
| ---------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------- |
| `VALIDATION_ERROR`           | `400` | A field failed schema validation                                                                                        |
| `INVALID_PAIR`               | `400` | Base and quote token are the same                                                                                       |
| `INVALID_CONFIG`             | `400` | `settlementOperator` is missing or malformed, a non-custody address was supplied, or a price reference field is invalid |
| `TOKEN_NO_ISIN`              | `400` | The security is unregistered and no identifier was supplied, or the registered security has no readable identifier      |
| `ISIN_MISMATCH`              | `400` | The declared identifier does not match what is registered on chain                                                      |
| `ORDER_BOOK_EXISTS`          | `409` | An `ACTIVE` or `PAUSED` book already quotes this pair                                                                   |
| `CUSTODY_SETUP_REQUIRED`     | `409` | On-chain prerequisites are outstanding. `error.setup.steps` carries the unsigned transactions                           |
| `SECURITY_INACTIVE`          | `409` | The token issuer has deactivated this security on the custody contract                                                  |
| `INVALID_CUSTODY_CONTRACT`   | `400` | The address did not answer as the external securities custody contract                                                  |
| `TOKEN_METADATA_UNAVAILABLE` | `502` | Token decimals could not be read, or are outside the usable range                                                       |
| `CHAIN_UNAVAILABLE`          | `502` | The RPC endpoint for your instance did not answer                                                                       |
| `CUSTODY_NOT_DEPLOYED`       | `503` | No custody contract is deployed on your instance's network                                                              |
| `SERVICE_NOT_ENABLED`        | `403` | The Trading service is not enabled on this instance                                                                     |
