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

# Place an order

> CreateVendorOrder end to end: the one time-in-force it accepts, four worked order shapes, and the taker coefficient you always pay.

Place a single fill-or-kill limit order for one of your participants with `CreateVendorOrder`.

<Info>
  Before this page:

  * a token and the order-write grant on your client ([Authentication](/authentication));
  * the participant's `participantId`, taken from the `kyc.approved` webhook
    ([Firms, participants and accounts](/identity));
  * the participant's `provisionedAccount`, the account you captured at onboarding
    ([External IDs, participants and accounts](/kyc#external-ids-participants-and-accounts)) — it
    goes in `order.account`;
  * the participant account funded, because every order is prefunded
    ([Move cash](/funding#move-cash));
  * the instrument's two scales and the fee formula
    ([Money on the wire](/instruments#money-on-the-wire)).
</Info>

<Snippet file="beta.mdx" />

### It is fill-or-kill limit orders, and nothing else

`CreateVendorOrder` accepts **limit orders with `TIME_IN_FORCE_FILL_OR_KILL` and nothing else.** Any
other time-in-force is rejected outright with

```
InvalidArgument: order.time_in_force must be TIME_IN_FORCE_FILL_OR_KILL
```

It is not silently downgraded. There is no market order, no day order, no GTC and no GTD on this
surface.

The reason: each order is prefunded with an exact atomic transfer, so the maximum cost has to be
known before the order reaches the book.

The consequence is commercial, not just technical — you are structurally always the taker. Read
[You are always the taker](#you-are-always-the-taker) before you price your product.

### Three request facts that are not in the reflected schema

**`order.clord_id` is required by the runtime.** `PreviewOrder` ignores it and `CreateVendorOrder`
requires it. Reflection advertised the older schema without it for a period, so a client generated
from that build compiles, sends no `clord_id`, and fails at runtime. Send it on every order and keep
it unique per order — it is the identifier you will match execution reports on.

**The participant is named only by `order.account`.** There is no separate customer-account field on
this request, and `x-participant-id` is **not used** here. The header is required on account-scoped
reads such as positions and reports, and it does nothing on this RPC.

The value you put in `order.account` is the `provisionedAccount` you captured at onboarding — see
[External IDs, participants and accounts](/kyc#external-ids-participants-and-accounts). It is opaque:
copy it verbatim and never build it.

<Note>
  **\[VERIFY] — which call returns `provisionedAccount` is not published.** The `kyc.approved` webhook
  is documented as carrying `participantId`, and nothing states where `provisionedAccount` comes back.

  Record whatever account string we hand you at onboarding against your user row, and confirm the
  source call with your integration lead.
</Note>

**`funding_request_ids` are values we return, not values you send.** The dedupe key is
`idempotency_key`. See [Recover a lost order](/outcomes#recover-a-lost-order).

<Snippet file="participant-id-warning.mdx" />

### The call

<Note>
  **\[VERIFY] — five things in the snippets below are not published, and one of them sits inside a
  runnable command.** Only `order.account`, `order.clord_id`, `order.time_in_force`, `order.symbol`,
  `order_qty`, `cash_order_qty`, `idempotency_key`, `funding_request_ids` and `failure_reason` are
  confirmed. Unconfirmed, and to be checked against your own generated code and proto copy before you
  send anything:

  * the **fully-qualified service name** `polymarket.v1.VendorOrderAPI/CreateVendorOrder`, which the
    `grpcurl` tab passes as the method argument;
  * the request, order and enum **type** names, and the generated stub names;
  * the **field name carrying the limit price**, shown below as `order.price`;
  * the **side enum values**, shown below as `SIDE_BUY` and `SIDE_SELL`;
  * your own `protoc` package layout — the bundle is unversioned, with no checksum or changelog.
</Note>

<CodeGroup>
  ```python Python theme={null}
  # Places one fill-or-kill limit order and branches on the terminal status.
  # PolymarketClient is the canonical auth client published on /connect#authentication.
  import uuid
  from decimal import Decimal

  from polymarket_auth import PolymarketClient

  # Generated from polymarket-protos.zip. VERIFY your own package path.
  from polymarket.v1 import order_pb2, order_pb2_grpc

  # The gRPC target is GRPC_TARGET in polymarket_auth.py — one place, both environments.
  pmx = PolymarketClient()
  # provisionedAccount — the account you captured at onboarding. Opaque; never construct it.
  ACCOUNT = "firms/20260821-examplefirminc-api-participant/accounts/..."
  SYMBOL = "aec-cfb-clmsn-lsu-2026-09-05"          # from ListInstruments, /instruments#find-instruments

  # Read both scales off THIS instrument. Never hard-code them. (/instruments#money-on-the-wire)
  PRICE_SCALE = Decimal(100)   # instrument.priceScale
  QTY_SCALE = Decimal(100)     # instrument.fractionalQtyScale

  LIMIT_PRICE = Decimal("0.32")   # decimal YES price
  CONTRACTS = Decimal("5")        # decimal contracts

  wire_price = int(LIMIT_PRICE * PRICE_SCALE)   # 32   = 0.32 x priceScale 100
  wire_qty = int(CONTRACTS * QTY_SCALE)         # 500  = 5    x fractionalQtyScale 100

  # Persist both of these against your own order row BEFORE you send. Recovery needs them.
  CLORD_ID = f"examplefirm-{uuid.uuid4().hex[:12]}"
  IDEMPOTENCY_KEY = str(uuid.uuid4())

  req = order_pb2.CreateVendorOrderRequest(
      idempotency_key=IDEMPOTENCY_KEY,
      order=order_pb2.VendorOrder(
          account=ACCOUNT,        # the ONLY place the participant is named
          clord_id=CLORD_ID,      # required by the runtime, ignored by PreviewOrder
          symbol=SYMBOL,
          time_in_force=order_pb2.TimeInForce.Value("TIME_IN_FORCE_FILL_OR_KILL"),
          side=order_pb2.Side.Value("SIDE_BUY"),
          price=wire_price,
          order_qty=wire_qty,
      ),
  )

  with pmx.channel() as ch:
      # x-participant-id is NOT sent on this RPC: pass no participant ID.
      resp = order_pb2_grpc.VendorOrderAPIStub(ch).CreateVendorOrder(req, metadata=pmx.metadata())

  status = order_pb2.VendorOrderStatus.Name(resp.status)

  if status == "PENDING":
      # Indeterminate, NOT queued. Resubmit the byte-identical body with the SAME idempotency_key.
      raise RuntimeError(f"PENDING {IDEMPOTENCY_KEY}: see /outcomes#recover-a-lost-order")
  if status == "REJECTED":
      # Arrives as gRPC OK. failure_reason carries exchange-authored text only.
      raise RuntimeError(f"REJECTED: {resp.failure_reason}")

  # ACCEPTED includes an order cancelled under fill-or-kill. It does NOT mean filled.
  print(f"ACCEPTED clord_id={CLORD_ID} funding_request_ids={list(resp.funding_request_ids)}")
  print("Wait for the drop-copy execution report before you credit anything: /outcomes#order-outcomes")
  ```

  ```typescript TypeScript theme={null}
  // Places one fill-or-kill limit order and branches on the terminal status.
  // PolymarketClient is the canonical auth client published on /connect#authentication.
  import { randomUUID } from "node:crypto";
  import { PolymarketClient } from "./polymarketAuth";

  // Generated from polymarket-protos.zip. VERIFY your own package path.
  import {
    VendorOrderAPIClient,
    TimeInForce,
    Side,
    VendorOrderStatus,
  } from "./gen/polymarket/v1/order";

  // The gRPC target is GRPC_TARGET in polymarketAuth.ts — one place, both environments.
  const pmx = new PolymarketClient();
  // provisionedAccount — the account you captured at onboarding. Opaque; never construct it.
  const ACCOUNT = "firms/20260821-examplefirminc-api-participant/accounts/...";
  const SYMBOL = "aec-cfb-clmsn-lsu-2026-09-05"; // from ListInstruments, /instruments#find-instruments

  // Read both scales off THIS instrument. Never hard-code them. (/instruments#money-on-the-wire)
  const PRICE_SCALE = 100n; // instrument.priceScale
  const QTY_SCALE = 100n;   // instrument.fractionalQtyScale

  // Decimal inputs held as integer cents / hundredths of a contract, so no float touches money.
  const LIMIT_PRICE_CENTS = 32n; // $0.32
  const CONTRACTS_HUNDREDTHS = 500n; // 5 contracts

  const wirePrice = (LIMIT_PRICE_CENTS * PRICE_SCALE) / 100n; // 32  = 0.32 x priceScale 100
  const wireQty = (CONTRACTS_HUNDREDTHS * QTY_SCALE) / 100n;  // 500 = 5    x fractionalQtyScale 100

  // Persist both of these against your own order row BEFORE you send. Recovery needs them.
  const CLORD_ID = `examplefirm-${randomUUID().replace(/-/g, "").slice(0, 12)}`;
  const IDEMPOTENCY_KEY = randomUUID();

  const client = new VendorOrderAPIClient(pmx.channel());

  // x-participant-id is NOT sent on this RPC: pass no participant ID.
  const resp = await client.createVendorOrder(
    {
      idempotencyKey: IDEMPOTENCY_KEY,
      order: {
        account: ACCOUNT,       // the ONLY place the participant is named
        clordId: CLORD_ID,      // required by the runtime, ignored by PreviewOrder
        symbol: SYMBOL,
        timeInForce: TimeInForce.TIME_IN_FORCE_FILL_OR_KILL,
        side: Side.SIDE_BUY,
        price: wirePrice.toString(),   // int64 travels as a string
        orderQty: wireQty.toString(),
      },
    },
    await pmx.metadata(),
  );

  const status = VendorOrderStatus[resp.status];

  if (status === "PENDING") {
    // Indeterminate, NOT queued. Resubmit the byte-identical body with the SAME idempotencyKey.
    throw new Error(`PENDING ${IDEMPOTENCY_KEY}: see /outcomes#recover-a-lost-order`);
  }
  if (status === "REJECTED") {
    // Arrives as gRPC OK. failureReason carries exchange-authored text only.
    throw new Error(`REJECTED: ${resp.failureReason}`);
  }

  // ACCEPTED includes an order cancelled under fill-or-kill. It does NOT mean filled.
  console.log(`ACCEPTED clord_id=${CLORD_ID} funding_request_ids=${resp.fundingRequestIds}`);
  console.log("Wait for the drop-copy execution report before crediting: /outcomes#order-outcomes");
  ```

  ```bash grpcurl theme={null}
  #!/usr/bin/env bash
  # curl cannot speak gRPC, and CreateVendorOrder has no REST equivalent. Use grpcurl.
  # Server reflection is entitlement-gated (PermissionDenied: method not permitted), so pass protos.
  set -euo pipefail

  TOKEN="$(./token.sh)"   # token.sh is published on /connect#authentication; stdout is the token
  # provisionedAccount — the account you captured at onboarding. Opaque; never construct it.
  ACCOUNT="firms/20260821-examplefirminc-api-participant/accounts/..."
  SYMBOL="aec-cfb-clmsn-lsu-2026-09-05"

  # Read both scales off THIS instrument. Never hard-code them. (/instruments#money-on-the-wire)
  PRICE_SCALE=100   # instrument.priceScale
  QTY_SCALE=100     # instrument.fractionalQtyScale

  WIRE_PRICE=$(( 32 * PRICE_SCALE / 100 ))    # 32  = $0.32 x priceScale 100
  WIRE_QTY=$(( 500 * QTY_SCALE / 100 ))       # 500 = 5 contracts x fractionalQtyScale 100

  # Persist both of these against your own order row BEFORE you send. Recovery needs them.
  CLORD_ID="examplefirm-$(uuidgen | tr -d '-' | cut -c1-12)"
  IDEMPOTENCY_KEY="$(uuidgen)"

  # NOTE: no x-participant-id header on this RPC.
  RESP=$(grpcurl \
    -import-path ./polymarket-protos \
    -proto polymarket/v1/order.proto \
    -H "authorization: Bearer ${TOKEN}" \
    -d "$(cat <<JSON
  {
    "idempotency_key": "${IDEMPOTENCY_KEY}",
    "order": {
      "account": "${ACCOUNT}",
      "clord_id": "${CLORD_ID}",
      "symbol": "${SYMBOL}",
      "time_in_force": "TIME_IN_FORCE_FILL_OR_KILL",
      "side": "SIDE_BUY",
      "price": "${WIRE_PRICE}",
      "order_qty": "${WIRE_QTY}"
    }
  }
  JSON
  )" \
    grpc-api.preprod.polymarketexchange.com:443 \
    polymarket.v1.VendorOrderAPI/CreateVendorOrder) || { echo "transport failure" >&2; exit 1; }
    # ^ service name unconfirmed — see the [VERIFY] note above this code group

  echo "$RESP"

  STATUS=$(echo "$RESP" | jq -r '.status')
  case "$STATUS" in
    PENDING)  echo "PENDING ${IDEMPOTENCY_KEY}: indeterminate, see /outcomes#recover-a-lost-order" >&2; exit 2 ;;
    REJECTED) echo "REJECTED: $(echo "$RESP" | jq -r '.failure_reason')" >&2; exit 3 ;;
    ACCEPTED) echo "ACCEPTED — not necessarily filled. See /outcomes#order-outcomes" ;;
    *)        echo "unknown status ${STATUS}" >&2; exit 4 ;;
  esac
  ```

  ```go Go theme={null}
  // Places one fill-or-kill limit order and branches on the terminal status.
  package main

  import (
  	"context"
  	"fmt"
  	"log"
  	"strings"

  	"github.com/google/uuid"
  	"google.golang.org/grpc/metadata"

  	// Canonical auth client from /connect#authentication. The gRPC target is
  	// pmauth.GRPCTarget — one place, both environments.
  	pmauth "example.com/yourfirm/pmauth"

  	// Generated from polymarket-protos.zip. VERIFY your own package path.
  	orderv1 "github.com/yourfirm/gen/polymarket/v1"
  )

  const (
  	// provisionedAccount — the account you captured at onboarding. Never construct it.
  	account = "firms/20260821-examplefirminc-api-participant/accounts/..."
  	symbol  = "aec-cfb-clmsn-lsu-2026-09-05" // from ListInstruments, /instruments#find-instruments

  	// Read both scales off THIS instrument. Never hard-code them. (/instruments#money-on-the-wire)
  	priceScale = int64(100) // instrument.priceScale
  	qtyScale   = int64(100) // instrument.fractionalQtyScale
  )

  func main() {
  	ctx := context.Background()
  	pm := pmauth.New()

  	wirePrice := 32 * priceScale / 100  // 32  = $0.32 x priceScale 100
  	wireQty := 500 * qtyScale / 100     // 500 = 5 contracts x fractionalQtyScale 100

  	// Persist both of these against your own order row BEFORE you send. Recovery needs them.
  	clordID := "examplefirm-" + strings.ReplaceAll(uuid.NewString(), "-", "")[:12]
  	idempotencyKey := uuid.NewString()

  	conn, err := pm.Channel()
  	if err != nil {
  		log.Fatalf("dial: %v", err)
  	}
  	defer conn.Close()

  	// x-participant-id is NOT sent on this RPC: pass "" for the participant ID.
  	md, err := pm.Metadata("")
  	if err != nil {
  		log.Fatalf("metadata: %v", err)
  	}

  	resp, err := orderv1.NewVendorOrderAPIClient(conn).CreateVendorOrder(
  		metadata.NewOutgoingContext(ctx, md),
  		&orderv1.CreateVendorOrderRequest{
  			IdempotencyKey: idempotencyKey,
  			Order: &orderv1.VendorOrder{
  				Account:     account, // the ONLY place the participant is named
  				ClordId:     clordID, // required by the runtime, ignored by PreviewOrder
  				Symbol:      symbol,
  				TimeInForce: orderv1.TimeInForce_TIME_IN_FORCE_FILL_OR_KILL,
  				Side:        orderv1.Side_SIDE_BUY,
  				Price:       wirePrice,
  				OrderQty:    wireQty,
  			},
  		},
  	)
  	if err != nil {
  		log.Fatalf("transport failure, order state unknown, key=%s: %v", idempotencyKey, err)
  	}

  	switch resp.GetStatus().String() {
  	case "PENDING":
  		// Indeterminate, NOT queued. Resubmit the byte-identical body with the SAME key.
  		log.Fatalf("PENDING %s: see /outcomes#recover-a-lost-order", idempotencyKey)
  	case "REJECTED":
  		// Arrives as gRPC OK. failure_reason carries exchange-authored text only.
  		log.Fatalf("REJECTED: %s", resp.GetFailureReason())
  	}

  	// ACCEPTED includes an order cancelled under fill-or-kill. It does NOT mean filled.
  	fmt.Printf("ACCEPTED clord_id=%s funding_request_ids=%v\n", clordID, resp.GetFundingRequestIds())
  	fmt.Println("Wait for the drop-copy execution report before crediting: /outcomes#order-outcomes")
  }
  ```
</CodeGroup>

The response, with the fields you branch on:

```json theme={null}
{
  "status": "ACCEPTED",
  "funding_request_ids": ["..."],
  "failure_reason": ""
}
```

`status` is the only field that decides what you do next, and `ACCEPTED` is not a fill. All three
values are specified under [Order outcomes](/outcomes#order-outcomes).

### Four worked order shapes

All four use `priceScale: 100` and `fractionalQtyScale: 100` and a YES price of \*\*$0.32**. Winning
contracts settle at $1.00 and losing contracts at \$0.00.

The **\[VERIFY]** cells below are the field name carrying the limit price and the two side enum
values. The scaling, the quantities and the money are confirmed; those three spellings are not.

<Warning>
  **The fee figures below are illustrative, not current.** They are computed at a taker Θ of 0.06.
  The taker coefficient changed on 2026-09-14 and every partner had to hand-edit their systems,
  because there is no fee-schedule endpoint to read it from. **VERIFY** — confirm the coefficient in
  force with your integration lead before you invoice anyone from these numbers, and derive the fee
  from the formula on [Money on the wire](/instruments#money-on-the-wire), never from a figure in a
  table.
</Warning>

#### 1. Buy YES — quantity-denominated

| Field                 | Wire value               | Decimal     |
| --------------------- | ------------------------ | ----------- |
| `side` **\[VERIFY]**  | `SIDE_BUY` **\[VERIFY]** | buy YES     |
| `price` **\[VERIFY]** | `32`                     | \$0.32      |
| `order_qty`           | `500`                    | 5 contracts |

Maximum cost \*\*$1.60** (5 × $0.32), which is what gets prefunded. Maximum return \*\*$5.00** if YES
settles at $1.00. Fee on a full fill: `0.06 × 5 × 0.32 × 0.68 = $0.0653` → **\$0.07** after banker's
rounding, which at that coefficient arrives as `commission_notional_collected: "700"`
(700 / 10,000). At a different coefficient both the cent figure and the wire figure change — recompute,
do not copy.

#### 2. Take the NO side — quantity-denominated

| Field                 | Wire value                | Decimal          |
| --------------------- | ------------------------- | ---------------- |
| `side` **\[VERIFY]**  | `SIDE_SELL` **\[VERIFY]** | take the NO side |
| `price` **\[VERIFY]** | `32`                      | \$0.32 YES price |
| `order_qty`           | `500`                     | 5 contracts      |

A `SELL` does not pay you cash. It **spends complementary NO collateral**, and the resulting short
carries an obligation of \*\*up to $1.00 per contract** — so 5 contracts is a $5.00 maximum obligation.
Maximum return \*\*$5.00** if YES settles at $0.00.

<Warning>
  **how much collateral a quantity-denominated `SELL` locks is not settled.** Whether an
  open short carries a standing collateral requirement, and where that requirement is exposed, is
  unanswered: `balance_reservation` and `margin_requirement` came back **empty** on the balance-ledger
  entries for a real open short.

  Until it is resolved, hold back \$1.00 per short contract in **your own** ledger and do not let a
  user withdraw funds backing an open short on the strength of a balance field. A partner following
  the balance fields alone could release collateral that is still obligated. The withdrawal rule
  built on this reserve is on
  [Which number is spendable](/balances#which-number-is-spendable).
</Warning>

#### 3. Cash-denominated buy

| Field                 | Wire value               | Decimal     |
| --------------------- | ------------------------ | ----------- |
| `side` **\[VERIFY]**  | `SIDE_BUY` **\[VERIFY]** | buy YES     |
| `price` **\[VERIFY]** | `32`                     | \$0.32      |
| `cash_order_qty`      | `2000`                   | **\$20.00** |

`cash_order_qty` is scaled by **`priceScale`**, so `2000` at `priceScale: 100` is $20.00 — not $2,000
and not $0.20. Maximum cost is $20.00 exactly, which is the point of denominating in cash: the
prefund equals the field. At $0.32 that is up to 62.5 contracts, returning up to $62.50 if YES settles
at \$1.00.

Send `order_qty` or `cash_order_qty`, not both.

#### 4. Cash-denominated sell

| Field                 | Wire value                | Decimal                                |
| --------------------- | ------------------------- | -------------------------------------- |
| `side` **\[VERIFY]**  | `SIDE_SELL` **\[VERIFY]** | take the NO side                       |
| `price` **\[VERIFY]** | `32`                      | \$0.32 YES price                       |
| `cash_order_qty`      | `2000`                    | \*\*spend $20.00**, not receive $20.00 |

This is the shape partners get backwards. On a `SELL`, `cash_order_qty = 2000` means \*\*spend $20.00 of
complementary NO collateral**. You are not being paid $20.00; you are committing \$20.00. A partner who
reads it the other way debits the user's balance and credits it again, and their ledger diverges by
twice the order size on every short.

<Warning>
  **Not yet published.**&#x20;
  We have not published how the exchange converts a `cash_order_qty` into a contract quantity, or how
  it rounds to the quantity increment. Do not compute the resulting contract count yourself for a
  customer-facing number — take it from the execution report.
</Warning>

### Rate limits on this call

<Warning>
  **Not yet published.**&#x20;
  `CreateVendorOrder` has **no published rate limit at any scope.** The general institutional REST
  limit is 100 req/sec firm-wide on a one-minute average, and there is a **5-second latency stopgap on
  orders**, but neither is stated as this RPC's budget. Get the number and its scope from your
  integration lead before you size a burst.
</Warning>

When you do exceed a limit, `Global Rate Limit Exceeded` arrives as an **execution-report rejection**,
not as an HTTP or gRPC error — so an order-placement path that only inspects the RPC result will
record it as accepted. See [Order outcomes](/outcomes#order-outcomes).

<Info>
  **API reference:** <a href="https://docs.polymarket.us/institutional/trading/overview" target="_blank" rel="noreferrer">Trading</a>. Opens on the public documentation site in a new tab.
  Where it disagrees with this page, this page is authoritative for the partner surface.
</Info>

## You are always the taker

<Note>
  **If you are an IB, this differs.**
  State two things explicitly to a regulatory reviewer, because neither is reachable here.
  **There is no modify or replace on the partner surface at all**, so an order-handling procedure
  you file cannot include one. And **day orders cancel at the traded-day roll, with GTD as the
  alternative** — neither applies on a fill-or-kill-only surface.
</Note>

Price your product against the one fee coefficient you can actually be charged.

### The structural consequence of fill-or-kill

`CreateVendorOrder` accepts `TIME_IN_FORCE_FILL_OR_KILL` and nothing else, so your orders never rest.
An order that never rests can never be the passive side of a trade. **You can only consume resting
depth, and you can never earn the maker rebate.**

This is a property of the order surface, not a tier you can be upgraded into or a volume threshold you
can trade through. No configuration changes it.

### Both coefficients, and the one you pay

```
Fee = Θ × C × p × (1 − p)
```

rounded to the cent with banker's rounding, where `C` is the de-scaled contract count and `p` is the
de-scaled YES price.

| Role  | Θ                               | Reachable from `CreateVendorOrder`?                                 |
| ----- | ------------------------------- | ------------------------------------------------------------------- |
| Taker | `0.06`                          | **Yes. This is the only rate you can ever pay.**                    |
| Maker | `−0.0125` (paid *to* the maker) | **No.** Your orders never rest, so they are never the passive side. |

Θ is a coefficient, not a percentage of principal. `p × (1 − p)` peaks at `p = 0.50`, so the fee is
largest mid-book and smallest at the extremes:

| YES price `p` | Taker fee per contract | As a share of what you pay for the contract |
| ------------- | ---------------------- | ------------------------------------------- |
| \$0.50        | \$0.015                | 3.00%                                       |
| \$0.32        | \$0.013056             | 4.08%                                       |
| \$0.98        | \$0.001176             | 0.12%                                       |

Two numbers worth carrying into a pricing conversation:

* Your worst case is **\$0.015 per contract**, at `p = 0.50`.
* A market maker on the other side of that same trade *receives* $0.003125 per contract, so the
  per-contract gap between your economics and theirs is **$0.018125\*\* at `p = 0.50` — the full
  `0.06 − (−0.0125) = 0.0725` spread, scaled by `p × (1 − p)`.

Price the taker coefficient into your own fee to the end user. A model built on a blended maker/taker
rate does not apply to this surface, and there is no path to the maker side later.

<Warning>
  **The taker coefficient changed from `0.06` to `0.0695` on 2026-09-14, and \[GAP] there is no
  fee-schedule endpoint.** Every partner had to hand-edit their systems, because Θ cannot be read from
  the API at all — not on the instrument, not on the execution report, not anywhere.

  Confirm today's value with your integration lead before you commit to a published price, keep Θ in
  exactly one place in your code, and reconcile every fill's `commission_notional_collected` against
  your own expectation so a coefficient change surfaces on the first fill rather than in a monthly
  report.
</Warning>

### Why a thin preprod book blocks you and not a market maker

Taker-only order entry and a shallow book combine badly. A market maker posts resting depth, so it can
trade in preprod regardless of how thin the book is. You can only cross depth somebody else posted, so
**when the book is empty you cannot transact at all.** The same environment is fully functional for
one partner type and completely blocked for yours.

The numbers behind that:

* `pmsim` is the live liquidity provider, and symbol coverage is thin and uneven: `astatc-` about
  **48%**, `aec-` about **3.7%**, `aec-atp` about **0.1%**. A second liquidity source is configured but not
  running.
* Books are shallow and intermittent — "straight 0 for a few minutes" is normal, not an incident.
* Of **121 open ATP instruments**, only a small minority showed any resting depth, and that depth sat
  at the edges: a **$0.98 offer** and a **$0.06 bid**. A fill-or-kill order at a realistic price
  crosses nothing.
* **Preprod markets do not resolve.** Instruments reach `INSTRUMENT_STATE_EXPIRED` in large batches —
  **363, 455, 544 and 549 in a single session** — with none resolving. One partner asked six times
  over 40 days to be able to test a settlement flow and never could.

So: expect `EXPIRED` on nearly every preprod order, and treat a preprod fill as a lucky event rather
than a test you can rely on repeating. `EXPIRED` is not a defect —
[Order outcomes](/outcomes#order-outcomes) explains why it carries no reason.

The only known way to manufacture a preprod fill contradicts the UAT attestation you are asked to
sign. That contradiction is on [Cancelling](/outcomes#cancelling); escalate it rather than choosing a side.

### Will a resting time-in-force be available?

<Warning>
  **Not yet published.**&#x20;
  Whether a resting time-in-force — and therefore maker eligibility — is on the roadmap for the
  partner surface is not published, and no timeframe has been committed. Ask your integration lead
  before you build a pricing model that assumes it arrives, and do not represent a maker rebate to
  your own customers as forthcoming.
</Warning>

## What can go wrong

| Where                    | Symptom                                                                   | Cause                                                                                                          | What you do                                                                                                                     |
| ------------------------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Placing an order         | `InvalidArgument: order.time_in_force must be TIME_IN_FORCE_FILL_OR_KILL` | Any other time-in-force                                                                                        | Send `TIME_IN_FORCE_FILL_OR_KILL`. It is the only accepted value.                                                               |
| Placing an order         | Order fails at runtime although your generated client compiled            | `order.clord_id` missing — reflection advertised a schema without it                                           | Always send `clord_id`, unique per order.                                                                                       |
| Placing an order         | `PERMISSION_DENIED`                                                       | The order-write grant is missing, or the token predates the grant                                              | Ask for the grant, then re-mint the token. Scopes are server-side.                                                              |
| Placing an order         | `CROSS_ISV_PARTICIPANT_IMPERSONATION_ATTEMPT`                             | `order.account` built from the firm returned by `GET /v1/whoami`                                               | That is your clearing-member firm. Use the `provisionedAccount` we gave you; never construct it.                                |
| Placing an order         | `NOT_FOUND: customer relationship claim failed`                           | A shared test SSN across users, an incomplete server-side setup step, or a half-provisioned firm               | There is no claim or link RPC — the relationship is established server-side by KYC. Send us the participant ID and the account. |
| Placing an order         | Order rejected for funds although the balance looks sufficient            | Every order is prefunded with an exact atomic transfer; free cash is net of collateral and accrued vendor fees | Check `GetFundingAccountBalance` and see [Move cash](/funding#move-cash).                                                       |
| Placing an order         | `status: PENDING`                                                         | Indeterminate, not queued — we could not confirm a terminal outcome                                            | Resubmit the byte-identical body with the **same** `idempotency_key`: [Recover a lost order](/outcomes#recover-a-lost-order).   |
| Placing an order         | `status: ACCEPTED` and no fill ever arrives                               | `ACCEPTED` includes an order cancelled under fill-or-kill                                                      | Expected. Read the `EXPIRED` section of [Order outcomes](/outcomes#order-outcomes).                                             |
| Placing an order         | `ALREADY_EXISTS` on a retry                                               | The body differed from the first submission under the same key                                                 | That is the guard working. Resend the original bytes, unmodified.                                                               |
| Placing an order         | Order is 100× the intended size                                           | `cash_order_qty` scaled by both scales instead of `priceScale` only                                            | `cash_order_qty` uses `priceScale`.                                                                                             |
| Placing an order         | Your ledger is off by 2× on every short                                   | Reading a `SELL` `cash_order_qty` as money received                                                            | It is money **spent** as complementary NO collateral.                                                                           |
| Placing an order         | Your fill economics are 100× off                                          | Raw `order_qty` used as a contract count                                                                       | Divide by `fractionalQtyScale`: [Money on the wire](/instruments#money-on-the-wire).                                            |
| You are always the taker | Your unit economics are short by roughly 1.8¢ per contract                | Modelling a blended maker/taker rate                                                                           | You pay the taker coefficient on every fill. There is no maker side available to you.                                           |
| You are always the taker | Fees are roughly 16% higher than your model                               | Θ moved from `0.06` to `0.0695` on 2026-09-14                                                                  | Confirm the live coefficient; there is no fee-schedule endpoint to read it from.                                                |
| You are always the taker | Nearly every preprod order returns `EXPIRED`                              | No resting depth at your limit price                                                                           | Environment, not code. `aec-atp` coverage is about 0.1%.                                                                        |
| You are always the taker | You cannot test a settlement flow at all                                  | Preprod markets do not resolve; instruments expire in batches                                                  | Raise it with your integration lead — one partner asked six times over 40 days.                                                 |
| You are always the taker | Your market-maker counterpart trades fine in the same environment         | They post resting depth; you can only cross it                                                                 | Expected asymmetry. FOK-only order entry makes book depth a hard dependency for you.                                            |
| You are always the taker | Your fee per contract looks wrong at the extremes                         | `p × (1 − p)` peaks at `p = 0.50`                                                                              | The fee is largest mid-book. \$0.015 per contract is your worst case.                                                           |

<Snippet file="support.mdx" />

## Next

[Outcomes and recovery](/outcomes)
