API DocumentationProfessional trading
/
DEV qualification

Professional Trading API

Contract 2.1 · DEV qualification candidate · 7 October 2026

Integrate a market-making application using OAuth, canonical REST or persistent WebSocket commands, a recoverable L2 book, and a private execution feed. These pages describe implemented contracts, not a production SLA. Environment activation and tested capacity must be confirmed during onboarding.

Publication on docs.ympredictions.com does not activate these interfaces in PROD. The examples and downloadable contract target the qualified DEV release; production endpoint availability requires a separate rollout and confirmation.

Start here

  1. Complete onboarding: one confidential client and one dedicated account, approved scopes, financial activation and UAT.
  2. Connect and place an order. Establish private-feed recovery and a fresh book before quoting.
  3. Implement cancellation safety, then reconcile executions and economics.
  4. Use the endpoint reference or downloadable OpenAPI for exact JSON shapes.

Three connections, three purposes

Interface Purpose Recovery
/v1/market-feed Shared, anonymous YES-coordinate L2 book Replace books with fresh snapshots on every connection; no historical replay.
/v1/order-entry or order REST Submit NEW, REPLACE and CANCEL Persist outbound commands; retry an identical command with its original key.
/v1/trading-feed Own command results, orders and financially posted fills Signed account-bound cursor; at-least-once replay and versioned order snapshots.

An acknowledgement means admission, not matching-engine completion. Do not infer your fills from public book changes. Identity comes from the validated bearer token, never a body field or client-selected account ID.

Core contract

  • Orders name action (BUY/SELL), outcome (YES/NO), integer price_cents (0..100) in that outcome, and integer contract quantity.
  • Public books use YES prices. A NO command's price is not pre-complemented.
  • Use immutable market IDs and their current positive market_generation.
  • Read book currency from /v1/account/trading-fees. DEV is a synthetic EUR book, not access to real-money trading.
  • Native post-only is maker-or-cancel, including on replacement. A crossing post-only replacement can cancel the existing quote; it does not promise to leave that quote working.
  • Shared firm wallets, arbitrary subaccount selectors, FIX, atomic quote-set replacement and guaranteed cross-market order sequencing are not offered.

YM owns authentication, admission, collateral, financial posting, lifecycle and participant projections. DXmatch supplies matching and venue-order evidence. Participants use YM interfaces; direct DXmatch credentials are not required.

The handbook is generated from versioned guides and a pinned participant OpenAPI snapshot of the DEV release, and checked for drift. Report discrepancies with UTC time, environment, error code, request correlation ID and client request ID—never secrets or bearer tokens.

1. Quickstart

Environment: DEV qualification; approved synthetic UAT account only

This guide uses the v2 order contract. Confirm v2 activation for the target environment before sending commands; v1 fields are intentionally rejected.

This sequence opens the private feed before submitting an order, then follows one order through NEW, REPLACE and CANCEL. Use an account and market explicitly approved for UAT. Do not run the example against an unapproved financial environment.

Prerequisites

YM supplies each approved integration with:

  • an OAuth confidential client_id and client_secret;
  • the YM account ID bound to that client;
  • the granted scopes;
  • the permitted environment and test markets; and
  • an operational contact.

The initial pilot binds one confidential client to one financial account. Do not place a user_id in an order request.

Set local variables without committing their values:

export YM_BASE_URL="https://dev.ympredictions.com"
export YM_TOKEN_URL="https://auth.dev.ympredictions.com/realms/ymp/protocol/openid-connect/token"
export YM_CLIENT_ID="supplied-by-ym"
export YM_CLIENT_SECRET="supplied-by-ym"
export YM_ACCOUNT_ID="supplied-by-ym"

1. Obtain a short-lived access token

TOKEN_RESPONSE="$(curl -sS -X POST "$YM_TOKEN_URL" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=$YM_CLIENT_ID" \
  --data-urlencode "client_secret=$YM_CLIENT_SECRET")"

export YM_ACCESS_TOKEN="$(printf '%s' "$TOKEN_RESPONSE" | jq -r '.access_token')"

The current pilot token lifetime is five minutes. Request a new token before expiry; do not expect a refresh token from the client-credentials grant.

2. Select a canonical market

curl -sS \
  "$YM_BASE_URL/v1/markets?view=canonical&status=active&limit=20" \
  | jq '.markets[] | {
      market_id: .id,
      market_generation,
      status,
      publication_state,
      title
    }'

Select a leaf with all of the following:

  • a positive market_generation;
  • status equal to OPEN;
  • publication_state equal to PUBLIC_OPEN; and
  • a close time that has not passed.

The order API remains the final admission authority. A catalog result is not a reservation of future tradeability.

export YM_MARKET_ID="selected-immutable-market-id"
export YM_MARKET_GENERATION="selected-positive-generation"

3. Establish a fresh L2 book

Connect to wss://dev.ympredictions.com/v1/market-feed?markets=<market-id> with the same bearer header. Require a FRESH book.snapshot and validate every connection sequence. Reconnect and replace the book on any gap. See Market data for the complete consumer contract.

For a one-off diagnostic snapshot:

curl -sS \
  "$YM_BASE_URL/v1/book?market_id=$YM_MARKET_ID&depth=20" \
  | jq .

The snapshot is public. seq describes the current YM projection; it is not a durable professional replay cursor. See Market data.

4. Open the private feed

Open this in a second terminal before sending the order:

npx wscat \
  -c 'wss://dev.ympredictions.com/v1/trading-feed' \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN"

Wait for this bootstrap sequence:

stream.welcome
stream.snapshot_started
stream.snapshot_chunk (zero or more)
stream.snapshot_completed
stream.ready

Only submit commands after stream.ready. Persist each event's signed cursor after the event has been validated and durably applied locally.

5. Submit a resting order

The example buys ten YES contracts at 45 cents using GTC:

export YM_CLIENT_ORDER_ID="quickstart-order-$(date +%s)"
export YM_NEW_REQUEST_ID="quickstart-new-$(date +%s)-$RANDOM"

NEW_RESPONSE="$(curl -sS -X POST "$YM_BASE_URL/v1/orders" \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $YM_NEW_REQUEST_ID" \
  --data "{
    \"client_order_id\": \"$YM_CLIENT_ORDER_ID\",
    \"market_id\": \"$YM_MARKET_ID\",
    \"market_generation\": $YM_MARKET_GENERATION,
    \"action\": \"BUY\",
    \"outcome\": \"YES\",
    \"price_cents\": 45,
    \"quantity\": 10,
    \"time_in_force\": \"GTC\"
  }")"

printf '%s' "$NEW_RESPONSE" | jq .
export YM_ORDER_ID="$(printf '%s' "$NEW_RESPONSE" | jq -r '.order.id')"

201 Created means YM admitted the command and durably created a PENDING order. Wait for order.working on the private feed, or read the canonical order until execution_status is WORKING, before treating it as venue-effective.

For a NO order, keep the same coordinate end to end. A request with "action":"BUY", "outcome":"NO", and "price_cents":35 must return and stream BUY / NO / 35. Never send or expect the internal complementary YES-book representation.

6. Read the canonical order

curl -sS \
  "$YM_BASE_URL/v1/orders/$YM_ORDER_ID" \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN" \
  | jq .

Record order_version. A replacement is fenced by the version it was based on.

7. Replace the working order

This changes the effective price of the order's immutable YES outcome to 44 cents while retaining the target total quantity of ten contracts:

ORDER_RESPONSE="$(curl -sS \
  "$YM_BASE_URL/v1/orders/$YM_ORDER_ID" \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN")"
export YM_ORDER_VERSION="$(printf '%s' "$ORDER_RESPONSE" | jq -r '.order_version')"
export YM_REPLACE_REQUEST_ID="quickstart-replace-$(date +%s)-$RANDOM"

curl -sS -X POST \
  "$YM_BASE_URL/v1/orders/$YM_ORDER_ID/replace" \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $YM_REPLACE_REQUEST_ID" \
  --data "{
    \"expected_order_version\": $YM_ORDER_VERSION,
    \"price_cents\": 44,
    \"total_quantity\": 10
  }" | jq .

202 Accepted means the replacement command was admitted. The prior venue-effective price and quantity remain authoritative until order.replaced arrives.

8. Cancel the order

Do not send a cancel while a replacement is still pending. Wait for order.replaced, then use a new idempotency key:

export YM_CANCEL_REQUEST_ID="quickstart-cancel-$(date +%s)-$RANDOM"

curl -sS -X POST \
  "$YM_BASE_URL/v1/orders/$YM_ORDER_ID/cancel" \
  -H "Authorization: Bearer $YM_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $YM_CANCEL_REQUEST_ID" \
  --data '{}' | jq .

202 Accepted is not cancellation finality. Wait for order.cancelled or a canonical terminal snapshot before releasing local risk.

9. Resume after disconnect

Reconnect with the last cursor your application fully applied:

wss://dev.ympredictions.com/v1/trading-feed?cursor=<URL-encoded-signed-cursor>

The server replays events after that cursor and sends stream.ready. Delivery is at least once; deduplicate using event_id and never derive sequence numbers from the cursor.

Forward path

REST is the pilot command transport. Persistent WebSocket order entry is in development and will use the same IDs, idempotency, market-generation fence and canonical lifecycle. Existing REST clients will not need a separate business model when that transport becomes available.

2. API reference and versioning

Download OpenAPI 3.1. This publication pins a DEV-release contract extracted from the platform specification with a participant endpoint allowlist and all referenced schemas. Internal services, operator administration, payments, retired order fields and retail-only streaming interfaces are excluded.

The public namespace remains /v1; schema_version: 2 identifies canonical order and private-feed payloads. The book stream uses ym.market-data.v1, and the command socket uses ym.order-entry.v1. These are separate contracts.

REST documentation describes WebSocket upgrades; the corresponding guide gives message ordering, recovery and client obligations. An HTTP schema alone is not an execution-state machine.

Compatibility

Ignore unknown optional response fields. Do not send unknown request fields or enums. Changes to price coordinates, identity, recovery or existing field meanings require an explicit migration. Old side, position_side, intent and decimal price order inputs are not v2 aliases and are rejected.

V1 private-feed cursors cannot resume v2. Retain historical terminal/fill data through any announced migration; a cursorless snapshot replaces only current nonterminal orders, not account history.

Maintaining this publication

From Backend/scripts/market-maker:

npm ci --ignore-scripts
npm run generate
npm run check
npm test

On the docs-only main branch, generation reads the published participant YAML, not main's application specification. Source revision and contract hashes are recorded in publication-source.json alongside the generator. Update this snapshot from the reviewed DEV release when its contract changes; the source hash test intentionally fails until that update is recorded explicitly.

The HTML handbook and endpoint reference are generated artifacts. The participant YAML remains the pinned, self-contained source for this publication. The older pilot HTML redirects here; its old PDF is not the current contract. Documentation does not itself activate a feature on another environment. Consult the environment's deployed revision and UAT sign-off.

3. Authentication and scopes

Use OAuth 2.0 client credentials. YM supplies a confidential client bound to a single dedicated financial account. Keep the secret in your secret manager; never send it to the trading API or place credentials in URLs.

POST https://auth.dev.ympredictions.com/realms/ymp/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=<assigned>&client_secret=<secret>

URL-encode all form values. Authenticate REST and all three headless WebSocket upgrades with Authorization: Bearer <access_token>. Do not request browser password grants, invent a user_id, or reuse another firm's credentials.

Scope Authority
orders:write Canonical order commands and own cancellation controls.
orders:read Own orders, positions, executions, fee policy and L2 stream.
trading-feed:read Own resumable private order/execution stream.
balances:read Own balances, ledger evidence and allocated rebates.
trading:professional Configured professional rate policy; grants no trading or data authority alone.

Scopes are operator-assigned, not elevated by requesting more scope at login. Tokens identify the service-account subject. This exact subject must be the approved YM financial account. Client names and display names are not identity.

Token renewal

Use expires_in; the current configured lifetime is 300 seconds. Obtain a new token before expiry, with jitter. Client credentials do not require or promise a refresh token. Cache secrets only in memory or an approved secret store.

Reconnect the private feed from its last durably applied cursor, rebuild public books from fresh snapshots, and reconnect command entry with the new token. Do not retransmit uncertain commands with new keys. Token expiry ends the session; per-command authorization still applies on the command socket.

Rotation prevents obtaining new tokens with the old secret; it does not alone invalidate every already-issued token. Emergency trading containment also requires an account restriction, terminal cancellation and verification. See Onboarding.

Isolation and transport

Balance path IDs must equal the token subject. Private cursors are signed and account-bound. Execution and ledger reads never return an opposing participant's identity or postings. Public books deliberately aggregate anonymous price levels from all participants.

Headless clients may omit Origin. Browser protections remain active. Use TLS and never disable certificate validation. A 401 requires authentication recovery; a 403 requires correcting entitlement or account state, not a retry storm. Honor Retry-After for 429 responses.

4. Markets, identity and lifecycle

Canonical discovery is REST; subscribed book streams carry generation, freshness and open/closed status. Catalog details still come from REST.

Every trading command targets one immutable leaf market_id and the exact current market_generation. Titles, slugs, URLs and provider identifiers are presentation or discovery data and must not be used as trading identity.

Hierarchy

Resource Purpose
Event The real-world occurrence, fixture, election or question grouping.
Market family A coordinated proposition such as match winner, set winner or tournament winner.
Market leaf One binary outcome market. Public orders select YES or NO; YM normalizes both to the leaf's internal YES book.

A family may contain one or many leaves. membership_complete describes whether the canonical family membership is complete; it does not make the entire family one tradable instrument.

Canonical discovery

Always request the canonical view:

GET /v1/markets?view=canonical
GET /v1/markets/{marketID}?view=canonical
GET /v1/events
GET /v1/events/{eventID}
GET /v1/events/{eventID}/market-families
GET /v1/market-families?view=canonical
GET /v1/market-families/{familyID}?view=canonical

Canonical collection pagination uses the opaque next_cursor; offset pagination is unsupported. Treat cursors as opaque and scoped to the request shape that produced them.

Provider mappings are returned only through explicit canonical expansions. They are diagnostic metadata, not stable order-entry identifiers.

Trading eligibility indicators

Before submitting an order, require:

  • market_generation > 0;
  • market status equal to OPEN;
  • publication_state equal to PUBLIC_OPEN; and
  • close_at, when present, later than the current UTC time.

These are client-side preflight indicators. YM rechecks authoritative admission, lifecycle, participant and collateral state atomically during order entry. A market can stop accepting commands after it was discovered.

PUBLIC_BLOCKED may remain visible for discovery, live presentation or history while admission is disabled. It is not a tradable state.

Market generation

market_generation is an optimistic safety fence around the market's current admission definition. It prevents a command constructed against an older definition from entering a newer market state.

On 409 stale_market_generation:

  1. Stop sending commands with the old generation.
  2. Fetch GET /v1/markets/{marketID}?view=canonical.
  3. Re-evaluate status, publication, close time and the changed definition.
  4. Rebuild the command with the new positive generation.
  5. Use a new client request ID if the economic command changed. Reuse the old ID only to recover the exact original payload.

Never increment or infer a generation locally.

