OpenPoly logo
Operator Api

Trades

Query Mini App trade and order records.

Trades

List Mini App order and trade records.

Path

GET /api/operator/trades

Required scope: trades:read

Query params

All list query parameters are optional.

ParameterTypeRequirementValues/default
statusstringOptionalOne of the order statuses below.
external_user_idstringOptionalExact operator-side user ID.
market_idpositive integerOptionalExact market ID. The current handler applies base-10 parseInt before validation, so integrations should send canonical decimal digits only.
created_fromdatetimeOptionalInclusive order creation-time lower bound.
created_todatetimeOptionalInclusive order creation-time upper bound.
sortstringOptionalcreated_at_desc (default) or updated_at_desc.
pagepositive integerOptionalDefault 1, maximum 10,000.
page_sizepositive integerOptionalDefault 50, maximum 100.

Invalid filters return 400 INVALID_STATUS, 400 INVALID_MARKET_ID, 400 INVALID_CREATED_FROM, 400 INVALID_CREATED_TO, or 400 INVALID_SORT. Missing page/page_size values, or values that do not parse to a positive base-10 integer, fall back to their defaults; values above their maxima are capped.

Status values

  • queued
  • processing
  • operator_debit_pending
  • operator_debited
  • operator_credit_pending
  • processed
  • failed
  • compensation_pending
  • compensated
  • manual_review

List response

Content type: application/json.

The examples below are illustrative. Field names, nesting, JSON types, nullability, and enum values match the handler contract.

type OrderStatus =
  | 'queued'
  | 'processing'
  | 'operator_debit_pending'
  | 'operator_debited'
  | 'operator_credit_pending'
  | 'processed'
  | 'failed'
  | 'compensation_pending'
  | 'compensated'
  | 'manual_review'

type TradeSide = 'buy' | 'sell'
type TradeOutcome = 'yes' | 'no'
type MarketStatus = 'open' | 'halted' | 'in_review' | 'resolved' | 'delisted'

interface Pagination {
  page: number
  page_size: number
  total: number
  total_pages: number
}

interface FeeBreakdown {
  fxFeeBps: number
  operatorFeeBps: number
  totalFeeBps: number
}

type JsonValue =
  | null
  | boolean
  | number
  | string
  | JsonValue[]
  | { [key: string]: JsonValue }

interface TradeListOperatorUser {
  id: number
  external_user_id: string
  display_name: string | null
}

interface TradeMarketSummary {
  id: number
  slug: string
  title: string
  status: MarketStatus
}

interface TradeListItem {
  id: number
  idempotency_key: string
  status: OrderStatus
  side: TradeSide
  outcome: TradeOutcome
  requested_external_amount_minor: number | null
  requested_shares_micro: number | null
  quoted_credits_equivalent: number | null
  quoted_external_amount_minor: number | null
  executed_trade_id: number | null
  executed_credits_delta: number | null
  executed_shares_delta: number | null
  executed_external_amount_minor: number | null
  fx_rate_snapshot_id: number | null
  fee_breakdown: JsonValue
  fee_credits_total: number
  operator_fee_bps: number
  operator_fee_credits: number
  gross_external_amount_minor: number
  fee_external_amount_minor: number
  net_external_amount_minor: number
  operator_fee_external_amount_minor: number
  currency_code: string
  failure_reason: string | null
  created_at: string
  updated_at: string
  processed_at: string | null
  operator_user: TradeListOperatorUser
  market: TradeMarketSummary
}

interface TradeListSummary {
  fee_credits_total: number
  operator_fee_bps: number
  operator_fee_credits_total: number
  fee_external_amount_minor_total: number
  operator_fee_external_amount_minor_total: number
  currency_code: string
}

interface TradeListResponse {
  summary: TradeListSummary
  items: TradeListItem[]
  pagination: Pagination
}

TradeListSummary

Summary values use the same filters as the list and aggregate all matching records, not only the current page.

FieldJSON typeDefinition
fee_credits_totalintegerSum of linked executed trades' total trade fees, in Polynion Credits. Orders without a linked trade contribute 0.
operator_fee_bpsintegerAuthenticated operator's configured share of trade fees, in basis points. 10_000 represents 100%.
operator_fee_credits_totalintegerSum of floor(fee_credits_total × operator_fee_bps / 10_000) per linked trade, in Credits.
fee_external_amount_minor_totalintegerSum of historical operator-currency equivalents of linked trade fees.
operator_fee_external_amount_minor_totalintegerSum of the operator's share of those historical fee equivalents.
currency_codestringAuthenticated operator's configured currency code.

TradeListItem

