Order lifecycle & statuses
Every order status, which ones are final, and how to track a trade to a final state
Trading calls return pending right away and finish in the background. You find out how a trade
ended by polling, or by listening for webhooks and WebSocket events. Always keep going until you
reach a terminal state.
Two levels of status
| Level | Where | Values |
|---|---|---|
| Trade | overallState on GET /execution/status | One state for the whole quote, across all its orders |
| Order | status on each order | One status per venue order |
A fill split across two venues is one trade with two orders.
Order statuses
| Status | Terminal | Meaning |
|---|---|---|
pending | No | Accepted. Funds are being checked. |
signing | No | The order is being signed. |
pending_bridge | No | Funds are moving to the venue's chain. |
submitting | No | Being sent to the venue. |
submitted | No | Sent to the venue, waiting for confirmation. |
open | No | A limit order resting on the venue's book. |
partially_filled_open | No | A resting limit order with some fills and size left. |
cancel_pending | No | Cancel requested, waiting for the venue to confirm. |
filled | Yes | Fully filled. |
partial_fill | Yes | Part filled. The rest will not fill. See partialFillReason. |
failed | Yes | Failed. GET /execution/status uses errorReason. GET /execution/orders uses errorMessage. |
expired | Yes | Timed out, or a limit order reached its expiry. |
cancelled | Yes | Cancelled by the user or the system. |
A market order never shows open, partially_filled_open, or cancel_pending.
partialFillReason is an open set. Known values: venue_capacity, price_slipped,
venue_cancelled, venue_expired, venue_closed, user_cancelled, timeout,
clearinghouse_capacity, native_remainder_voided, bridge_shortfall.
bridge_shortfall means a bridge delivered less than quoted, so the order was downsized before
submit. It replaces venue_capacity on a downsized market order that ends partial_fill. A
downsized order the venue filled in full still ends filled, with no reason.
On a market order, remainingSizeRaw is set only on partial_fill (requested shares minus filled
shares) and stays null on filled. Limit orders always carry it, 0 once complete.
stateDiagram-v2
[*] --> pending
pending --> signing
pending --> pending_bridge
pending --> submitting
signing --> submitting
pending_bridge --> submitting
submitting --> submitted
submitted --> filled
submitted --> partial_fill
submitted --> open: limit order
open --> partially_filled_open
open --> cancel_pending
partially_filled_open --> cancel_pending
open --> filled
partially_filled_open --> filled
partially_filled_open --> partial_fill
cancel_pending --> cancelled
cancel_pending --> filled
pending --> failed
submitting --> failed
submitted --> failed
pending_bridge --> expired
open --> expired
pending --> cancelled
filled --> [*]
partial_fill --> [*]
failed --> [*]
expired --> [*]
cancelled --> [*]The diagram shows the usual paths. Not every order visits every state, and a failure can happen from any in-flight state. Branch on the status you read, not on the path you expect.
Trade states
GET /execution/status rolls the orders up into overallState:
overallState | Terminal | Meaning |
|---|---|---|
created | No | Accepted, execution has not started. |
routing, quoting, placing, confirming | No | In progress. |
filled | Yes | Every order filled. |
partially_filled | Yes | At least one order filled or part filled, and at least one did not fill in full. |
cancelled | Yes | Every order was cancelled. |
expired | Yes | Every order expired. |
failed | Yes | Anything else that ended without a fill. |
Stop tracking the trade when terminal is true. It can stay false for a short time after
every order is final while remaining execution steps finish. If you requested an app-paid
referral, continue tracking that payout separately: its purpose: "referral_payout" signing
request can appear after the trade is filled. See Referrals.
Poll a quote-based trade
Use this for POST /execution/fill, and for POST /execution/orders using the quoteId in its
response.
let status = await client.getExecutionStatus({ quoteId });
// This loop tracks the trade. Track app-paid referrals separately if requested.
while (!status.terminal) {
await new Promise((r) => setTimeout(r, status.pollAfterMs ?? 1000));
status = await client.getExecutionStatus({ quoteId });
}
console.log(status.overallState, status.orders);Key fields in the response:
| Field | Meaning |
|---|---|
terminal | true once the trade is final. App-paid referral payouts can still need a signature. |
overallState | The trade state above. |
pollAfterMs | Wait this long before the next poll. null when terminal. |
orders[] | Per order: orderId, venue, status, filledAmountRaw, actualSharesRaw, executionPriceRaw, quotedPriceRaw, txHash, errorReason. |
steps[] | Progress rows for a UI. Group by groupId and show label. |
errorReason | Why the trade failed, on GET /execution/status. The order list calls this errorMessage. |
pendingSignatures[] | Not self-custody only. An app-paid referral puts purpose: "referral_payout" here after the trade is filled. See Referrals. |
filledAmountRaw and actualSharesRaw are 6-decimal integer strings: 1000000 is $1 or 1 share.
quotedPriceRaw is a 0–1 decimal string such as "0.53", not a 6-decimal integer. executionPriceRaw is the effective filled USD per received/sold share, including fee effects already reflected in those quantities; it is omitted without usable fill quantities and never falls back to the quote.
limitPriceRaw is the price-scaled integer: 530000 is 0.53. sizeRaw (listed as limitSizeRaw on GET /execution/orders) uses the share scale: 1000000 is 1 share. Do not read 530000 as dollars.
404 means no order is recorded for that quoteId for this user yet. Right after a fill request,
the request may still be running, so a 404 is not final and should be retried. Do not get a new
quote until Errors, retries & idempotency says to.
API reference: Get execution status.
Poll by order id or your own id
GET /execution/orders lists the user's orders. Filter it to one order:
| Filter | Use for |
|---|---|
orderId | Limit orders, cancels, and any order you hold the id of |
externalId | A direct order after a timeout, when you never saw the response |
quoteId | Every leg of a quote |
status, orderType, venueMarketIds | Lists and reconciliation |
const orders = await client.getExecutionOrders({ orderId });Each row has status, filledAmountRaw, actualSharesRaw, executionPrice (not
executionPriceRaw), txHash, and errorMessage. executionPrice is a 0–1 decimal string such
as "0.53", equal to filledAmountRaw / actualSharesRaw. It includes a venue fee only where filledAmountRaw
does: a sell's proceeds are net of fee, while most venues report a buy's fee separately in
actualVenueFeeRaw. On venues that fold a buy's fee into its cost, a buy near 1 can read slightly
above 1. On Hyperliquid it is the quote until the realized fill is recorded, shortly after filled. filledAmountRaw and actualSharesRaw stay 6-decimal integer strings (1000000 is
$1 or 1 share).
On a Polymarket market order, filledAmountRaw on a buy is the collateral the order paid, before
the venue fee in actualVenueFeeRaw. txHash is the Polygon transaction that settled the fill.
Both are read from the fill's on-chain OrderFilled log, usually within a minute of filled.
Until then, txHash holds the Polymarket order hash. A limit order keeps the order hash, since it
can settle across several transactions.
Poll until status is terminal. A resting limit order stays open until it fills,
is cancelled, or expires.
Push instead of polling
| Channel | Events | Notes |
|---|---|---|
| Webhooks | trades.placed, trades.filled | Server-to-server. trades.filled carries status filled or partial_fill, and your externalId for direct orders. There is no webhook for a failed trade, so poll to catch failures. |
| WebSocket | order_event | For a signed-in user in the browser. Use REST to backfill on page load and reconnect. |
Payloads are small. Read the order from the API for amounts and prices.
Retries and idempotency: Errors, retries & idempotency.