Lifecycle behavior

  • An OPEN market may accept orders when its admission and publication state also allow trading.
  • Closing begins a controlled stop-and-drain process. A displayed live status alone does not override admission state.
  • CLOSED prevents new exposure while finality or settlement may still be in progress.
  • RESOLVED records the canonical result. Historical discovery may retain the market after trading has ended.
  • Existing nonterminal orders may receive system-generated cancellation or expiry events as part of lifecycle control. These appear on the same private feed as client commands.

Do not assume the scheduled start of an event is necessarily its trading close or resolution time. Use the canonical market fields and effective lifecycle events.

Revision versus generation

revision tracks canonical presentation/catalog changes. market_generation is the trading admission fence. A client must not substitute one for the other.

Streaming status

The professional book stream includes the catalog generation, lifecycle and fresh/stale status for subscribed markets. Stop quoting when it becomes stale or closed; fetch canonical details when generation changes. It does not replace catalog pagination or promise an ordered history of every catalog revision. See Market data.

5. Order entry and lifecycle

Status: canonical REST NEW, REPLACE and CANCEL available in the controlled pilot

YM exposes one canonical order model. REST admission, canonical reads and the private feed describe the same order and command identities. The persistent WebSocket command adapter enters this same business path; it does not create separate order semantics. FIX is not offered.

Three distinct states

Never collapse these layers into one generic accepted state:

Layer Authority Meaning
Command admission YM The command was durably accepted or rejected by YM.
Exchange execution DXmatch evidence projected by YM The effective working, filled, replaced or terminal state.
Financial posting YM ledger The resulting hold, fill and settlement posting state.

An HTTP 201 or 202 is command admission, not venue finality.

Action, outcome and price semantics

The public contract always describes the participant's selected outcome. Clients send action, outcome, and that outcome's price_cents:

action outcome Meaning
BUY YES Increase YES exposure at the stated YES price.
SELL YES Reduce YES exposure at the stated YES price.
BUY NO Increase NO exposure at the stated NO price.
SELL NO Reduce NO exposure at the stated NO price.

YM derives the internal YES-book side and OPEN/CLOSE accounting effect. Do not complement NO prices: BUY NO at 35 means pay 35 cents for NO. Naked shorting is not exposed; a SELL must be covered by the named outcome position.

Prices are integer cents from 0 through 100; quantities are positive whole contracts. The public professional contract currently exposes limit orders only.

Time in force

Value Behavior
GTC Rest until filled, explicitly cancelled or removed by market lifecycle. Preferred for professional resting liquidity.
IOC Attempt immediate execution and cancel any unfilled remainder. It remains a limit order with price protection.
DAY Venue-supported session-scoped lifetime. Do not use it as a substitute for a long-lived prediction-market order.

GTD, FOK, market, stop and iceberg orders are not currently exposed.

NEW

POST /v1/orders
Authorization: Bearer <token>
Idempotency-Key: <participant-scoped-command-id>
Content-Type: application/json

Required body:

{
  "client_order_id": "strategy-a-000001",
  "market_id": "immutable-market-id",
  "market_generation": 7,
  "action": "BUY",
  "outcome": "YES",
  "price_cents": 45,
  "quantity": 100,
  "time_in_force": "GTC"
}

Do not send user_id. client_order_id is participant-defined, has a maximum length of 200 characters and must remain unique within the participant's retained order history.

Do not send the retired v1 fields side, position_side, intent, or decimal price. Unknown request fields fail closed.

Responses:

  • 201: newly admitted command with a PENDING order.
  • 200: exact idempotent replay of the original command.
  • 4xx/5xx: inspect the canonical error and follow the retry policy.

YM transactionally records the admitted command, order, collateral reservation and durable downstream work before responding. DXmatch execution remains asynchronous.

REPLACE

POST /v1/orders/{orderID}/replace
Idempotency-Key: <new-command-id>
{
  "expected_order_version": 4,
  "price_cents": 46,
  "total_quantity": 120
}

Rules:

  • Use the order_version from the latest canonical order snapshot.
  • price_cents remains in the order's immutable outcome coordinate. A NO replacement is therefore also a NO price and must not be complemented.
  • total_quantity is the target total for the order, including quantity already filled. It must exceed cumulative_filled_quantity.
  • A replacement receives its own idempotency key.
  • Only one mutation may be pending on an order.
  • 202 means the replacement was admitted. Until order.replaced, the prior effective price and quantity remain authoritative.
  • A rejected replacement leaves the prior effective order in place.
  • Fills racing with an admitted replacement remain valid and are reflected in subsequent order versions.

DX applies an accepted REPLACE to one existing venue order as a single-order mutation. YM admission is not proof that it became effective. REPLACE does not atomically replace a bid/ask pair.

CANCEL

POST /v1/orders/{orderID}/cancel
Idempotency-Key: <new-command-id>
Content-Type: application/json

{}
  • 202 means a venue-directed cancel was admitted and pending_action=CANCEL.
  • 200 means cancellation is already locally effective or the command is an idempotent completed/no-op result.
  • Cancellation is final only when the canonical order is terminal or an order.cancelled event is applied.
  • Lifecycle and risk cancellations use the same canonical order state and are visible even when initiated outside the current client connection.

Idempotency

All single-order mutations require Idempotency-Key:

  • maximum 200 characters;
  • scoped to the authenticated participant;
  • the ym: prefix is reserved;
  • retry an uncertain request with the exact same key and payload;
  • reusing a key with a different payload is a conflict; and
  • use a distinct key for each distinct economic command.

Transport timeout is not evidence of failure. Recover by replaying the exact request or querying the resulting canonical order.

Clients must durably retain each outbound command payload under its client_request_id. A command result identifies admission/outcome and current order state; it is not a copy of the client's command-request journal. There is currently no general query-by-client-request-ID endpoint.

Batch commands

POST /v1/orders/batch
POST /v1/orders/cancel-batch

The contract accepts at most 250 items. Each item carries its own client_request_id and returns its own status, acceptance and retryability.

A batch is a transport convenience, not one atomic multi-order transaction. Clients must inspect every item and retry only the exact uncertain items with the same item IDs and payloads.

Canonical reads

GET /v1/orders/{orderID}
GET /v1/orders?market_id=...&execution_status=...&cursor=...&limit=...

Order reads expose:

  • immutable public action and outcome, with all prices and filled notional expressed in that outcome's coordinate;
  • execution_status: PENDING, WORKING, PARTIALLY_FILLED, FILLED, CANCELLED, EXPIRED or REJECTED;
  • pending_action: REPLACE, CANCEL or null;
  • state_quality: CONFIRMED or RECONCILIATION_REQUIRED;
  • original, current total, cumulative filled, remaining and working quantity;
  • order_version; and
  • admitted, venue-accepted, fill, terminal and update timestamps when known.

The single-order response supplies an ETag derived from order_version and accepts If-None-Match.

If state_quality=RECONCILIATION_REQUIRED, stop mutating that order until YM has restored confirmed venue truth.

Canonical REST command and list envelopes carry schema_version: 2. The single-order GET returns the bare v2 Order resource; its URL remains /v1 and it does not add a second schema envelope.

Account projections

GET /v1/executions?from=...&to=...
GET /v1/positions
GET /v1/portfolio
GET /v1/accounts/{selfAccountID}/balances

These routes are self-scoped by the bearer token. Private fill events currently report financial settlement_status=POSTED; a fill is not presented as financially complete before its approved posting path succeeds.

GET /v1/trades is a legacy account-activity projection outside Order Contract v2: side and price_cents are in the YES-book venue coordinate, not the selected-outcome v2 order coordinate. It is useful for reconciliation but is not the live v2 recovery authority. Use private-feed order.fill, whose nested order and fill price are participant- relative, for live strategy state. The legacy endpoint is deliberately omitted from the curated v2 OpenAPI.

Native post-only

Set post_only: true on NEW with GTC or DAY; IOC with post-only is rejected before admission. The flag is immutable and retained by REPLACE. DXmatch enforces native maker-or-cancel: a crossing NEW is cancelled without a fill. A crossing post-only REPLACE can cancel the existing quote. This is an effective terminal outcome, not a rejected replacement that leaves the old quote working. Observe private events and the canonical order.

Persistent command entry

Connect to wss://dev.ympredictions.com/v1/order-entry with orders:write. Validate welcome contract ym.order-entry.v1. A frame wraps the identical canonical REST command:

{"request_id":"connection-correlation-17","operation":"REPLACE","client_request_id":"durable-replace-key","order_id":"order-uuid","body":{"expected_order_version":4,"price_cents":44,"total_quantity":10}}

operation is NEW, REPLACE, CANCEL or CANCEL_ALL. NEW/CANCEL_ALL omit order_id; CANCEL has no body or {}. NEW/REPLACE bodies are the REST JSON objects; CANCEL_ALL is the optional market scope. Batch commands remain REST.

Responses have type: "command.response", your request_id, HTTP-equivalent status, the canonical response body, and optional retry_after. Concurrent responses may arrive out of order. Never reuse a correlation ID while it is in flight; the durable client_request_id is the idempotency key across REST and WebSocket. A socket close or 503 leaves outcome uncertain: retry the identical payload/key, not a newly generated command.

There are 12 normal and 4 reserved cancellation slots per connection. An inflight_limit 429 is explicitly not forwarded. Do not treat every other transport error as proof of non-execution. Keep the private feed open for effective outcomes; no execution events are multiplexed onto this command socket. See Cancellation safety for shutdown controls.

6. Private trading feed

Status: controlled pilot

Current message schema: v2

Endpoint:

wss://dev.ympredictions.com/v1/trading-feed

Required scope: trading-feed:read

The feed is participant-scoped and read-only. It reports canonical command, order, replacement, fill and terminal state regardless of whether the change originated from REST, lifecycle control or another authorized session for the same account.

Connection

Pass the access token in the WebSocket upgrade request:

Authorization: Bearer <access_token>

Participant identity is derived from the token. No user_id, account, MPID or venue-session selector is accepted.

WebSocket compression is disabled for the low-latency pilot profile. Clients must accept JSON text frames up to the max_frame_bytes announced in stream.welcome.

New session bootstrap

Without a cursor, the feed sends:

  1. stream.welcome
  2. stream.snapshot_started
  3. zero or more stream.snapshot_chunk frames
  4. stream.snapshot_completed
  5. events committed after the snapshot high-water mark
  6. stream.ready
  7. live lifecycle events and stream.heartbeat

The snapshot contains all canonical nonterminal orders for the authenticated participant. Replace local nonterminal state with the completed snapshot, then apply replayed events in order. Do not begin strategy command flow before stream.ready.

stream.welcome advertises the contract name, heartbeat interval, maximum frame size and at_least_once delivery mode. Use those values rather than hardcoding deployment defaults.

For v2 the advertised contract is Private Order & Execution Feed v2, and every lifecycle and control message carries schema_version: 2.

Resume

Each lifecycle event includes an opaque signed cursor. Persist it only after the event has been validated and durably applied locally.

Reconnect with:

wss://dev.ympredictions.com/v1/trading-feed?cursor=<URL-encoded-last-applied-cursor>

The server replays events after the supplied cursor, then sends stream.ready. A resume session does not send another account snapshot unless the client reconnects without a cursor.

The cursor is:

  • opaque;
  • signed by YM;
  • bound to the authenticated participant and stream; and
  • not a DXmatch offset or a number clients may increment.

A cursor issued by v1 is invalid for v2. Before cutover, durably consume v1 through the announced boundary and retain terminal/fill history. Then discard the v1 cursor and connect without a cursor. The v2 snapshot replaces only nonterminal order state; it does not replay v1 terminal orders or fill history.

Delivery and convergence

Delivery is at least once. A correct consumer must:

  1. Require schema_version == 2 and the expected account scope.
  2. Deduplicate lifecycle facts by event_id.
  3. Apply an order snapshot only when its order_version is newer than the locally stored version.
  4. Persist the event and its cursor atomically in the client's own store.
  5. Keep independent orders independent; do not assume one global exchange ordering across markets.

An admitted mutation may produce an admission PENDING command result and a later terminal command result. These are different facts with different event_id values but the same command.command_id; do not deduplicate them by command identity. Repeated delivery of either fact retains its event identity. Rejected-at-admission commands have only their admission result.

Apply a command result independently of the embedded order snapshot. A new terminal command outcome still matters when that snapshot's order_version is equal to or older than the locally stored order version. Once a command is terminal, a delayed admission PENDING/UNKNOWN fact must not regress it. Publication and replay may deliver these facts in a different order.

Existing retained event IDs and signed cursors remain valid; this distinction does not rewrite historical journal payloads or manufacture missing historical terminal results.

Duplicate delivery is normal recovery behavior and must not create a duplicate fill or command.

The feed is a result/state journal, not the participant's outbound request journal. Persist every submitted payload locally under client_request_id; pending replacements intentionally retain the prior effective order terms until DX confirms the mutation.

Lifecycle events

Event Meaning
order.admitted A canonical order was admitted by YM; it is not necessarily working at DXmatch.
order.working Venue evidence confirms the order is working.
order.replaced Venue evidence confirms the new single-order price/quantity is effective.
order.fill A fill and complete updated order snapshot are available.
order.cancelled Cancellation is effective.
order.expired The venue/lifecycle made the order terminal by expiry.
order.rejected The order was rejected and is terminal.
order.command_result Outcome for REPLACE or CANCEL, and for a rejected NEW; a rejected NEW may have no order. Successful NEW admission is order.admitted.

Every ordinary lifecycle event carries a complete canonical order snapshot. The sole orderless case is a schema-valid NEW rejected before an order could exist.

Relevant event fields include:

  • event_id, event_type, cursor and order_version;
  • complete order state;
  • participant-relative action, outcome, outcome price, and filled notional;
  • originating command and client-request identity where applicable;
  • participant-safe venue evidence: symbol, the participant's execution ID, shared trade/match ID when supplied, and venue event time;
  • fill price, quantity, maker/taker role and posting state; and
  • YM receive, projection and socket-send timestamps.

order.price_cents, order.filled_notional_cents, and fill.price_cents are all expressed in order.outcome. Clients never complement NO values.

Raw DXmatch replay offsets, MPID/account/session topology and the opposite participant's execution identifier are deliberately not exposed.

Control errors

The server may send stream.error and close the connection:

Code Action
INVALID_CURSOR Discard the cursor only after confirming the connection used the correct participant; reconnect without it for a fresh snapshot.
CURSOR_EXPIRED Reconnect without the cursor and rebuild nonterminal state from a fresh snapshot.
REPLAY_LIMIT_EXCEEDED Reconnect without the cursor; investigate why the client fell behind.
SLOW_CONSUMER Stop command flow, improve local consumption, then resume from last_written_cursor or the client's earlier safely applied cursor.
STREAM_ERROR Preserve the last safely applied cursor and reconnect with bounded backoff. Escalate persistent failures.

last_written_cursor means the last event written to the socket, not necessarily the last event your application applied. Prefer your own durable last-applied cursor.

Heartbeats and token rotation

Treat absence of both data and heartbeats beyond the announced interval plus a small network allowance as a disconnected session. Refresh the short-lived OAuth token out of band, open a replacement connection and resume from the last applied cursor.

Client-to-server application messages are not part of Private Feed v2. Standard WebSocket ping/pong may be used by the transport, but it does not acknowledge business events.

Forward path