id identifies a Mini App order. It is not the executed trade ID. Use executed_trade_id with the trade-detail endpoint.

FieldJSON typeNullableDefinition
idintegerNoMini App order ID.
idempotency_keystringNoOperator-supplied order idempotency key.
statusstring enumNoCurrent OrderStatus.
sidestring enumNobuy or sell.
outcomestring enumNoyes or no.
requested_external_amount_minorintegerYesStored requested operator-currency amount. Normally populated for buys; may be null for sells or records without an amount request.
requested_shares_microintegerYesStored requested/quoted share quantity in micro-shares. 1_000_000 equals one share.
quoted_credits_equivalentintegerYesCredits equivalent recorded at quote time.
quoted_external_amount_minorintegerYesOperator-currency amount recorded at quote time.
executed_trade_idintegerYesLinked Polynion trade ID after execution. Use this value for GET /api/operator/trades/{tradeId}.
executed_credits_deltaintegerYesSigned Credits balance delta: normally negative for buys and positive for sells.
executed_shares_deltaintegerYesExecuted quantity in micro-shares.
executed_external_amount_minorintegerYesHistorical operator-wallet debit/credit amount recorded at execution.
fx_rate_snapshot_idintegerYesFX snapshot used by the quote/execution.
fee_breakdownJSON valueYesStored quote fee configuration. Current producers write FeeBreakdown; legacy/unprocessed records can be null. The route returns the stored JSON without runtime shape validation.
fee_credits_totalintegerNoLinked trade's total fee in Credits, or 0 before/no execution.
operator_fee_bpsintegerNoCurrent authenticated operator fee-share configuration in basis points.
operator_fee_creditsintegerNofloor(fee_credits_total × operator_fee_bps / 10_000).
gross_external_amount_minorintegerNoSigned gross user cash flow before the fee: negative for buys and positive for sells. Sell gross reconstructs the pre-fee amount as the net wallet credit plus the fee.
fee_external_amount_minorintegerNoHistorical operator-currency equivalent of fee_credits_total, derived proportionally from this order's executed Credits and external amount. Returns 0 when execution data is unavailable or zero.
net_external_amount_minorintegerNoSigned net user cash flow after the fee: negative for buys and positive for sells. Buy net is the gross wallet debit less the fee; sell net is the wallet credit recorded at execution.
operator_fee_external_amount_minorintegerNofloor(fee_external_amount_minor × operator_fee_bps / 10_000).
currency_codestringNoAuthenticated operator's configured currency code.
failure_reasonstringYesStored order failure reason.
created_atISO 8601 stringNoOrder creation timestamp.
updated_atISO 8601 stringNoOrder update timestamp.
processed_atISO 8601 stringYesSuccessful/final processing timestamp when recorded.
operator_userobjectNoTradeListOperatorUser.
marketobjectNoTradeMarketSummary. This endpoint-specific projection is smaller than the objects in the Markets API.

External amount fields are zero-decimal integer amounts in currency_code; do not apply an ISO currency exponent. The *_external_amount_minor name is retained for API consistency. Gross and net amounts use user cash-flow signs (buy < 0, sell > 0), while fee amounts remain non-negative reporting values.

Nested list objects

Object/fieldJSON typeNullableDefinition
FeeBreakdown.fxFeeBpsintegerNoFX conversion fee configured at quote time, in basis points.
FeeBreakdown.operatorFeeBpsintegerNoOperator fee component captured by the quote writer. The current trade quote path writes 0; it is distinct from top-level operator_fee_bps, which is the operator's revenue share of trade fees.
FeeBreakdown.totalFeeBpsintegerNoSum of the fee-breakdown components.
TradeListOperatorUser.idintegerNoInternal operator-user mapping ID.
TradeListOperatorUser.external_user_idstringNoExact operator-side user ID.
TradeListOperatorUser.display_namestringYesDisplay-name snapshot.
TradeMarketSummary.idintegerNoStable Polynion market ID.
TradeMarketSummary.slugstringNoStable market slug.
TradeMarketSummary.titlestringNoSource market title.
TradeMarketSummary.statusstring enumNoCurrent MarketStatus.

Pagination

FieldJSON typeDefinition
pageintegerEffective page number after defaulting/capping; always 1..10,000.
page_sizeintegerEffective page size after defaulting/capping.
totalintegerTotal matching orders across all pages.
total_pagesintegermax(1, ceil(total / page_size)); an empty result reports 1.

Example