The pilot feed already provides participant isolation, snapshot, replay and resume. Before contractual general availability, YM intends to complete multi-instance/high-availability evidence, retention and disaster-recovery proof, sustained slow-consumer testing and a published availability target. Future command WebSockets will share this canonical event stream rather than replace it.

7. Errors and retry policy

Status: trading-core canonical error envelope available; gateway-edge envelope and public versioned error registry in development

Canonical order errors use this shape:

{
  "code": "stale_market_generation",
  "message": "market generation is stale",
  "retryable": false,
  "command_id": null,
  "client_request_id": "strategy-a-new-42",
  "details": {}
}

The deprecated error field may duplicate message during transition. New clients should use code, message and retryable. Authentication, authorization, gateway rate-limit, and upstream-availability failures may still arrive as the transport-edge shape {"error":"..."}. Treat those as HTTP transport failures; do not infer a command outcome from that body.

HTTP policy

Status General meaning Client action
200 Read success, exact idempotent replay, or locally completed/no-op mutation. Inspect the canonical command and order.
201 A new order command was durably admitted. Wait for exchange-effective state.
202 A replace or venue-directed cancel was durably admitted. Wait for the corresponding effective event.
304 The order version matches If-None-Match. Keep the local snapshot.
400 Invalid request or unsupported contract value. Correct the request; do not blind-retry.
401 Missing, invalid or expired token. Obtain a valid token, then retry with the same command ID and payload.
403 Scope, account or policy denial. Do not retry until authorization/policy changes.
404 Resource not found in the participant scope. Reconcile identity; do not create a replacement identity blindly.
409 Current state conflicts with the command. Follow the specific code below.
429 Rate limit exceeded. Honor Retry-After; preserve command identity.
503 Required admission, reconciliation or rate-limit dependency unavailable. Follow retryable; use the same ID for an uncertain command.

Important validation codes

These require a corrected request rather than a retry:

  • missing_idempotency_key
  • invalid_idempotency_key
  • reserved_idempotency_key
  • invalid_json
  • invalid_canonical_order
  • invalid_order
  • invalid_order_id
  • invalid_replace

Identity and idempotency conflicts

Code Meaning Action
idempotency_payload_conflict The NEW key already identifies a different payload. Generate a new key only for a genuinely new command.
replace_command_conflict / cancel_command_conflict The mutation key conflicts with an existing command payload. Reconcile the original command; do not overwrite its identity.
client_order_id_conflict The participant has retained that client order ID already. Reuse it only to recover the original order; otherwise choose a new order ID.
command_already_pending Another mutation is awaiting effective venue state. Wait for the private-feed outcome, refresh the order, then decide again.

Market and quote conflicts

Code Meaning Action
stale_market_generation The command used an old admission generation. Refresh the canonical market and re-evaluate the command.
market_not_open Lifecycle no longer accepts this command. Stop sending until a later canonical state explicitly permits it.
market_temporarily_unavailable Admission/readiness is currently blocked. Refresh state; retry only if the same command remains valid.
market_quote_unavailable The protected IOC quote could not be verified and the command was terminally rejected. Refresh the book and make a new economic decision under a new ID; replaying the old ID only recovers that rejection.
market_quote_no_liquidity No executable protected liquidity exists. Wait for book change or submit a different limit command with a new ID.
market_quote_moved The protected quote changed before admission. Refresh and consciously reprice using a new command identity.
market_quote_slippage_exceeded Available execution exceeded the supplied protection. Re-evaluate price protection; never auto-widen without strategy authority.

Replace conflicts

Code Meaning Action
order_not_replaceable The order is no longer in a replaceable working state. Refresh the order.
order_version_conflict expected_order_version is stale. Refresh and decide against the latest version.
replace_quantity_filled Target total does not exceed cumulative fills. Refresh fill state and choose a valid target.
replace_noop Target price and remaining quantity are already effective. Treat as no change; do not loop.
insufficient_replace_reserve Additional collateral/exposure is unavailable. Reduce/cancel or fund the account; do not blind-retry.
short_quantity_increase_not_supported This BUY NO order's total quantity cannot currently be increased safely. Cancel it and submit a separately collateralized NEW if still intended.

Coverage and participant policy

  • insufficient_coverage means available cash or position coverage is insufficient at admission.
  • money_gate_closed, user_not_eligible and responsible_gambling_* are policy decisions, not transient venue failures.
  • order_admission_busy may be retried with bounded jitter and the same exact ID/payload.

Uncertain outcomes

command_commit_unknown is the critical recovery case: YM could not prove to the caller whether the command transaction committed. Retry the exact command with the same Idempotency-Key. Do not generate a new key.

reconciliation_required means YM will not return or mutate uncertain canonical state. Stop mutation, preserve the last confirmed state and retry the read with bounded backoff. Escalate if it persists.

For connection loss or any HTTP timeout after request transmission:

  1. Do not infer failure.
  2. Retry the exact payload with the same key.
  3. Query orders and consume the private feed.
  4. Create a new command ID only after the original outcome is known and a new economic decision is made.

If a response or feed event contains a canonical command with admission_status=REJECTED, that identity is terminal even when its transport status was 503. Replaying it recovers the same rejection; it does not rerun admission.

Backoff

Use bounded exponential backoff with jitter for retryable transport and 5xx failures. Honor Retry-After on 429. Cancellation and risk-reducing traffic should use the separately provisioned cancellation capacity but must not spin without backpressure.

Forward path

YM intends to publish a generated, versioned registry containing every external error code, retry class and remediation, then enforce it against handler tests. Until that is complete, the curated OpenAPI plus this policy define the pilot contract; unknown codes must fail safe and be reported with correlation data.

8. Recoverable market data

wss://dev.ympredictions.com/v1/market-feed?markets=<URL-encoded-comma-separated-IDs>
Authorization: Bearer <token with orders:read>

Subscribe to 1–100 distinct, published immutable market IDs. Wildcards, cursors and dynamic subscription messages are not supported. Change subscriptions by opening a new connection. An unknown or unpublished market rejects the whole handshake instead of silently omitting it.

Wire contract

Every application message has this envelope:

{"stream_id":"connection-uuid","sequence":1,"event":{"type":"stream.welcome","data":{"contract":"ym.market-data.v1","heartbeat_ms":5000,"recovery":"new_connection_snapshot","price_coordinate":"YES","markets":["market-id"]}}}

sequence starts at 1 and increases by one across all messages on that connection. It is not a market, database or DXmatch replay offset. Validate it before applying an event. Gaps, an unexpected stream ID, stale data or a broken connection invalidate the local book.

Event Client action
stream.welcome Validate contract, subscription and heartbeat.
book.snapshot Atomically replace both sides; establish generation and feed epoch.
book.delta Set side/price to absolute quantity; zero deletes the level.
book.changes Apply all level edits atomically, never a half-applied book image.
market.status Stop quoting on STALE or CLOSED.
stream.ready Initial messages queued; does not mean every market is fresh.
stream.heartbeat Connection liveness, not market freshness.

Snapshot data contains market_id, market_generation, lifecycle, state, feed_epoch, bids, asks and updated_at. Levels contain price_cents, decimal compatibility price, and integer qty. Delta data contains market_id, market_generation, side (BUY for bids / SELL for asks), price_cents, absolute quantity and observed_at. book.changes has the same market/generation/time with a changes array of level edits.

Source cadence and timestamp meaning

The shared L2 book, REST snapshots and derived best prices use coherent DX marketDepth snapshots only. Top-of-book quotes are not depth updates. Apply every book.changes message atomically, including removal of replaced levels.

DX may combine intermediate book states into its latest snapshot. Consequently, a continuous YM connection sequence does not prove that every exchange book change was delivered. Use the private trading feed for your own effective orders and fills; do not wait for a public depth update to acknowledge them. The public book is not a lossless order-flow or queue-priority feed.

observed_at is YM's processing timestamp, not the time DX changed its book. Receipt minus observed_at measures the downstream delivery leg only. Heartbeat frequency is independent of depth-update frequency. There is currently no contractual depth refresh rate or exchange-to-client latency guarantee; clients cannot request a faster upstream rate through /v1/market-feed.

Only a FRESH snapshot establishes a usable book, derived from the current DX feed epoch and catalog generation while the market is open. Stale reasons can be awaiting_snapshot, upstream_unavailable or market_unavailable; a closed market reports not_open. Recovery supplies a complete snapshot before deltas resume. Observe the catalog's close_at yourself; do not wait for a heartbeat to stop quoting after a known deadline.

Reconnect and slow consumers

There is no historical book replay. On every connection rebuild books from new snapshots. Do not combine an old REST snapshot with a new connection's deltas. Reconnecting to another server follows the same rule.

Each connection is bounded at 256 queued messages and 2 MiB. Overflow disconnects instead of silently dropping updates. Handle WebSocket pings promptly; use the advertised heartbeat and your own staleness timer. Write timeout is 2 seconds; pong/read timeout is 15 seconds. Do not send application subscription frames.

Prices and supplementary REST

Public L2 and tape use YES-book prices. A YES ask at 65 cents is liquidity against BUY YES @65 or SELL NO @35. A YES bid at 65 is liquidity against SELL YES @65 or BUY NO @35. Commands send selected-outcome prices unchanged.

GET /v1/book?market_id=...&depth=20 and /v1/markets/{id}/book provide snapshots (depth at most 200). Their projection seq is not this stream's sequence. /v1/markets/{id}/trades?limit=200 returns recent public trades; /stats returns analytics. Streaming public tape is not offered by this contract; private fills come from the private feed, not L2 inference.

Anchors are reference signals, not executable prices. Retail /v1/ws is not the professional recovery contract. This feed fans out existing in-memory venue-derived state with bounded, nonblocking queues, without per-update database reads or journal writes.

9. Limits and operating envelope

DEV qualification is not a production SLA or unlimited-capacity promise. Obtain the environment's approved rate, market scope and contact before starting a strategy. Nothing here authorizes production trading or bypasses collateral, KYC, risk or account controls.

Resource Protocol limit
Idempotency key / client order ID 200 characters; ym: request prefix reserved
Batch NEW / CANCEL 250 items; independent outcomes
Orders / canonical market page 500 items
Execution page / time window 500 fills / 31 days
Ledger read 100 exact entry IDs; 10,000 returned postings
REST book depth / public trades 200 per side / 200 trades
Book subscriptions 100 markets per connection
Book connections 4 per account, 64 per market-data process
Command connections 4 per account, 64 per gateway process
Command socket in flight 12 ordinary plus 4 reserved cancellation slots per connection
Command frame / response 16 KiB / 64 KiB
Book outgoing queue 256 messages and 2 MiB; disconnect on overflow
Manual cancel cohort 10,000 orders; split larger scopes by market
Dead-man timeout 10–120 seconds; large expiry cohorts split into bounded groups
Private snapshot chunk Up to 100 orders within advertised frame bound

Process-local limits are not cluster-wide entitlements. Independent socket slots do not make same-order mutations independent: follow order versions and pending-action rules.

Current DEV professional capacity is 3,300 units/minute, with a separate cancellation bucket at the same ceiling. Lease renew/disarm uses that control bucket. Reads consume the ordinary profile. Every command frame is authenticated and charged. A single request/frame costs one unit; a batch costs one per item, including invalid or replayed items. A rate-rejected batch is not forwarded. Honor 429 and Retry-After; reserve capacity for renewal, reconciliation and cancellation rather than spending it all on new quotes.

Failure handling and measurement

Stop quoting on stale books, lost private recovery, unrenewed leases, restricted identity or uncertain command state. Reconcile; do not retry uncertain commands with new keys. See Errors and retries.

Disconnection does not imply cancellation. Use the lease or scope-cancel control and verify completion. During a database, venue or network outage, cancellation can be delayed; durable work survives restart and completion still requires terminal evidence.

Measure admission and exchange-effective private-feed latency separately. Retain every run, warmup, error, timeout and unsubmitted command; report p50/p99/max and counts above fixed thresholds. Published capacity requires an agreed mix of commands, fills, accounts, subscriptions and recovery scenarios.

FIX, atomic quote sets, shared multi-strategy subaccount delegation and contractual HA/SLO terms are not offered. Do not infer them from an endpoint or a short successful benchmark.

10. Cancellation and heartbeat safety

Account or market cancellation

POST /v1/orders/cancel-all
Authorization: Bearer <orders:write token>
Idempotency-Key: <durably retained unique key>
Content-Type: application/json

{"market_id":"optional-immutable-market-id"}

Use {} for all this account's markets. The operation captures an immutable cohort under the account's admission fence and durably requests terminal cancellation. A 202 response supplies request_id, total_orders, remaining_orders and PENDING or COMPLETE.

Poll GET /v1/cancellations/{request_id} with orders:read until COMPLETE. This requires terminal order evidence and no open hold/provisional reservation for that cohort. It does not prevent future orders: stop your producers before using it for strategy shutdown.

Retrying the original key returns the original cohort, never later quotes. Changing scope under that key returns 409. Another account cannot access this request. More than 10,000 orders returns 422; use separate market scopes. Uncertain replacements are reconciled through venue request lookup, not blindly replayed before terminal cancellation.

Opt-in heartbeat lease

cancel-on-disconnect means a missed application heartbeat, not immediate TCP close handling. It works with REST or WebSocket commands. One lease covers the entire financial account.

Arm with PUT /v1/cancel-on-disconnect (orders:write):

{"lease_id":"new-nonzero-uuid","timeout_seconds":30}

Wait for ARMED and record server expires_at. Renew the same ID and timeout, for example every 10 seconds for a 30-second lease. Stop quoting if renewal cannot be confirmed. Allow for network jitter and token renewal when choosing your timeout.

GET /v1/cancel-on-disconnect (orders:read) returns current state. After expiry, FIRED and cancellation_ids identify durable cancellation groups. Poll every group's /v1/cancellations/{id} to completion. FIRED does not claim the matching engine has already cancelled everything.

Expiry captures the current cohort atomically and splits it into groups of at most 10,000. Orders admitted after capture are not included. This is not an account freeze: all producers must honor the heartbeat-failure stop rule. Startup recovers expired leases; missing external proof keeps work pending.

Disarm and rearm

DELETE /v1/cancel-on-disconnect with {"lease_id":"current-id"} disarms an unexpired lease; it does not cancel orders. After expiry, late renewals and disarms return 409 and cannot undo cancellation.

To rearm, use a new UUID and previous_lease_id equal to the last lease ID. Rearm is refused while the previous lease is armed or cancellation incomplete. Old delayed requests cannot revive an old epoch or replace a new one. Changing timeout requires disarm and a new epoch, not a heartbeat mutation.

11. Executions, fees and ledger reconciliation

The private feed is the live recovery authority. Bounded REST reads support independent reconciliation; none is a second matching or settlement engine.

Fee policy

GET /v1/account/trading-fees (orders:read) returns currency, server as_of and new_order_fee_policy (id, rate_bps, rebate_bps). Policy is pinned at NEW; replacement does not silently change it.

Maker executions have zero execution fee. Taker fees use the symmetric YES-price curve, cumulatively per taker order:

round_half_up(sum(quantity × rate_bps × price_cents × (100-price_cents)) / 1,000,000)

Each fill posts the increase in this rounded cumulative amount. Do not round fills independently. For 100 contracts at 50 cents with rate 500 bps, the fee is 125 cents, not 5% of the 5,000-cent purchase notional. Read the policy; do not hardcode today's configuration.

Posted executions

GET /v1/executions?from=<RFC3339>&to=<RFC3339>&limit=100

Required scope: orders:read. Supply a fixed half-open posting-time window [from,to) of at most 31 days, ending no later than now. Follow next_cursor without changing the window. Page size is at most 500. Deduplicate by fill_id, a string preserving 64-bit precision.

Items contain your trade/order/client-order/market IDs, selected action/outcome/ price, quantity, liquidity role, fee, paired trade fee-policy ID, ledger_entry_id and posted_at. No counterparty identity is returned. The policy ID is the trade's taker policy, not a claim that the maker paid a fee.

Pages expose committed data, not a frozen multi-page snapshot. Re-read recent overlapping windows after reconnect and deduplicate; a transaction can commit after a page was read. posted_at is database posting time, not exchange time. Missing canonical or ledger prerequisites return unavailable, not a silently incomplete fill.

Ledger evidence and balances

GET /v1/account/ledger?entry_ids=<comma-separated-UUIDs>
GET /v1/accounts/{your-account-id}/balances

Required scope: balances:read. Ledger reads return only your postings for 1–100 exact entry IDs from executions or rebates. Interpret signed movements in the named account, not one sum treated as spendable cash. Balance reads are the spendable/reserved balance authority.

A settlement entry balances across all participants and system accounts. Your visible subset need not sum to zero; opposing postings are never exposed. This is not an unbounded full-history export.

Rebates

GET /v1/account/rebates?market_id=<id>&policy_id=<id>&book_day=YYYY-MM-DD

Required scope: balances:read. Rebates are allocated from a funded market/policy/book-day pool at operator close, using the implemented weighting rules. Book days use Europe/Malta; rebates are not immediate per-fill cash.

CREDITED returns the actual allocation and immutable ledger entry. NOT_CREDITED has no cash amount: it is not a zero-entitlement promise, withdrawable pending balance or forecast. Reconcile credited entries against your own ledger postings and balance movement.

12. Onboarding and operational handoff

One approved integration maps to one confidential OAuth client, one immutable service-account subject and one dedicated financial account. Separate firms must not share credentials. Multiple processes may share an account only with coordinated order ownership, account limits and one heartbeat lease owner.

Operator checklist

  1. Record the participant, environment, approved responsible contact, economic terms, allowed usage and approval reference. Do not invent approvals or classify a real participant as a synthetic test identity.
  2. Create a least-privilege confidential client with the supplied operator utility. It disables browser/password/implicit grants, enables service accounts, restricts scope mapping and supplies the YM audience. Its returned account ID is the service-account subject; never substitute an email or another participant's ID.
  3. Establish that dedicated financial identity using the normal approved account controls. Record classification, legal/identity and risk evidence, and limits. Client creation alone neither approves KYC nor enables trading, creates funds, waives RG checks or rewrites another user's identity.
  4. For DEV UAT, use an explicitly classified synthetic TEST_PLAYER and balanced test funding. For real participants, complete the applicable identity, legal and funding process; test-fixture provisioning is not that process.
  5. Hand over environment URLs, account/client IDs, granted scopes, supported markets, rate profile, fee/rebate policy, credential delivery channel and operational contact. Deliver the secret separately through a secret manager.
  6. Run the acceptance sequence below; retain exact revision and results. Do not approve production use from DEV documentation or an unverified code branch.

Operator utility

The operator utilities and reference clients belong to the qualified DEV release, not the docs-only publication on main. YM supplies the matching version during onboarding. From that release's Backend/scripts/market-maker, review the plan first:

node onboarding.cjs plan ym-mm-firm-strategy approval-reference

An authorized operator supplies MM_AUTH_ORIGIN, MM_ADMIN_TOKEN and MM_SECRET_OUTPUT through protected process configuration. The utility is intentionally DEV-only in this qualification release. create provisions the client and writes its secret to a new owner-only file; stdout contains only the nonsecret manifest. It refuses to overwrite an existing client or secret file. rotate issues a new secret; disable blocks new token issuance. It does not automatically alter financial identities, activate trading or fund an account.

node onboarding.cjs create ym-mm-firm-strategy approval-reference
node onboarding.cjs rotate ym-mm-firm-strategy
node onboarding.cjs disable ym-mm-firm-strategy

Participant UAT acceptance

The read-only doctor.cjs checks token/account binding, reads and all three WebSocket handshakes without submitting commands. Set YM_BASE_URL, YM_ISSUER, YM_CLIENT_ID, YM_CLIENT_SECRET, YM_ACCOUNT_ID and YM_MARKETS (comma-separated approved market IDs), then run node doctor.cjs. Its output contains no credentials. This is a smoke check, not the complete acceptance sequence. book.cjs is a tested reference consumer for sequence validation, absolute quantities and atomic book changes.

  • Acquire/renew tokens; verify wrong-secret and missing-scope rejection and that another account's reads/cursor cannot be used.
  • Discover the approved canonical markets and generations; establish a fresh book and completed private snapshot/replay before commands.
  • NEW → working → REPLACE → effective → CANCEL → terminal/released. Replay exact keys after an uncertain response; changed payloads must not duplicate.
  • Verify YES and NO price coordinates, partial/full fills and cumulative fees; reconcile posted executions, own ledger movements and current balances.
  • Test noncrossing and crossing post-only, including replacement cancellation.
  • Test mixed batch outcomes and per-item retry, not only HTTP status 200.
  • Disconnect/reconnect public and private feeds, reject sequence gaps, recover snapshots/cursors, and stop quoting while freshness is unavailable.
  • Prove scope cancellation excludes other accounts/markets/later orders. Miss a lease heartbeat, verify every captured order terminal and holds released, then rearm explicitly. A later heartbeat must not undo expiry.
  • Rotate a disposable client's secret; old issuance must fail, new issuance and token scopes must work. Separately demonstrate financial containment.
  • Measure latency with the intended connections, renewal and reconciliation workload; retain failures and threshold counts, not only the best run.

Emergency containment and offboarding

Stop quote producers, freeze/restrict the financial account with the existing audited operator control, request terminal cancellation and verify orders and holds. Disable OAuth issuance and revoke sessions as applicable. Rotation or client disable alone is not instantaneous invalidation of cached bearer tokens. Retain immutable financial records; never delete a traded account as cleanup.

Support handoff must include an actual reachable contact and an agreed response process. This document does not manufacture a staffed 24/7 desk or contractual SLA. Include environment, UTC timestamps, request/command IDs, stream/cursor context and sanitized errors in incidents; never include secrets.

Endpoint reference

39 operations. Expand an endpoint for parameters, authentication and exact request/response shapes. Component references resolve in the schema section and downloadable OpenAPI.

GET/v1/categoriesCanonical top-level prediction-market categories

Canonical top-level prediction-market categories

Scopes: public

Request and response contract
responses:
  "200":
    description: Public canonical categories from the active taxonomy registry
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalTaxonomyListResponse"
GET/v1/taxonomyCanonical public taxonomy hierarchy

Canonical public taxonomy hierarchy

Scopes: public

Request and response contract
responses:
  "200":
    description: Public taxonomy nodes with explicit parent references
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalTaxonomyListResponse"
GET/v1/tagsOrthogonal public event-discovery tags

Orthogonal public event-discovery tags

Scopes: public

NameLocationRequiredContract
categoryqueryno{"type":"string"}
qqueryno{"type":"string"}
limitqueryno{"type":"integer","minimum":1,"maximum":500}
Request and response contract
responses:
  "200":
    description: Tags; tags are facets and never semantic parents
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalTagListResponse"
GET/v1/seriesCanonical recurring series nodes

Canonical recurring series nodes

Scopes: public

Request and response contract
responses:
  "200":
    description: Public series nodes from the active taxonomy registry
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalTaxonomyListResponse"
GET/v1/sportsCanonical sports taxonomy nodes

Canonical sports taxonomy nodes

Scopes: public

Request and response contract
responses:
  "200":
    description: Public sport nodes from the active taxonomy registry
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalTaxonomyListResponse"
GET/v1/competitionsCanonical sports competitions

Canonical sports competitions

Scopes: public

NameLocationRequiredContract
sportqueryno{"type":"string"}
qqueryno{"type":"string"}
limitqueryno{"type":"integer","minimum":1,"maximum":500}
Request and response contract
responses:
  "200":
    description: Public competitions with immutable entity ids and parent references
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCompetitionListResponse"
GET/v1/eventsCanonical event-first market discovery

The recommended discovery entry point. Results use immutable YM ids, human slugs, explicit parent references, public revisions, and opaque keyset cursors. Offset pagination is deliberately unsupported.

Scopes: public

NameLocationRequiredContract
categoryquerynoComma-separated canonical category keys.
sportqueryno{"type":"string"}
competitionqueryno{"type":"string"}
seriesqueryno{"type":"string"}
cyclequeryno{"type":"string"}
phasequeryno{"type":"string"}
tagquerynoComma-separated tag ids or slugs.
statusquerynoComma-separated lifecycle statuses or public aliases (`active`,`pending`,`resolved`).
qqueryno{"type":"string"}
start_minqueryno{"type":"string","format":"date-time"}
start_maxqueryno{"type":"string","format":"date-time"}
close_minqueryno{"type":"string","format":"date-time"}
close_maxqueryno{"type":"string","format":"date-time"}
includequerynoSupports `families`, `families.markets`, and explicit `external_mappings`.
cursorquerynoOpaque `next_cursor` from the preceding page.
limitqueryno{"type":"integer","minimum":1,"maximum":200}
Request and response contract
responses:
  "200":
    description: Canonical event page
    headers:
      ETag:
        schema:
          type: string
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalEventListResponse"
  "304":
    description: Representation matches `If-None-Match`.
  "400":
    description: Invalid filter, include, timestamp, status, cursor, or unsupported offset.
GET/v1/events/{eventID}Canonical event detail by immutable id, current slug, or permanent alias

Canonical event detail by immutable id, current slug, or permanent alias

Scopes: public

NameLocationRequiredContract
eventIDpathyes{"type":"string"}
includequerynoSupports `families`, `families.markets`, and explicit `external_mappings`.
Request and response contract
responses:
  "200":
    description: Canonical event detail. `Content-Location` contains the current
      canonical route.
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalEventResponse"
  "404":
    description: Event not found.
GET/v1/events/{eventID}/market-familiesCanonical market families nested under an event

Canonical market families nested under an event

Scopes: public

NameLocationRequiredContract
eventIDpathyes{"type":"string"}
includequerynoSupports `families.markets` and explicit `external_mappings`.
Request and response contract
responses:
  "200":
    description: Event-scoped canonical market families
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalFamilyListResponse"
  "404":
    description: Event not found.
GET/v1/market-familiesGrouped market discovery with legacy or canonical hierarchy view

Grouped market discovery with legacy or canonical hierarchy view

Scopes: public

NameLocationRequiredContract
viewquerynoUse `canonical` for immutable identity, revisions, and canonical routes. Omission preserves the legacy response during migration.
eventquerynoCanonical view only; filter by immutable event id, slug, or permanent alias.
includequerynoCanonical view only; supports `families.markets` and `external_mappings`.
cursorquerynoCanonical view only; opaque keyset cursor. Offset pagination is unsupported.
statusquerynoOptional comma-separated family statuses (`active`,`resolved`,`pending`)
categoryquerynoOptional comma-separated lower-case category filter
entity_idquerynoOptional canonical sports entity id filter
entity_slugquerynoOptional canonical sports entity slug filter
limitqueryno{"type":"integer","minimum":1,"maximum":500}
Request and response contract
responses:
  "200":
    description: Market families
    content:
      application/json:
        schema:
          oneOf:
            - $ref: "#/components/schemas/MarketFamilyListResponse"
            - $ref: "#/components/schemas/CanonicalDirectFamilyListResponse"
  "400":
    description: Invalid canonical filter, include, status, cursor, or unsupported offset.
GET/v1/market-families/{familyID}Market family detail with legacy or canonical hierarchy view

Market family detail with legacy or canonical hierarchy view

Scopes: public

NameLocationRequiredContract
familyIDpathyes{"type":"string"}
viewqueryno{"type":"string","enum":["legacy","canonical"]}
includequerynoCanonical view only; provider ids require explicit `external_mappings`.
Request and response contract
responses:
  "200":
    description: Market family detail
    content:
      application/json:
        schema:
          oneOf:
            - $ref: "#/components/schemas/MarketFamilyResponse"
            - $ref: "#/components/schemas/CanonicalFamilyResponse"
  "400":
    description: Ambiguous family slug or invalid canonical include.
  "404":
    description: Market family not found
GET/v1/marketsPublic market catalog with legacy quote view or canonical hierarchy view

Public market catalog with legacy quote view or canonical hierarchy view

Scopes: public

NameLocationRequiredContract
viewquerynoUse `canonical` for immutable event/family identity, revision, and canonical-route fields. Omission preserves the legacy response during migration.
eventquerynoCanonical view only; immutable event id, current slug, or permanent event alias.
familyquerynoCanonical view only; immutable family id or an event-scoped family slug/alias.
includequerynoCanonical view only; comma-separated expansion list. Provider ids require explicit `external_mappings`.
cursorquerynoCanonical view only; opaque keyset cursor. Offset pagination is unsupported.
statusquerynoOptional comma-separated lifecycle filter. Supports raw backend statuses and frontend aliases (`active`,`pending`,`resolved`).
categoryquerynoOptional comma-separated lower-case category filter.
countryquerynoOptional comma-separated lower-case country filter.
limitqueryno{"type":"integer","minimum":1,"maximum":500}
Request and response contract
responses:
  "200":
    description: Markets
    content:
      application/json:
        schema:
          oneOf:
            - $ref: "#/components/schemas/LegacyMarketListResponse"
            - $ref: "#/components/schemas/CanonicalMarketListResponse"
  "400":
    description: Invalid canonical filter, include, status, cursor, or unsupported offset.
GET/v1/markets/{marketID}Public market detail with legacy quote or canonical hierarchy fields

Public market detail with legacy quote or canonical hierarchy fields

Scopes: public

NameLocationRequiredContract
marketIDpathyes{"type":"string"}
viewquerynoUse `canonical` for immutable event/family identity, revision, and canonical URL.
includequerynoCanonical view only; provider ids require explicit `external_mappings`.
Request and response contract
responses:
  "200":
    description: Market detail
    content:
      application/json:
        schema:
          oneOf:
            - $ref: "#/components/schemas/LegacyMarket"
            - $ref: "#/components/schemas/CanonicalMarketResponse"
  "400":
    description: Invalid canonical include.
  "404":
    description: Market not found
GET/v1/markets/{marketID}/bookL2 book snapshot

L2 book snapshot

Scopes: public

NameLocationRequiredContract
marketIDpathyes{"type":"string"}
depthquerynoMaximum price levels returned per side. Values above 200 are capped at 200.
Request and response contract
responses:
  "200":
    description: Book
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/BookSnapshot"
GET/v1/bookBook snapshot by query params

Book snapshot by query params

Scopes: public

NameLocationRequiredContract
market_idqueryyes{"type":"string"}
depthquerynoMaximum price levels returned per side. Values above 200 are capped at 200.
Request and response contract
responses:
  "200":
    description: Book
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/BookSnapshot"
GET/v1/markets/{marketID}/statsMarket statistics derived from settled fills and open holds