{
  "summary": {
    "fee_credits_total": 500,
    "operator_fee_bps": 1000,
    "operator_fee_credits_total": 50,
    "fee_external_amount_minor_total": 810,
    "operator_fee_external_amount_minor_total": 81,
    "currency_code": "IDR"
  },
  "items": [
    {
      "id": 9001,
      "idempotency_key": "operator-order-123",
      "status": "processed",
      "side": "buy",
      "outcome": "yes",
      "requested_external_amount_minor": 81000,
      "requested_shares_micro": 12500000,
      "quoted_credits_equivalent": 50000,
      "quoted_external_amount_minor": 81000,
      "executed_trade_id": 501,
      "executed_credits_delta": -50000,
      "executed_shares_delta": 12500000,
      "executed_external_amount_minor": 81000,
      "fx_rate_snapshot_id": 77,
      "fee_breakdown": {
        "fxFeeBps": 0,
        "operatorFeeBps": 0,
        "totalFeeBps": 0
      },
      "fee_credits_total": 500,
      "operator_fee_bps": 1000,
      "operator_fee_credits": 50,
      "gross_external_amount_minor": -81000,
      "fee_external_amount_minor": 810,
      "net_external_amount_minor": -80190,
      "operator_fee_external_amount_minor": 81,
      "currency_code": "IDR",
      "failure_reason": null,
      "created_at": "2026-07-10T12:00:00.000Z",
      "updated_at": "2026-07-10T12:00:01.000Z",
      "processed_at": "2026-07-10T12:00:01.000Z",
      "operator_user": {
        "id": 42,
        "external_user_id": "operator-user-123",
        "display_name": "Rina"
      },
      "market": {
        "id": 51,
        "slug": "will-indonesia-qualify-for-the-2026-world-cup",
        "title": "Will Indonesia qualify for the 2026 World Cup?",
        "status": "open"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total": 1,
    "total_pages": 1
  }
}

Trade detail

GET /api/operator/trades/{tradeId}

Required scope: trades:read

Use the actual executed trade id from the list response's executed_trade_id. Do not pass the Mini App order id; they identify different records.

tradeId is a required positive integer path parameter. The endpoint has no query parameters or request body.

The route is operator-scoped. It returns only a Mini App trade whose trade row and linked order both belong to the authenticated operator. An unknown trade or a trade owned by another operator returns the same 404 TRADE_NOT_FOUND response.

Example

curl -sS \
  -H "Authorization: Bearer ***" \
  "https://your-beacon-origin.example.com/api/operator/trades/501"

Response

Content type: application/json. Response headers include Cache-Control: no-store and Vary: Authorization.

type TradeStatus = 'queued' | 'processed' | 'failed'
type OperatorUserStatus = 'active' | 'suspended' | 'disabled'
type ResolvedOutcome = 'yes' | 'no'

interface TradeDetailOperatorUser {
  id: number
  external_user_id: string
  polynion_user_id: number
  display_name: string | null
  status: OperatorUserStatus
}

interface TradeDetailMarket {
  id: number
  slug: string
  title: string
  status: MarketStatus
  resolved_outcome: ResolvedOutcome | null
}

interface TradeDetailOrder {
  id: number
  idempotency_key: string
  status: OrderStatus
  side: TradeSide
  outcome: TradeOutcome
  requested_external_amount_minor: number | null
  requested_shares_micro: number | null
  quoted_credits_equivalent: number | null
  quoted_external_amount_minor: number | null
  executed_trade_id: number
  executed_credits_delta: number | null
  executed_shares_delta: number | null
  executed_external_amount_minor: number | null
  fx_rate_snapshot_id: number | null
  fee_breakdown: JsonValue
  attempt_count: number
  failure_reason: string | null
  created_at: string
  updated_at: string
  processed_at: string | null
}

interface TradeDetailResponse {
  trade_id: number
  order_id: number
  idempotency_key: string
  status: TradeStatus
  side: TradeSide
  outcome: TradeOutcome
  shares_delta: number
  credits_delta: number
  avg_price_bps: number
  fee_credits_total: number
  operator_fee_bps: number
  operator_fee_credits: number
  source_channel: 'mini_app'
  external_currency_code: string | null
  external_amount_minor: number | null
  fx_rate_snapshot_id: number | null
  failure_reason: string | null
  created_at: string
  processed_at: string | null
  operator_user: TradeDetailOperatorUser
  market: TradeDetailMarket
  order: TradeDetailOrder
}

Response example:

{
  "trade_id": 501,
  "order_id": 9001,
  "idempotency_key": "mini_app_order:9001:trade",
  "status": "processed",
  "side": "buy",
  "outcome": "yes",
  "shares_delta": 12500000,
  "credits_delta": -50000,
  "avg_price_bps": 4000,
  "fee_credits_total": 500,
  "operator_fee_bps": 1000,
  "operator_fee_credits": 50,
  "source_channel": "mini_app",
  "external_currency_code": "IDR",
  "external_amount_minor": 81000,
  "fx_rate_snapshot_id": 77,
  "failure_reason": null,
  "created_at": "2026-07-10T12:00:00.000Z",
  "processed_at": "2026-07-10T12:00:01.000Z",
  "operator_user": {
    "id": 42,
    "external_user_id": "operator-user-123",
    "polynion_user_id": 987,
    "display_name": "Rina",
    "status": "active"
  },
  "market": {
    "id": 51,
    "slug": "will-indonesia-qualify-for-the-2026-world-cup",
    "title": "Will Indonesia qualify for the 2026 World Cup?",
    "status": "open",
    "resolved_outcome": null
  },
  "order": {
    "id": 9001,
    "idempotency_key": "operator-order-123",
    "status": "processed",
    "side": "buy",
    "outcome": "yes",
    "requested_external_amount_minor": 81000,
    "requested_shares_micro": 12500000,
    "quoted_credits_equivalent": 50000,
    "quoted_external_amount_minor": 81000,
    "executed_trade_id": 501,
    "executed_credits_delta": -50000,
    "executed_shares_delta": 12500000,
    "executed_external_amount_minor": 81000,
    "fx_rate_snapshot_id": 77,
    "fee_breakdown": {
      "fxFeeBps": 0,
      "operatorFeeBps": 0,
      "totalFeeBps": 0
    },
    "attempt_count": 1,
    "failure_reason": null,
    "created_at": "2026-07-10T12:00:00.000Z",
    "updated_at": "2026-07-10T12:00:01.000Z",
    "processed_at": "2026-07-10T12:00:01.000Z"
  }
}

TradeDetailResponse

FieldJSON typeNullableDefinition
trade_idintegerNoExecuted Polynion trade ID.
order_idintegerNoLinked Mini App order ID.
idempotency_keystringNoTrade-row idempotency key. This is separate from order.idempotency_key.
statusstring enumNoTrade status: queued, processed, or failed.
sidestring enumNobuy or sell.
outcomestring enumNoyes or no.
shares_deltaintegerNoExecuted quantity in micro-shares. 1_000_000 equals one share.
credits_deltaintegerNoSigned Credits delta: normally negative for buys and positive for sells.
avg_price_bpsintegerNoAverage execution price in basis points; 10_000 represents 100%.
fee_credits_totalintegerNoTotal trade fee in Polynion Credits.
operator_fee_bpsintegerNoAuthenticated operator's current fee-share configuration in basis points.
operator_fee_creditsintegerNofloor(fee_credits_total × operator_fee_bps / 10_000).
source_channelstring literalNoAlways mini_app; the loader excludes other channels.
external_currency_codestringYesCurrency code recorded on the trade.
external_amount_minorintegerYesHistorical external-wallet debit/credit amount recorded on the trade.
fx_rate_snapshot_idintegerYesFX snapshot recorded on the trade.
failure_reasonstringYesTrade-row failure reason.
created_atISO 8601 stringNoTrade creation timestamp.
processed_atISO 8601 stringYesTrade processing timestamp.
operator_userobjectNoTradeDetailOperatorUser.
marketobjectNoTradeDetailMarket.
orderobjectNoFull linked TradeDetailOrder.

TradeDetailOperatorUser

FieldJSON typeNullableDefinition
idintegerNoInternal operator-user mapping ID.
external_user_idstringNoExact operator-side user ID.
polynion_user_idintegerNoInternal Polynion user ID linked to this operator user.
display_namestringYesDisplay-name snapshot.
statusstring enumNoactive, suspended, or disabled.

TradeDetailMarket

This is the canonical market projection for trade detail and trade-history identity. It intentionally contains fewer fields than Markets API objects.

FieldJSON typeNullableDefinition
idintegerNoStable Polynion market ID.
slugstringNoStable market slug.
titlestringNoSource market title.
statusstring enumNoCurrent MarketStatus.
resolved_outcomestring enumYesyes, no, or null when unresolved.

TradeDetailOrder

FieldJSON typeNullableDefinition
idintegerNoMini App order ID; equal to top-level order_id.
idempotency_keystringNoOperator-supplied order idempotency key.
statusstring enumNoCurrent OrderStatus.
sidestring enumNobuy or sell.
outcomestring enumNoyes or no.
requested_external_amount_minorintegerYesStored requested operator-currency amount.
requested_shares_microintegerYesStored requested/quoted quantity in micro-shares.
quoted_credits_equivalentintegerYesCredits equivalent recorded at quote time.
quoted_external_amount_minorintegerYesExternal amount recorded at quote time.
executed_trade_idintegerNoLinked executed trade ID. The detail loader requires it to equal top-level trade_id.
executed_credits_deltaintegerYesSigned executed Credits delta.
executed_shares_deltaintegerYesExecuted quantity in micro-shares.
executed_external_amount_minorintegerYesExternal wallet amount recorded at execution.
fx_rate_snapshot_idintegerYesFX snapshot recorded on the order.
fee_breakdownJSON valueYesStored quote-time data. Current producers write FeeBreakdown; the route returns the stored JSON without runtime shape validation.
attempt_countintegerNoNumber of processing attempts recorded on the order.
failure_reasonstringYesOrder processing failure reason.
created_atISO 8601 stringNoOrder creation timestamp.
updated_atISO 8601 stringNoOrder update timestamp.
processed_atISO 8601 stringYesOrder processing timestamp.

credits_delta, fee_credits_total, operator_fee_credits, order.quoted_credits_equivalent, and order.executed_credits_delta are Polynion Credits. External amount fields are zero-decimal integer amounts labeled by external_currency_code; do not apply an ISO currency exponent.

The detail response does not include fee_external_amount_minor or operator_fee_external_amount_minor. Those historical fee-equivalent fields are available on the list response.

Rendered HTML alternative

GET /api/operator/trades/{tradeId}/view

Required scope: trades:read

This route uses the same operator-scoped loader as the JSON detail endpoint and renders its data as a responsive read-only receipt. It is designed for operator backends and the Dashboard API Playground's isolated HTML Preview.

Unlike the Portfolio HTML view, this endpoint does not create or update users, mint launch tokens, or require launch:write. It is a read-only GET.

curl -sS \
  -H "Authorization: Bearer ***" \
  "https://your-beacon-origin.example.com/api/operator/trades/501/view" \
  --output trade-501.html

The HTML response includes:

  • execution summary with side, outcome, external amount, shares, average price, Credits delta, and total fee
  • operator user and market identity
  • linked order quote and settlement values
  • idempotency keys, FX snapshot, timestamps, attempts, and failure reason when present
  • escaped fee-breakdown JSON
  • responsive mobile and print layouts

Response type: HTML document string, not JSON.

Response propertyValue
Status200
Content-Typetext/html; charset=utf-8
Cache-Controlno-store
Content-Security-PolicyRestricts all sources, framing, forms, images, and styles to the receipt's declared policy.
X-Robots-Tagnoindex, nofollow
X-Content-Type-Optionsnosniff
VaryAuthorization

Both detail routes use the same tenant-scoped loader and return the same validation and not-found errors.

Trade detail errors:

  • 400 INVALID_TRADE_ID: tradeId is non-positive, malformed, or outside the safe-integer range.
  • 404 TRADE_NOT_FOUND: no operator-owned Mini App trade and linked order match.

Market/user trade history HTML

GET /api/operator/trades/{marketId}/{externalUserId}

Required scope: trades:read

This read-only route renders the authenticated operator's processed Mini App trades for one exact operator user and one exact market. It remains available for historical operator-owned trades when that market is no longer currently visible to the operator.

Path parameters:

ParameterTypeRequirement
marketIdpositive integerRequired
externalUserIdstring, 1–255 charactersRequired; URL-encode the value

Optional pagination uses page (default 1, maximum 10,000) and page_size (default 50, maximum 100). Results are ordered by newest trade first. A valid operator user and market with no matching trades returns 200 with a clear empty-history view.

The endpoint is intended for authenticated operator backends and the Dashboard API Playground. The HTML table includes market, UTC created_at time, side, outcome, human-readable shares converted from micro-shares, and signed zero-decimal external-currency amount. processed_at is loaded but is not rendered as the table time. All dynamic values are escaped. The route does not mutate users, markets, orders, or launch tokens.

Response type: HTML document string, not JSON. Buy amounts render as negative wallet movement; sell amounts render as positive wallet movement. The document also renders the exact operator-user identity, market title/slug/status, and pagination values.

Response propertyValue
Status200
Content-Typetext/html; charset=utf-8
Cache-Controlno-store
Content-Security-PolicyRestrictive inline-document policy.
X-Robots-Tagnoindex, nofollow
X-Content-Type-Optionsnosniff
VaryAuthorization

Errors:

  • 400 INVALID_MARKET_ID
  • 400 INVALID_EXTERNAL_USER_ID
  • 404 OPERATOR_USER_NOT_FOUND
  • 404 MARKET_NOT_FOUND
Copyright © 2026