Market statistics derived from settled fills and open holds

Scopes: public

NameLocationRequiredContract
marketIDpathyes{"type":"string"}
Request and response contract
responses:
  "200":
    description: Market stats
    content:
      application/json:
        schema:
          type: object
          required:
            - market_id
            - volume_24h
            - total_volume
            - traders_count_24h
            - trades_count_24h
            - avg_position_24h
            - open_interest
          properties:
            market_id:
              type: string
            volume_24h:
              type: number
            total_volume:
              type: number
            traders_count_24h:
              type: integer
            trades_count_24h:
              type: integer
            avg_position_24h:
              type: number
            open_interest:
              type: number
GET/v1/markets/{marketID}/tradesRecent taker-side market trades

Recent taker-side market trades

Scopes: public

NameLocationRequiredContract
marketIDpathyes{"type":"string"}
limitqueryno{"type":"integer","minimum":1,"maximum":200}
Request and response contract
responses:
  "200":
    description: Recent trades
    content:
      application/json:
        schema:
          type: object
          required:
            - market_id
            - count
            - trades
          properties:
            market_id:
              type: string
            count:
              type: integer
            trades:
              type: array
              items:
                type: object
                required:
                  - trade_id
                  - market_id
                  - user_id
                  - username
                  - counterparty_user_id
                  - side
                  - liquidity_role
                  - price
                  - price_cents
                  - quantity
                  - occurred_at
                properties:
                  trade_id:
                    type: string
                  market_id:
                    type: string
                  user_id:
                    type: string
                    description: Pseudonymous participant identifier
                  username:
                    type: string
                    description: Pseudonymous display identifier
                  counterparty_user_id:
                    type: string
                    description: Pseudonymous participant identifier for the opposite side
                  side:
                    type: string
                    enum:
                      - BUY
                      - SELL
                  liquidity_role:
                    type: string
                    enum:
                      - TAKER
                      - MAKER
                  price:
                    type: number
                  price_cents:
                    type: integer
                  quantity:
                    type: integer
                  occurred_at:
                    type: string
                    format: date-time
GET/v1/ordersList the authenticated participant's canonical orders

List the authenticated participant's canonical orders

Scopes: orders:read

NameLocationRequiredContract
market_idqueryno{"type":"string"}
execution_statusqueryno{"type":"string","enum":["PENDING","WORKING","PARTIALLY_FILLED","FILLED","CANCELLED","EXPIRED","REJECTED"]}
pending_actionqueryno{"type":"string","enum":["NONE","REPLACE","CANCEL"]}
state_qualityqueryno{"type":"string","enum":["CONFIRMED","RECONCILIATION_REQUIRED"]}
cursorquerynoOpaque newest-first keyset cursor returned by the prior page.
limitqueryno{"type":"integer","minimum":1,"maximum":500}
Request and response contract
responses:
  "200":
    description: Stable newest-first canonical order page
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderListResponse"
  "400":
    description: Invalid canonical filter or cursor
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
  "503":
    description: One or more selected orders require reconciliation before canonical
      read cutover
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
POST/v1/ordersAdmit a canonical order command

Admit a canonical order command

Scopes: orders:write

NameLocationRequiredContract
Idempotency-KeyheaderyesParticipant-scoped command identity; the `ym:` prefix is reserved for internal commands.
Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        $ref: "#/components/schemas/CreateOrderRequest"
responses:
  "200":
    description: Exact scoped idempotent replay with the original command result and
      current order snapshot
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
  "201":
    description: Command admitted atomically with a PENDING canonical order
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
        example:
          schema_version: 2
          command:
            command_id: 9b1b889c-2fd3-46d6-bc55-441afc7a7cc8
            order_id: 3bdb9f66-402e-4f06-9bc7-74026c004317
            command_sequence: 1
            command_type: NEW
            client_request_id: mm-new-no-000001
            client_order_id: strategy-no-000001
            admission_status: ADMITTED
            outcome_status: PENDING
            admitted_at: 2026-09-02T10:00:00Z
            created_at: 2026-09-02T10:00:00Z
            updated_at: 2026-09-02T10:00:00Z
          order:
            id: 3bdb9f66-402e-4f06-9bc7-74026c004317
            created_by_command_id: 9b1b889c-2fd3-46d6-bc55-441afc7a7cc8
            client_order_id: strategy-no-000001
            market_id: example-market
            market_generation: 7
            action: BUY
            outcome: NO
            price_cents: 35
            original_quantity: 10
            total_quantity: 10
            cumulative_filled_quantity: 0
            remaining_unfilled_quantity: 10
            working_quantity: 0
            filled_notional_cents: 0
            execution_status: PENDING
            pending_action: null
            state_quality: CONFIRMED
            order_version: 1
            is_terminal: false
            is_working: false
            may_still_execute: true
            has_reserved_exposure: true
            time_in_force: GTC
            admitted_at: 2026-09-02T10:00:00Z
            updated_at: 2026-09-02T10:00:00Z
          idempotent_replay: false
  "400":
    description: Invalid command
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
  "409":
    description: Stale generation, idempotency conflict, command conflict, or
      unavailable market state
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
  "503":
    description: Temporarily unavailable admission dependency
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
GET/v1/orders/{orderID}Get order

Get order

Scopes: orders:read

NameLocationRequiredContract
orderIDpathyes{"type":"string","format":"uuid"}
Request and response contract
responses:
  "200":
    description: Current authoritative canonical order snapshot
    headers:
      ETag:
        description: Derived from order_version; accepted by If-None-Match.
        schema:
          type: string
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/Order"
  "304":
    description: The supplied ETag matches the current order version
  "503":
    description: The order requires canonical reconciliation
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
POST/v1/orders/{orderID}/replaceAdmit a canonical single-order replacement command

Admit a canonical single-order replacement command

Scopes: orders:write

NameLocationRequiredContract
Idempotency-Keyheaderyes{"type":"string","maxLength":200,"pattern":"^(?![Yy][Mm]:).+
quot;}
orderIDpathyes{"type":"string","format":"uuid"}
Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        $ref: "#/components/schemas/ReplaceOrderRequest"
responses:
  "200":
    description: Exact idempotent replay of the original replacement result
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
  "202":
    description: Replacement admitted; the prior price and quantity remain effective
      until DX confirms REPLACED
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
  "409":
    description: Version conflict, no-op replacement, insufficient exposure, or
      another mutation is pending
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
POST/v1/orders/{orderID}/cancelAdmit a canonical cancel command

Admit a canonical cancel command

Scopes: orders:write

NameLocationRequiredContract
Idempotency-KeyheaderyesParticipant-scoped command identity; the `ym:` prefix is reserved for internal commands.
orderIDpathyes{"type":"string","format":"uuid"}
Request and response contract
responses:
  "200":
    description: Cancel is already effective locally or is an idempotent
      completed/no-op result
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
  "202":
    description: Venue-directed cancel admitted; order truth retains its prior
      execution status with pending_action=CANCEL
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalCommandOrderResponse"
  "409":
    description: Idempotency conflict or another mutation command is already pending
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
  "503":
    description: Order requires reconciliation before a safe cancel decision
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CanonicalOrderError"
POST/v1/orders/batchCreate multiple limit orders

DEV-qualified per-item NEW admission and exact replay. Up to 250 independent outcomes; not an atomic quote set. Each item consumes one rate unit, including rejected and replayed items.

Scopes: orders:write

Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        required:
          - orders
        properties:
          orders:
            type: array
            minItems: 1
            maxItems: 250
            items:
              $ref: "#/components/schemas/CreateOrderBatchItem"
responses:
  "200":
    description: Per-item batch result
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/OrderBatchResponse"
POST/v1/orders/cancel-batchCancel multiple existing orders

DEV-qualified per-item CANCEL admission and exact replay. Up to 250 independent outcomes; inspect every result. Each item consumes one cancellation rate unit, including rejected and replayed items.

Scopes: orders:write

Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        required:
          - orders
        properties:
          orders:
            type: array
            minItems: 1
            maxItems: 250
            items:
              type: object
              required:
                - order_id
                - client_request_id
              properties:
                order_id:
                  type: string
                  format: uuid
                client_request_id:
                  type: string
                  maxLength: 200
                  pattern: ^(?![Yy][Mm]:).+$
responses:
  "200":
    description: Per-item batch result
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/OrderBatchResponse"
POST/v1/orders/cancel-allDurably cancel a fixed cohort of this account's outstanding orders

Omitting market_id selects the whole authenticated account. The request captures at most 10000 order IDs; retries never include newer orders. HTTP 202 means durable admission, not completed cancellation. Fills that win the race remain valid. Read the cancellation resource until COMPLETE, and reconcile executions through the private trading feed. This is not an account freeze or a liquidation of filled positions.

Scopes: orders:write

NameLocationRequiredContract
Idempotency-Keyheaderyes{"type":"string","minLength":1,"maxLength":200}
Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        additionalProperties: false
        properties:
          market_id:
            type: string
            maxLength: 500
responses:
  "202":
    description: Durable cancellation admitted or replayed
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CancellationStatus"
  "409":
    description: The same key was used with a different market scope
  "422":
    description: Scope exceeds 10000 orders; divide by market
  "503":
    description: Retry the identical request and key; do not infer cancellation
GET/v1/cancellations/{requestID}Read this account's cancellation progress

Read this account's cancellation progress

Scopes: orders:read

NameLocationRequiredContract
requestIDpathyes{"type":"string","format":"uuid"}
Request and response contract
responses:
  "200":
    description: COMPLETE requires terminal state and released unmatched collateral
      for every captured order
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CancellationStatus"
  "404":
    description: Unknown cancellation or a different account's request
GET/v1/cancel-on-disconnectRead the authenticated account's opt-in heartbeat lease

Read the authenticated account's opt-in heartbeat lease

Scopes: orders:read

Request and response contract
responses:
  "200":
    description: ARMED is a deadline, FIRED means cancellation admitted, not yet
      necessarily complete
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CancellationLease"
  "404":
    description: No lease has been armed
PUT/v1/cancel-on-disconnectArm or renew an account-wide dead-man cancellation lease

Opt-in and independent of transport. Renew before expiry with the same lease_id and timeout. Missing heartbeats trigger durable cancellation of outstanding orders, not liquidation or permanent account freezing. Heartbeats do not place or replace orders. After expiry, wait for the every resource in cancellation_ids to be COMPLETE, then use a new lease_id and set previous_lease_id to the current lease_id from GET. Delayed old heartbeats cannot revive an expired or disarmed epoch. Close sockets alone does not disarm it.

Scopes: orders:write

Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        additionalProperties: false
        required:
          - lease_id
          - timeout_seconds
        properties:
          lease_id:
            type: string
            format: uuid
          previous_lease_id:
            type: string
            format: uuid
          timeout_seconds:
            type: integer
            minimum: 10
            maximum: 120
responses:
  "200":
    description: Committed current lease; start quoting only after this acknowledgement
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CancellationLease"
  "409":
    description: Expired
    disarmed: null
    changed epoch or pending prior cancellation; read state: null
  "503":
    description: Outcome unconfirmed; read state and stop quoting until protection confirmed
DELETE/v1/cancel-on-disconnectExplicitly disarm a still-current unexpired heartbeat lease

Explicitly disarm a still-current unexpired heartbeat lease

Scopes: orders:write

Request and response contract
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        additionalProperties: false
        required:
          - lease_id
        properties:
          lease_id:
            type: string
            format: uuid
responses:
  "200":
    description: Disarmed; remaining orders are NOT cancelled by disarm
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CancellationLease"
  "409":
    description: Stale lease identity or already expired; cancellation cannot be revoked
GET/v1/order-entryPersistent authenticated NEW, REPLACE, CANCEL and CANCEL_ALL command transport

WebSocket contract ym.order-entry.v1. Send text JSON with request_id, operation, client_request_id, optional order_id and body matching the corresponding REST request. Each command.response contains request_id, the REST status and unmodified JSON body. Responses may arrive out of order. Admission is not execution: consume /v1/trading-feed separately. The same per-command scope, revocation and rate rules apply as REST. Limits per connection are 12 concurrent NEW/REPLACE and 4 reserved cancellation requests; no queue. Maximum input frame is 16 KiB. Slow readers disconnect. Reconnect on token expiry; retry unknown outcomes with identical client_request_id and payload. No automatic replay.

Scopes: orders:write

Request and response contract
responses:
  "101":
    description: WebSocket connected; first frame is stream.welcome
  "400":
    description: Query parameters are not supported
  "401":
    description: Invalid or missing authentication
  "403":
    description: Missing scope or disallowed browser origin
  "429":
    description: Connection limit or handshake rate limit
GET/v1/market-feedProfessional order-book snapshots and incremental updates

Professional order-book snapshots and incremental updates

Scopes: orders:read

NameLocationRequiredContract
marketsqueryyesComma-separated immutable market IDs; 1 to 100 distinct IDs, no wildcard.
Request and response contract
responses:
  "101":
    description: Authenticated WebSocket. Every connection starts with snapshots;
      reconnect replaces all local books.
  "400":
    description: Invalid or unsupported subscription parameters.
  "401":
    description: Authentication required.
  "403":
    description: Insufficient scope or disallowed browser origin.
  "404":
    description: Unknown market.
  "429":
    description: Connection or request limit exceeded.
GET/v1/trading-feedOpen the resumable private order and execution WebSocket feed

This endpoint serves Private Order & Execution Feed v2. A v1 cursor cannot resume v2; after durably completing v1 through the announced boundary, begin with a cursorless snapshot. That snapshot replaces only nonterminal state and does not replay v1 terminal orders or fills. The gateway authenticates the bearer token and signs participant identity to the dedicated private-trading-feed service. Identity is never selected by query string. Without a cursor the stream sends a consistent nonterminal snapshot before live events. With a cursor it resumes at-least-once delivery after the last client-applied event. WebSocket compression is disabled for this low-latency profile. The route is absent until PRIVATE_TRADING_FEED_ENABLED is explicitly enabled after the documented durability, replica, retention, and canonical-readiness rollout gates pass.

Scopes: trading-feed:read

NameLocationRequiredContract
cursorquerynoOpaque signed last-applied cursor from this participant's prior session.
Request and response contract
responses:
  "101":
    description: WebSocket protocol switch; message schemas are documented by
      x-private-trading-feed below.
  "401":
    description: Missing or invalid bearer authentication
  "403":
    description: Missing trading-feed scope or disallowed browser Origin
  "503":
    description: Private feed unavailable
GET/v1/executionsPaginated financially posted fills in the order's YES/NO coordinate

Self-scoped, bounded to a 31-day window and 500 fills per page. Ordered by posting timestamp and fill ID descending. Pass next_cursor with exactly the same from/to window. No counterparty identity is returned. Each page sees committed data, not a cross-page database snapshot; re-read recent overlapping windows and deduplicate fill_id to cover in-flight commits. This is reconciliation history, not an ordered private-feed replacement.

Scopes: orders:read

NameLocationRequiredContract
fromqueryyes{"type":"string","format":"date-time"}
toqueryyes{"type":"string","format":"date-time"}
limitqueryno{"type":"integer","minimum":1,"maximum":500,"default":100}
cursorqueryno{"type":"string"}
Request and response contract
responses:
  "200":
    description: Posted fills; empty next_cursor means no further currently visible page
    content:
      application/json:
        schema:
          type: object
          required:
            - schema_version
            - user_id
            - from
            - to
            - executions
            - next_cursor
          properties:
            schema_version:
              const: 2
            user_id:
              type: string
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time
            next_cursor:
              type: string
            consistency:
              type: string
            executions:
              type: array
              items:
                $ref: "#/components/schemas/PostedExecution"
  "400":
    description: Invalid window
    cursor: null
    limit or query parameter: null
  "503":
    description: Financial or canonical projection incomplete; not an empty
      successful history
GET/v1/account/trading-feesCurrent new-order fee policy and account book currency

Current new-order fee policy and account book currency

Scopes: orders:read

Request and response contract
responses:
  "200":
    description: Existing orders retain their admitted policy; no new fee is charged
      by this read
    content:
      application/json:
        schema:
          type: object
          required:
            - user_id
            - currency
            - as_of
            - new_order_fee_policy
            - policy_binding
          properties:
            user_id:
              type: string
            currency:
              type: string
            as_of:
              type: string
              format: date-time
            new_order_fee_policy:
              type: object
              required:
                - id
                - rate_bps
                - rebate_bps
              properties:
                id:
                  type: integer
                rate_bps:
                  type: integer
                rebate_bps:
                  type: integer
            policy_binding:
              type: string
            fee_formula:
              type: string
            maker_execution_fee_cents:
              type: integer
            rebate:
              type: string
            rebate_cash_authority:
              type: string
GET/v1/account/rebatesRead this account's credited allocation for a named maker-rebate pool

Read this account's credited allocation for a named maker-rebate pool

Scopes: balances:read

NameLocationRequiredContract
market_idqueryyes{"type":"string"}
policy_idqueryyes{"type":"integer","minimum":1}
book_dayqueryyes{"type":"string","format":"date"}
Request and response contract
responses:
  "200":
    description: NOT_CREDITED is not a zero entitlement or a withdrawable accrual
    content:
      application/json:
        schema:
          type: object
          required:
            - status
            - market_id
            - policy_id
            - book_day
            - amount_cents
          properties:
            status:
              type: string
              enum:
                - CREDITED
                - NOT_CREDITED
            market_id:
              type: string
            policy_id:
              type: integer
            book_day:
              type: string
              format: date
            amount_cents:
              type:
                - integer
                - "null"
              format: int64
            ledger_entry_id:
              type: string
              format: uuid
            posted_at:
              type: string
              format: date-time
GET/v1/account/ledgerThis account's immutable postings for named financial entries

Supply ledger entry IDs returned by executions, credited rebates or payment evidence. Only postings whose account_code belongs to the authenticated participant are returned. Another participant's entry ID returns no data. No all-history scan or mutable balance reconstruction. The participant subset need not sum to zero; the complete internal journal includes counterparties and platform accounts and balances.

Scopes: balances:read

NameLocationRequiredContract
entry_idsqueryyesComma-separated list of 1 to 100 entry UUIDs
Request and response contract
responses:
  "200":
    description: Participant-owned postings only
    content:
      application/json:
        schema:
          type: object
          required:
            - user_id
            - postings
            - scope
          properties:
            user_id:
              type: string
            scope:
              type: string
            postings:
              type: array
              items:
                type: object
                required:
                  - posting_id
                  - entry_id
                  - entry_type
                  - account_code
                  - amount_cents
                  - posted_at
                properties:
                  posting_id:
                    type: string
                  entry_id:
                    type: string
                    format: uuid
                  entry_type:
                    type: string
                  account_code:
                    type: string
                  amount_cents:
                    type: integer
                    format: int64
                  posted_at:
                    type: string
                    format: date-time
  "422":
    description: More than 10000 postings; split entry IDs into smaller requests
GET/v1/positionsList user positions (YES/NO exposure projection)

List user positions (YES/NO exposure projection)

Scopes: orders:read

NameLocationRequiredContract
user_idqueryno{"type":"string"}
market_idqueryno{"type":"string"}
Request and response contract
responses:
  "200":
    description: Positions listed
    content:
      application/json:
        schema:
          type: object
          required:
            - user_id
            - count
            - positions
          properties:
            user_id:
              type: string
            count:
              type: integer
            positions:
              type: array
              items:
                type: object
                required:
                  - market_id
                  - yes_shares
                  - no_shares
                  - avg_yes_price
                  - avg_no_price
                  - mark_yes_price
                  - mark_no_price
                  - unrealized_pnl
                  - realized_pnl
                  - market
                properties:
                  market_id:
                    type: string
                  yes_shares:
                    type: integer
                  no_shares:
                    type: integer
                  avg_yes_price:
                    type: number
                  avg_no_price:
                    type: number
                  mark_yes_price:
                    type: number
                  mark_no_price:
                    type: number
                  unrealized_pnl:
                    type: number
                  realized_pnl:
                    type: number
                  market:
                    type: object
                    required:
                      - market_id
                      - title
                      - description
                      - status
                      - settlement_currency
                      - updated_at
                      - metadata
                    properties:
                      market_id:
                        type: string
                      title:
                        type: string
                      description:
                        type: string
                      status:
                        type: string
                        enum:
                          - DRAFT
                          - OPEN
                          - SUSPENDED
                          - RESOLVED
                          - CLOSED
                      settlement_currency:
                        type: string
                      close_at:
                        type: string
                        format: date-time
                        nullable: true
                      resolve_at:
                        type: string
                        format: date-time
                        nullable: true
                      resolved_outcome:
                        type: string
                        nullable: true
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                      metadata:
                        type: object
                        additionalProperties: true
GET/v1/portfolioUser portfolio summary and positions

User portfolio summary and positions

Scopes: orders:read

NameLocationRequiredContract
user_idqueryno{"type":"string"}
market_idqueryno{"type":"string"}
Request and response contract
responses:
  "200":
    description: Portfolio summary
    content:
      application/json:
        schema:
          type: object
          required:
            - user_id
            - total_value
            - total_pnl
            - pnl_percent
            - positions
          properties:
            user_id:
              type: string
            total_value:
              type: number
            total_pnl:
              type: number
            pnl_percent:
              type: number
            positions:
              type: array
              items:
                type: object
                required:
                  - market_id
                  - yes_shares
                  - no_shares
                  - avg_yes_price
                  - avg_no_price
                  - mark_yes_price
                  - mark_no_price
                  - unrealized_pnl
                  - realized_pnl
                  - market
                properties:
                  market_id:
                    type: string
                  yes_shares:
                    type: integer
                  no_shares:
                    type: integer
                  avg_yes_price:
                    type: number
                  avg_no_price:
                    type: number
                  mark_yes_price:
                    type: number
                  mark_no_price:
                    type: number
                  unrealized_pnl:
                    type: number
                  realized_pnl:
                    type: number
                  market:
                    type: object
                    required:
                      - market_id
                      - title
                      - description
                      - status
                      - settlement_currency
                      - updated_at
                      - metadata
                    properties:
                      market_id:
                        type: string
                      title:
                        type: string
                      description:
                        type: string
                      status:
                        type: string
                        enum:
                          - DRAFT
                          - OPEN
                          - SUSPENDED
                          - RESOLVED
                          - CLOSED
                      settlement_currency:
                        type: string
                      close_at:
                        type: string
                        format: date-time
                        nullable: true
                      resolve_at:
                        type: string
                        format: date-time
                        nullable: true
                      resolved_outcome:
                        type: string
                        nullable: true
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                      metadata:
                        type: object
                        additionalProperties: true
GET/v1/accounts/{userID}/balancesUser balances (derived from immutable postings)

User balances (derived from immutable postings)

Scopes: balances:read

NameLocationRequiredContract
userIDpathyes{"type":"string"}
Request and response contract
responses:
  "200":
    description: Balances

Shared schemas

CanonicalTaxonomyListResponse
type: object
additionalProperties: false
required:
  - count
  - source
properties:
  taxonomy:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTaxonomyNode"
  categories:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTaxonomyNode"
  series:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTaxonomyNode"
  sports:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTaxonomyNode"
  count:
    type: integer
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalTaxonomyNode
type: object
additionalProperties: false
required:
  - id
  - node_key
  - kind
  - category
  - label
  - slug
  - path
  - depth
  - sort_order
  - registry_version
  - canonical_url
properties:
  id:
    type: string
  node_key:
    type: string
  parent_id:
    type: string
  kind:
    type: string
  group_kind:
    type: string
  category:
    type: string
  label:
    type: string
  slug:
    type: string
  path:
    type: string
  depth:
    type: integer
  sort_order:
    type: integer
  registry_version:
    type: string
  canonical_url:
    type: string
  metadata:
    type: object
    additionalProperties: true
CanonicalTagListResponse
type: object
additionalProperties: false
required:
  - tags
  - count
  - source
properties:
  tags:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTag"
  count:
    type: integer
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalTag
type: object
additionalProperties: false
required:
  - id
  - slug
  - label
  - event_count
properties:
  id:
    type: string
  slug:
    type: string
  label:
    type: string
  category:
    type: string
  event_count:
    type: integer
    format: int64
  metadata:
    type: object
    additionalProperties: true
CanonicalCompetitionListResponse
type: object
additionalProperties: false
required:
  - competitions
  - count
  - source
properties:
  competitions:
    type: array
    items:
      type: object
      additionalProperties: true
  count:
    type: integer
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalEventListResponse
type: object
additionalProperties: false
required:
  - events
  - count
  - limit
  - has_more
  - next_cursor
  - source
properties:
  events:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalEvent"
  count:
    type: integer
  limit:
    type: integer
  has_more:
    type: boolean
  next_cursor:
    type: string
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalEvent
type: object
additionalProperties: false
required:
  - id
  - slug
  - title
  - description
  - status
  - classification_status
  - classification_source
  - taxonomy
  - tags
  - revision
  - updated_at
  - canonical_url
properties:
  id:
    type: string
  slug:
    type: string
  title:
    type: string
  description:
    type: string
  status:
    type: string
  classification_status:
    type: string
  classification_source:
    type: string
  starts_at:
    type: string
    format: date-time
  close_at:
    type: string
    format: date-time
  resolve_at:
    type: string
    format: date-time
  taxonomy:
    $ref: "#/components/schemas/CanonicalEventTaxonomy"
  tags:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTag"
  revision:
    type: integer
    format: int64
  updated_at:
    type: string
    format: date-time
  canonical_url:
    type: string
  live:
    $ref: "#/components/schemas/LivePresentationSnapshot"
  market_families:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalMarketFamily"
CanonicalEventTaxonomy
type: object
additionalProperties: false
required:
  - category
  - breadcrumbs
properties:
  category:
    type: string
  sport:
    type: string
  competition:
    type: string
  series:
    type: string
  cycle:
    type: string
  phase:
    type: string
  path_pattern_id:
    type: string
  node_id:
    type: string
  node_key:
    type: string
  path:
    type: string
  breadcrumbs:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalTaxonomyNode"
LivePresentationSnapshot
type: object
additionalProperties: false
required:
  - provider
  - providerScopeId
  - sport
  - schemaKind
  - isLive
  - isTerminal
  - freshness
  - competitors
  - periods
  - clock
  - summary
  - sourceCursors
  - observedAt
properties:
  eventId:
    type: string
  bindingHash:
    type: string
  provider:
    type: string
  providerScopeId:
    type: string
  sport:
    type: string
  schemaKind:
    type: string
    enum:
      - TEAM_GAME
      - TENNIS_MATCH
      - GOLF_LEADERBOARD
      - NONE
  providerStatusName:
    type: string
  isLive:
    type: boolean
  isTerminal:
    type: boolean
  freshness:
    type: string
    enum:
      - PRE_EVENT
      - FRESH
      - STALE
      - TERMINAL
      - UNAVAILABLE
  startedAt:
    type: string
    format: date-time
  endedAt:
    type: string
    format: date-time
  competitors:
    type: array
    items:
      $ref: "#/components/schemas/LivePresentationCompetitor"
  periods:
    type: array
    items:
      $ref: "#/components/schemas/LivePresentationPeriod"
  clock:
    $ref: "#/components/schemas/LivePresentationClock"
  summary:
    $ref: "#/components/schemas/LivePresentationSummary"
  sourceCursors:
    type: object
    additionalProperties:
      type: string
  observedAt:
    type: string
    format: date-time
  validUntil:
    type: string
    format: date-time
  revision:
    type: integer
    format: int64
LivePresentationCompetitor
type: object
additionalProperties: false
required:
  - key
  - name
properties:
  key:
    type: string
  name:
    type: string
  score:
    type: number
LivePresentationPeriod
type: object
additionalProperties: false
required:
  - key
properties:
  key:
    type: string
  label:
    type: string
  participant1Score:
    type: number
  participant2Score:
    type: number
  updatedAt:
    type: string
    format: date-time
LivePresentationClock
type: object
additionalProperties: false
properties:
  currentPeriod:
    type: string
  currentTime:
    type: string
  remainingTime:
    type: string
  remainingTimeInPeriod:
    type: string
  stopped:
    type: boolean
LivePresentationSummary
type: object
additionalProperties: false
properties:
  text:
    type: string
CanonicalMarketFamily
type: object
additionalProperties: false
required:
  - id
  - event_id
  - slug
  - title
  - description
  - family_type_key
  - family_code
  - selection_mode
  - membership_complete
  - status
  - display_order
  - revision
  - updated_at
  - canonical_url
properties:
  id:
    type: string
  event_id:
    type: string
  slug:
    type: string
  title:
    type: string
  description:
    type: string
  family_type_key:
    type: string
  family_code:
    type: string
  selection_mode:
    type: string
  resolution_cardinality:
    type: string
  resolution_coordination:
    type: string
  resolution_constraint_key:
    type: string
  membership_complete:
    type: boolean
  status:
    type: string
  display_order:
    type: integer
  close_at:
    type: string
    format: date-time
  resolve_at:
    type: string
    format: date-time
  resolved_selection_key:
    type: string
  revision:
    type: integer
    format: int64
  updated_at:
    type: string
    format: date-time
  canonical_url:
    type: string
  publication_state:
    $ref: "#/components/schemas/MarketPublicationState"
  presentation_renderer:
    $ref: "#/components/schemas/MarketPresentationRenderer"
  live_data_policy:
    $ref: "#/components/schemas/MarketLiveDataPolicy"
  live:
    $ref: "#/components/schemas/LivePresentationSnapshot"
  markets:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalMarket"
MarketPublicationState
type: string
enum:
  - HIDDEN_BLOCKED
  - PUBLIC_BLOCKED
  - PUBLIC_OPEN
  - PUBLIC_DEGRADED
  - RETIRED
MarketPresentationRenderer
type: string
enum:
  - TEAM_GAME
  - TENNIS_MATCH
  - SELECTION_GRID
MarketLiveDataPolicy
type: string
enum:
  - OPTIONAL_DISPLAY
  - REQUIRED_FOR_IN_PLAY
  - NOT_APPLICABLE
CanonicalMarket
type: object
additionalProperties: false
required:
  - id
  - family_id
  - event_id
  - title
  - description
  - status
  - market_generation
  - settlement_currency
  - selection_key
  - outcome_id
  - outcome_name
  - sort_order
  - revision
  - updated_at
  - canonical_url
properties:
  id:
    type: string
  family_id:
    type: string
  event_id:
    type: string
  title:
    type: string
  description:
    type: string
  status:
    type: string
  market_generation:
    type: integer
    format: int64
    minimum: 0
    description: Exact current admission fence for canonical order entry. A zero
      value means this public market is not available through the
      generation-fenced canonical order contract; refresh the market after any
      stale_market_generation response.
  settlement_currency:
    type: string
  selection_key:
    type: string
  outcome_id:
    type: string
  outcome_name:
    type: string
  sort_order:
    type: integer
  close_at:
    type: string
    format: date-time
  resolve_at:
    type: string
    format: date-time
  resolved_outcome:
    type: string
  revision:
    type: integer
    format: int64
  updated_at:
    type: string
    format: date-time
  canonical_url:
    type: string
  publication_state:
    $ref: "#/components/schemas/MarketPublicationState"
  presentation_renderer:
    $ref: "#/components/schemas/MarketPresentationRenderer"
  live_data_policy:
    $ref: "#/components/schemas/MarketLiveDataPolicy"
  live:
    $ref: "#/components/schemas/LivePresentationSnapshot"
  external_mappings:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalExternalMapping"
CanonicalExternalMapping
type: object
additionalProperties: false
description: Typed provider identity. Omitted unless `external_mappings` is
  explicitly requested.
required:
  - provider
  - namespace
  - role
properties:
  provider:
    type: string
  namespace:
    type: string
  role:
    type: string
  provider_event_id:
    type: string
  provider_event_slug:
    type: string
  provider_market_id:
    type: string
  provider_outcome_id:
    type: string
  condition_id:
    type: string
  question_id:
    type: string
  selected_token_id:
    type: string
  opposite_token_id:
    type: string
CanonicalEventResponse
type: object
additionalProperties: false
required:
  - event
  - alias_used
  - source
properties:
  event:
    $ref: "#/components/schemas/CanonicalEvent"
  alias_used:
    type: boolean
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalFamilyListResponse
type: object
additionalProperties: false
required:
  - event_id
  - market_families
  - count
  - source
properties:
  event_id:
    type: string
  market_families:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalMarketFamily"
  count:
    type: integer
  source:
    type: string
    enum:
      - canonical_catalog
MarketFamilyListResponse
type: object
required:
  - families
  - count
  - cached
  - source
properties:
  families:
    type: array
    items:
      $ref: "#/components/schemas/MarketFamily"
  count:
    type: integer
  cached:
    type: boolean
  source:
    type: string
MarketFamily
type: object
required:
  - id
  - title
  - description
  - category
  - status
  - outcomes
  - totalVolume
  - volume24h
  - liquidity
  - endDate
properties:
  id:
    type: string
  eventId:
    type: string
  eventTitle:
    type: string
  eventStatus:
    type: string
  verticalKey:
    type: string
  pathPatternId:
    type: string
  providerName:
    type: string
  providerEventRef:
    type: string
  classificationStatus:
    type: string
  classificationSource:
    type: string
  title:
    type: string
  description:
    type: string
  category:
    type: string
  status:
    type: string
    enum:
      - active
      - resolved
      - pending
  familyStatus:
    type: string
  familyTypeKey:
    type: string
  familyCode:
    type: string
  selectionMode:
    type: string
  displayOrder:
    type: integer
  canonicalSlug:
    type: string
  canonicalUrl:
    type: string
  publicationState:
    $ref: "#/components/schemas/MarketPublicationState"
  presentationRenderer:
    $ref: "#/components/schemas/MarketPresentationRenderer"
  liveDataPolicy:
    $ref: "#/components/schemas/MarketLiveDataPolicy"
  liveDetails:
    $ref: "#/components/schemas/LivePresentationSnapshot"
  outcomes:
    type: array
    items:
      $ref: "#/components/schemas/MarketFamilyOutcome"
  totalVolume:
    type: number
  volume24h:
    type: number
  liquidity:
    type: number
  endDate:
    type: string
  imageUrl:
    type: string
MarketFamilyOutcome
type: object
required:
  - id
  - market_id
  - market_generation
  - name
  - probability
  - volume24h
  - change24h
properties:
  id:
    type: string
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
    minimum: 0
    description: Current activation generation for this tradable leaf.
  publicationState:
    $ref: "#/components/schemas/MarketPublicationState"
  name:
    type: string
  probability:
    type: number
  volume24h:
    type: number
  change24h:
    type: number
  flag:
    type: string
  imageUrl:
    type: string
  entityId:
    type: string
  entitySlug:
    type: string
  entityKind:
    type: string
    enum:
      - team
      - athlete
      - competition
      - venue
  entitySportKey:
    type: string
  shortName:
    type: string
  countryCode:
    type: string
  iconMetadata:
    type: object
    additionalProperties: true
  resolved:
    type: boolean
  result:
    type: string
    enum:
      - win
      - lose
      - void
CanonicalDirectFamilyListResponse
type: object
additionalProperties: false
required:
  - market_families
  - count
  - limit
  - has_more
  - next_cursor
  - source
properties:
  market_families:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalMarketFamily"
  count:
    type: integer
  limit:
    type: integer
  has_more:
    type: boolean
  next_cursor:
    type: string
  source:
    type: string
    enum:
      - canonical_catalog
MarketFamilyResponse
type: object
required:
  - family
  - source
properties:
  family:
    $ref: "#/components/schemas/MarketFamily"
  source:
    type: string
CanonicalFamilyResponse
type: object
additionalProperties: false
required:
  - market_family
  - alias_used
  - source
properties:
  market_family:
    $ref: "#/components/schemas/CanonicalMarketFamily"
  alias_used:
    type: boolean
  source:
    type: string
    enum:
      - canonical_catalog
LegacyMarketListResponse
type: object
required:
  - markets
  - count
  - cached
  - source
properties:
  markets:
    type: array
    items:
      $ref: "#/components/schemas/LegacyMarket"
  count:
    type: integer
  cached:
    type: boolean
  source:
    type: string
LegacyMarket
type: object
required:
  - market_id
  - market_generation
  - title
  - description
  - status
  - settlement_currency
  - updated_at
  - metadata
properties:
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
    minimum: 0
    description: Current activation generation used to fence order commands. A zero
      value means the market is not activation-managed and cannot be submitted
      through the canonical order path.
  title:
    type: string
  description:
    type: string
  status:
    type: string
    enum:
      - DRAFT
      - OPEN
      - SUSPENDED
      - RESOLVED
      - CLOSED
  settlement_currency:
    type: string
  close_at:
    type: string
    format: date-time
    nullable: true
  resolve_at:
    type: string
    format: date-time
    nullable: true
  resolved_outcome:
    type: string
    nullable: true
  publication_state:
    $ref: "#/components/schemas/MarketPublicationState"
  presentation_renderer:
    $ref: "#/components/schemas/MarketPresentationRenderer"
  live_data_policy:
    $ref: "#/components/schemas/MarketLiveDataPolicy"
  liveDetails:
    $ref: "#/components/schemas/LivePresentationSnapshot"
  updated_at:
    type: string
    format: date-time
  metadata:
    type: object
    additionalProperties: true
  best_bid:
    type: number
  best_ask:
    type: number
  last_price:
    type: number
  volume_24h:
    type: number
  total_volume:
    type: number
  open_interest:
    type: number
  seq:
    type: integer
CanonicalMarketListResponse
type: object
additionalProperties: false
required:
  - markets
  - count
  - limit
  - has_more
  - next_cursor
  - source
properties:
  markets:
    type: array
    items:
      $ref: "#/components/schemas/CanonicalMarket"
  count:
    type: integer
  limit:
    type: integer
  has_more:
    type: boolean
  next_cursor:
    type: string
  source:
    type: string
    enum:
      - canonical_catalog
CanonicalMarketResponse
type: object
additionalProperties: false
required:
  - market
  - source
properties:
  market:
    $ref: "#/components/schemas/CanonicalMarket"
  source:
    type: string
    enum:
      - canonical_catalog
BookSnapshot
type: object
required:
  - market_id
  - depth
  - bids
  - asks
  - seq
  - updated_at
properties:
  market_id:
    type: string
  depth:
    type: integer
    minimum: 1
    maximum: 200
  bids:
    type: array
    items:
      $ref: "#/components/schemas/BookLevel"
  asks:
    type: array
    items:
      $ref: "#/components/schemas/BookLevel"
  seq:
    type: integer
    format: int64
    description: YM projection sequence; not a durable professional replay cursor.
  updated_at:
    type: string
    format: date-time
    description: YM projection timestamp, not a venue-origin timestamp.
BookLevel
type: object
required:
  - price
  - qty
properties:
  price:
    type: number
    description: Decimal price retained for consumer compatibility.
  price_cents:
    type: integer
    minimum: 0
    maximum: 100
    description: Exact integer-cent price used by the canonical trading contract
      when present.
  qty:
    type: integer
    minimum: 0
CanonicalOrderListResponse
type: object
additionalProperties: false
required:
  - schema_version
  - count
  - orders
  - next_cursor
properties:
  schema_version:
    type: integer
    const: 2
  count:
    type: integer
    minimum: 0
  orders:
    type: array
    items:
      $ref: "#/components/schemas/Order"
  next_cursor:
    type: string
    description: Empty when no later page exists.
Order
type: object
additionalProperties: false
required:
  - id
  - created_by_command_id
  - market_id
  - market_generation
  - action
  - outcome
  - price_cents
  - original_quantity
  - total_quantity
  - cumulative_filled_quantity
  - remaining_unfilled_quantity
  - working_quantity
  - filled_notional_cents
  - execution_status
  - pending_action
  - state_quality
  - order_version
  - is_terminal
  - is_working
  - may_still_execute
  - has_reserved_exposure
  - admitted_at
  - updated_at
properties:
  post_only:
    type: boolean
    default: false
    description: True when this order was admitted as native maker-or-cancel.
      Absence means false.
  id:
    type: string
    format: uuid
  created_by_command_id:
    type: string
    format: uuid
  client_order_id:
    type: string
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
    minimum: 1
    description: Exact positive market-generation admission fence.
  action:
    type: string
    enum:
      - BUY
      - SELL
    description: Immutable participant action against outcome; SELL is
      position-reducing only.
  outcome:
    type: string
    enum:
      - YES
      - NO
    description: Immutable economic outcome. Price and filled notional fields use
      this outcome's coordinate.
  price_cents:
    type: integer
    format: int64
    minimum: 0
    maximum: 100
    description: Effective limit price of the named outcome, in cents.
  original_quantity:
    type: integer
    format: int64
    minimum: 1
  total_quantity:
    type: integer
    format: int64
    minimum: 1
    description: Latest venue-effective total quantity; original_quantity remains
      the immutable NEW size.
  cumulative_filled_quantity:
    type: integer
    format: int64
    minimum: 0
  remaining_unfilled_quantity:
    type: integer
    format: int64
    minimum: 0
  working_quantity:
    type: integer
    format: int64
    minimum: 0
  filled_notional_cents:
    type: integer
    format: int64
    minimum: 0
    description: Sum of fill price times quantity in the named outcome coordinate.
  execution_status:
    type: string
    enum:
      - PENDING
      - WORKING
      - PARTIALLY_FILLED
      - FILLED
      - CANCELLED
      - EXPIRED
      - REJECTED
  pending_action:
    type:
      - string
      - "null"
    enum:
      - REPLACE
      - CANCEL
      - null
  state_quality:
    type: string
    enum:
      - CONFIRMED
      - RECONCILIATION_REQUIRED
  order_version:
    type: integer
    format: int64
    minimum: 1
  is_terminal:
    type: boolean
  is_working:
    type: boolean
  may_still_execute:
    type: boolean
  has_reserved_exposure:
    type: boolean
  time_in_force:
    type: string
    enum:
      - GTC
      - IOC
      - DAY
  admitted_at:
    type: string
    format: date-time
  venue_accepted_at:
    type:
      - string
      - "null"
    format: date-time
  last_fill_at:
    type:
      - string
      - "null"
    format: date-time
  terminal_at:
    type:
      - string
      - "null"
    format: date-time
  terminal_reason:
    type: string
  updated_at:
    type: string
    format: date-time
CanonicalOrderError
type: object
required:
  - code
  - message
  - retryable
properties:
  code:
    type: string
  message:
    type: string
  error:
    type: string
    deprecated: true
    description: Transitional alias for message.
  command_id:
    type:
      - string
      - "null"
    format: uuid
  client_request_id:
    type:
      - string
      - "null"
  admission_status:
    type: string
    enum:
      - ADMITTED
      - REJECTED
    description: Present when the response recovered or persisted a canonical command.
  outcome_status:
    type: string
    enum:
      - PENDING
      - EFFECTIVE
      - REJECTED
      - NOOP
      - CANCELLED_BEFORE_DISPATCH
      - UNKNOWN
    description: Present when the response recovered or persisted a canonical command.
  idempotent_replay:
    type: boolean
    description: Present when a canonical command identity was recovered.
  retryable:
    type: boolean
  details:
    type: object
    additionalProperties: true
CreateOrderRequest
allOf:
  - $ref: "#/components/schemas/CreateOrderFields"
unevaluatedProperties: false
CreateOrderFields
type: object
required:
  - client_order_id
  - market_id
  - market_generation
  - action
  - outcome
  - price_cents
  - quantity
  - time_in_force
properties:
  client_order_id:
    type: string
    maxLength: 200
    description: Participant-defined order identity, unique within the authenticated
      account.
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
    minimum: 1
    description: Exact admission generation fence obtained from the market resource.
  action:
    type: string
    enum:
      - BUY
      - SELL
    description: BUY opens or increases a fully collateralized position in outcome.
      SELL reduces that outcome only and is rejected when unreserved holdings
      are insufficient; it never reverses or nets another outcome.
  outcome:
    type: string
    enum:
      - YES
      - NO
    description: Immutable economic outcome for this order. All public prices and
      fill notionals use this outcome's coordinate.
  price_cents:
    type: integer
    format: int64
    minimum: 0
    maximum: 100
    description: Limit price of outcome, in integer cents. For NO, YM converts once
      to the complementary YES-book price internally; clients must not
      complement it.
  quantity:
    type: integer
    format: int64
    minimum: 1
  time_in_force:
    type: string
    enum:
      - GTC
      - IOC
      - DAY
    description: GTC is the professional resting primitive; IOC attempts immediate
      execution; DAY is the venue-supported session-scoped alternative.
  post_only:
    type: boolean
    default: false
    description: Native maker-or-cancel instruction; requires GTC or DAY. Immutable
      through REPLACE. Execution outcome is asynchronous, not implied by HTTP
      admission.
example:
  client_order_id: strategy-no-000001
  market_id: example-market
  market_generation: 7
  action: BUY
  outcome: NO
  price_cents: 35
  quantity: 10
  time_in_force: GTC
CanonicalCommandOrderResponse
type: object
additionalProperties: false
required:
  - schema_version
  - command
  - idempotent_replay
properties:
  schema_version:
    type: integer
    const: 2
  command:
    $ref: "#/components/schemas/CanonicalOrderCommand"
  order:
    $ref: "#/components/schemas/Order"
  idempotent_replay:
    type: boolean
CanonicalOrderCommand
type: object
additionalProperties: false
required:
  - command_id
  - command_type
  - client_request_id
  - admission_status
  - outcome_status
  - created_at
  - updated_at
properties:
  command_id:
    type: string
    format: uuid
  order_id:
    type: string
    format: uuid
  command_sequence:
    type: integer
    format: int64
    minimum: 1
  command_type:
    type: string
    enum:
      - NEW
      - REPLACE
      - CANCEL
  client_request_id:
    type: string
  client_order_id:
    type: string
  admission_status:
    type: string
    enum:
      - ADMITTED
      - REJECTED
  outcome_status:
    type: string
    enum:
      - PENDING
      - EFFECTIVE
      - REJECTED
      - NOOP
      - CANCELLED_BEFORE_DISPATCH
      - UNKNOWN
  reason_code:
    type: string
  reason_detail:
    type: string
  admitted_at:
    type: string
    format: date-time
  dispatched_at:
    type: string
    format: date-time
  completed_at:
    type: string
    format: date-time
  created_at:
    type: string
    format: date-time
  updated_at:
    type: string
    format: date-time
ReplaceOrderRequest
type: object
additionalProperties: false
required:
  - expected_order_version
  - price_cents
  - total_quantity
properties:
  expected_order_version:
    type: integer
    format: int64
    minimum: 1
    description: Optimistic concurrency fence from the latest canonical order snapshot.
  price_cents:
    type: integer
    format: int64
    minimum: 0
    maximum: 100
    description: Replacement price in the order's immutable outcome coordinate.
  total_quantity:
    type: integer
    format: int64
    minimum: 1
    description: Target total quantity including any quantity already filled.
CreateOrderBatchItem
allOf:
  - $ref: "#/components/schemas/CreateOrderFields"
  - type: object
    required:
      - client_request_id
    properties:
      client_request_id:
        type: string
        minLength: 1
        maxLength: 200
        pattern: ^(?![Yy][Mm]:).+$
        description: Per-item participant-scoped command identity.
unevaluatedProperties: false
OrderBatchResponse
type: object
required:
  - accepted
  - failed
  - results
properties:
  accepted:
    type: integer
  failed:
    type: integer
  results:
    type: array
    items:
      type: object
      required:
        - index
        - accepted
        - retryable
        - status
      properties:
        index:
          type: integer
        accepted:
          type: boolean
        retryable:
          type: boolean
        status:
          type: integer
        client_request_id:
          type: string
        order_id:
          type: string
        error:
          type: string
        body:
          type: object
          additionalProperties: true
CancellationStatus
type: object
additionalProperties: false
required:
  - request_id
  - requested_at
  - total_orders
  - remaining_orders
  - status
properties:
  request_id:
    type: string
    format: uuid
  requested_at:
    type: string
    format: date-time
  market_id:
    type: string
  total_orders:
    type: integer
    minimum: 0
    maximum: 10000
  remaining_orders:
    type: integer
    minimum: 0
  status:
    type: string
    enum:
      - PENDING
      - COMPLETE
CancellationLease
type: object
required:
  - lease_id
  - state
  - timeout_seconds
  - expires_at
properties:
  lease_id:
    type: string
    format: uuid
  state:
    type: string
    enum:
      - ARMED
      - FIRED
      - DISARMED
  timeout_seconds:
    type: integer
    minimum: 10
    maximum: 120
  expires_at:
    type: string
    format: date-time
  cancellation_ids:
    type: array
    items:
      type: string
      format: uuid
    description: Immutable groups captured atomically at expiry. Poll every group's
      cancellation status; FIRED alone is not proof of completion.
PostedExecution
type: object
required:
  - fill_id
  - trade_id
  - order_id
  - market_id
  - action
  - outcome
  - price_cents
  - quantity
  - liquidity_role
  - fee_cents
  - fee_policy_id
  - ledger_entry_id
  - posted_at
properties:
  fill_id:
    type: string
    description: Stable posting-side fill ID; string to avoid 64-bit JSON precision loss
  trade_id:
    type: string
    format: uuid
  order_id:
    type: string
    format: uuid
  client_order_id:
    type: string
  market_id:
    type: string
  action:
    type: string
    enum:
      - BUY
      - SELL
  outcome:
    type: string
    enum:
      - YES
      - NO
  price_cents:
    type: integer
    minimum: 0
    maximum: 100
  quantity:
    type: integer
  liquidity_role:
    type: string
    enum:
      - MAKER
      - TAKER
  fee_cents:
    type: integer
  fee_policy_id:
    type: integer
    description: The paired trade's taker fee policy; zero means no fee receipt policy
  ledger_entry_id:
    type: string
    format: uuid
  posted_at:
    type: string
    format: date-time
    description: Database posting time
    not exchange transaction time: null
ProfessionalCommandFrame
type: object
additionalProperties: false
required:
  - request_id
  - operation
  - client_request_id
properties:
  request_id:
    type: string
    minLength: 1
    maxLength: 200
  operation:
    type: string
    enum:
      - NEW
      - REPLACE
      - CANCEL
      - CANCEL_ALL
  client_request_id:
    type: string
    minLength: 1
    maxLength: 200
  order_id:
    type: string
    format: uuid
  body:
    type: object
description: NEW body is CreateOrderRequest; REPLACE body is
  ReplaceOrderRequest. CANCEL body is absent or empty. CANCEL_ALL body is empty
  or has market_id. order_id is required only for REPLACE/CANCEL. request_id is
  connection-local; client_request_id is the durable REST Idempotency-Key, not a
  new identity.
ProfessionalCommandWelcome
type: object
required:
  - type
  - contract
  - schema_version
  - normal_inflight_limit
  - cancel_inflight_limit
  - heartbeat_ms
  - recovery
  - execution_feed
properties:
  type:
    const: stream.welcome
  contract:
    const: ym.order-entry.v1
  schema_version:
    const: 2
  normal_inflight_limit:
    const: 12
  cancel_inflight_limit:
    const: 4
  heartbeat_ms:
    const: 5000
  recovery:
    const: retry_identical_client_request_id
  execution_feed:
    const: /v1/trading-feed
ProfessionalCommandResponse
type: object
required:
  - type
  - request_id
  - status
  - body
properties:
  type:
    const: command.response
  request_id:
    type: string
  status:
    type: integer
    minimum: 200
    maximum: 599
  body:
    type: object
    description: Unmodified corresponding REST response or transport error;
      admission is not execution.
  retry_after:
    type: string
ProfessionalBookEnvelope
type: object
required:
  - stream_id
  - sequence
  - event
properties:
  stream_id:
    type: string
    format: uuid
  sequence:
    type: integer
    minimum: 1
    description: Connection-local contiguous sequence including control messages;
      starts at one.
  event:
    oneOf:
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: book.snapshot
          data:
            $ref: "#/components/schemas/ProfessionalBookSnapshot"
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: book.delta
          data:
            $ref: "#/components/schemas/ProfessionalBookDelta"
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: book.changes
          data:
            $ref: "#/components/schemas/ProfessionalBookChanges"
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: market.status
          data:
            $ref: "#/components/schemas/ProfessionalBookStatus"
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: stream.welcome
          data:
            type: object
            required:
              - contract
              - heartbeat_ms
              - recovery
              - price_coordinate
              - markets
            properties:
              contract:
                const: ym.market-data.v1
              heartbeat_ms:
                const: 5000
              recovery:
                const: new_connection_snapshot
              price_coordinate:
                const: YES
              markets:
                type: array
                items:
                  type: string
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: stream.ready
          data:
            type: object
            required:
              - markets
            properties:
              markets:
                type: array
                items:
                  type: string
      - type: object
        required:
          - type
          - data
        properties:
          type:
            const: stream.heartbeat
          data:
            type: object
            required:
              - at
            properties:
              at:
                type: string
                format: date-time
ProfessionalBookSnapshot
allOf:
  - $ref: "#/components/schemas/ProfessionalBookStatus"
  - type: object
    required:
      - bids
      - asks
      - updated_at
    properties:
      bids:
        type: array
        items:
          $ref: "#/components/schemas/ProfessionalBookLevel"
      asks:
        type: array
        items:
          $ref: "#/components/schemas/ProfessionalBookLevel"
      updated_at:
        type: string
        format: date-time
ProfessionalBookStatus
type: object
required:
  - market_id
  - market_generation
  - lifecycle
  - state
  - feed_epoch
properties:
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
  lifecycle:
    type: string
  state:
    type: string
    enum:
      - FRESH
      - STALE
      - CLOSED
  reason:
    type: string
  feed_epoch:
    type: integer
    format: int64
ProfessionalBookLevel
type: object
required:
  - price
  - price_cents
  - qty
properties:
  price:
    type: number
    minimum: 0
    maximum: 1
  price_cents:
    type: integer
    minimum: 0
    maximum: 100
  qty:
    type: integer
    format: int64
    minimum: 1
ProfessionalBookDelta
allOf:
  - $ref: "#/components/schemas/ProfessionalBookChange"
  - type: object
    required:
      - market_id
      - market_generation
      - observed_at
    properties:
      market_id:
        type: string
      market_generation:
        type: integer
        format: int64
      observed_at:
        type: string
        format: date-time
ProfessionalBookChange
type: object
required:
  - side
  - price_cents
  - quantity
properties:
  side:
    type: string
    enum:
      - BUY
      - SELL
  price_cents:
    type: integer
    minimum: 0
    maximum: 100
  quantity:
    type: integer
    format: int64
    minimum: 0
    description: Absolute resulting level quantity; zero removes the level.
ProfessionalBookChanges
type: object
required:
  - market_id
  - market_generation
  - observed_at
  - changes
properties:
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
  observed_at:
    type: string
    format: date-time
  changes:
    type: array
    minItems: 1
    items:
      $ref: "#/components/schemas/ProfessionalBookChange"
    description: Apply all changes atomically before exposing the resulting book.
PrivateFeedLifecycleEvent
type: object
additionalProperties: false
description: Order lifecycle events carry a complete order snapshot. The sole
  orderless variant is order.command_result for an authenticated, schema-valid
  NEW command rejected by YM before an order exists; that variant carries the
  canonical command and no order, fill, or venue fields.
required:
  - schema_version
  - cursor
  - event_id
  - event_type
  - account_scope
  - cause
  - ym_received_at
  - ym_projected_at
  - server_sent_at
allOf:
  - if:
      properties:
        event_type:
          const: order.command_result
      required:
        - event_type
    then:
      required:
        - command
      oneOf:
        - required:
            - order_id
            - order_version
            - order
        - properties:
            command:
              allOf:
                - $ref: "#/components/schemas/CanonicalOrderCommand"
                - type: object
                  required:
                    - command_type
                    - admission_status
                    - outcome_status
                  properties:
                    command_type:
                      const: NEW
                    admission_status:
                      const: REJECTED
                    outcome_status:
                      const: REJECTED
          not:
            anyOf:
              - required:
                  - order_id
              - required:
                  - order_version
              - required:
                  - order
              - required:
                  - fill
              - required:
                  - venue
              - required:
                  - venue_event_at
    else:
      required:
        - order_id
        - order_version
        - order
      not:
        required:
          - command
  - if:
      properties:
        event_type:
          const: order.fill
      required:
        - event_type
    then:
      required:
        - fill
    else:
      not:
        required:
          - fill
properties:
  schema_version:
    type: integer
    const: 2
  cursor:
    type: string
    description: Persist only after the client fully validates and applies this event.
  event_id:
    type: string
    format: uuid
  event_type:
    type: string
    enum:
      - order.admitted
      - order.working
      - order.replaced
      - order.fill
      - order.cancelled
      - order.expired
      - order.rejected
      - order.command_result
  account_scope:
    type: string
    const: self
  order_id:
    type: string
    format: uuid
  order_version:
    type: integer
    format: int64
    minimum: 1
  cause:
    $ref: "#/components/schemas/PrivateFeedCause"
  venue:
    $ref: "#/components/schemas/PrivateFeedVenueEvidence"
  venue_event_at:
    type: string
    format: date-time
  ym_received_at:
    type: string
    format: date-time
  ym_projected_at:
    type: string
    format: date-time
  server_sent_at:
    type: string
    format: date-time
  command:
    allOf:
      - $ref: "#/components/schemas/CanonicalOrderCommand"
    description: Present on order.command_result and omitted on other lifecycle
      events. A rejected NEW may omit order_id when admission failed before any
      order existed.
  order:
    $ref: "#/components/schemas/Order"
  fill:
    $ref: "#/components/schemas/PrivateFeedFill"
PrivateFeedCause
type: object
additionalProperties: false
required:
  - type
properties:
  type:
    type: string
  command_id:
    type: string
    format: uuid
  client_request_id:
    type: string
  reason_code:
    type: string
PrivateFeedVenueEvidence
type: object
additionalProperties: false
required:
  - provider
description: Participant-safe venue identity. Raw DX replay offsets,
  session/MPID topology, account identity, and the counterparty-side execution
  identifier are never exposed.
properties:
  provider:
    type: string
    example: dxmatch
  symbol:
    type: string
  execution_id:
    type: string
    description: The authenticated participant's own DX execution identifier.
  trade_id:
    type: string
    description: Shared DX trade identifier when supplied by the Trading Feed.
  match_id:
    type: string
    description: Shared DX match identifier when supplied by the Trading Feed.
PrivateFeedFill
type: object
additionalProperties: false
required:
  - trade_id
  - market_id
  - market_generation
  - price_cents
  - quantity
  - liquidity_role
  - settlement_status
properties:
  trade_id:
    type: string
  market_id:
    type: string
  market_generation:
    type: integer
    format: int64
  price_cents:
    type: integer
    format: int64
    minimum: 0
    maximum: 100
    description: Participant-relative fill price in the nested order's outcome
      coordinate; never the internal YES-book price for a NO order.
  quantity:
    type: integer
    format: int64
  liquidity_role:
    type: string
    enum:
      - MAKER
      - TAKER
  settlement_status:
    type: string
    enum:
      - POSTED
PrivateFeedControlMessage
type: object
additionalProperties: false
required:
  - schema_version
  - type
  - server_sent_at
properties:
  schema_version:
    type: integer
    const: 2
  type:
    type: string
    enum:
      - stream.welcome
      - stream.snapshot_started
      - stream.snapshot_chunk
      - stream.snapshot_completed
      - stream.ready
      - stream.heartbeat
      - stream.error
  server_sent_at:
    type: string
    format: date-time
  contract:
    type: string
    const: Private Order & Execution Feed v2
  heartbeat_seconds:
    type: integer
    minimum: 1
  max_frame_bytes:
    type: integer
    minimum: 1
  delivery:
    type: string
    const: at_least_once
  snapshot_id:
    type: string
    format: uuid
  as_of_cursor:
    type: string
  scope:
    type: string
    const: NONTERMINAL
  order_count:
    type: integer
    minimum: 0
  orders:
    type: array
    maxItems: 100
    items:
      $ref: "#/components/schemas/Order"
  code:
    type: string
    enum:
      - CURSOR_EXPIRED
      - REPLAY_LIMIT_EXCEEDED
      - INVALID_CURSOR
      - SLOW_CONSUMER
      - STREAM_ERROR
  message:
    type: string
  last_written_cursor:
    type: string
YM Professional Trading · contract 2.1 · DEV qualification · 2026-10-07