Skip to content

REST and WebSocket API Reference

Status: Normative. This appendix is the single source of truth for the pm-api-gwy REST contract as exposed under /api/v1. For an operational, tutorial-style guide see API Gateway (REST/WebSocket). The key words MUST, MUST NOT, SHOULD, and MAY are used per RFC 2119.

Scope & conventions

This appendix documents the client-visible REST surface of the API gateway. It covers request shapes, replies, query parameters, auth rules, and the stable error codes returned by the gateway. WebSocket streams live under /api/v1/ws/ (private, market-data, admin/monitor, admin/sim) and are specified normatively in the WebSocket API Reference; they are not repeated here.

All paths are rooted at /api/v1. Admin paths name their target as a path segment — /admin/symbols/{symbol}/..., /admin/gateways/{gateway_id}/..., /admin/market/... — and a body, where one is accepted, carries only optional parameters.

Endpoint index

Bootstrap

Endpoint Access Purpose
GET /api/v1/bootstrap/trader Any valid key One-request startup payload for TRADER / MARKET_MAKER / ADMIN
GET /api/v1/bootstrap/mm MARKET_MAKER key One-request startup payload for MARKET_MAKER (adds quote state)
GET /api/v1/bootstrap/admin Admin role One-request startup payload for ADMIN

Trading REST

Endpoint Access Purpose
POST /api/v1/orders Trading key Submit one order for the caller's gateway
DELETE /api/v1/orders/{order_id} Trading key Cancel one live order
PATCH /api/v1/orders/{order_id} Trading key Amend price and/or quantity on one live order
POST /api/v1/orders/{order_id}/replace Trading key Cancel then replace one live order
GET /api/v1/orders Trading key Return the caller gateway's live order cache
GET /api/v1/orders/{order_id} Trading key Return one cached order
POST /api/v1/oco Trading key Submit an OCO pair
DELETE /api/v1/oco/{oco_id} Trading key Cancel an OCO pair
POST /api/v1/combos Trading key Submit a combo order
DELETE /api/v1/combos/{combo_id} Trading key Cancel a combo order
POST /api/v1/quotes Trading key Submit a two-sided market-maker quote
DELETE /api/v1/quotes/{symbol} Trading key Cancel the active quote for one symbol
POST /api/v1/mass-cancel Trading key Cancel all resting exposure for the caller or one symbol
GET /api/v1/symbols Trading key Return instrument metadata
GET /api/v1/news Authenticated key Return the latest market-sim headlines and sector map
GET /api/v1/companies/{symbol} Authenticated key Return one company profile from the company database
GET /api/v1/session Authenticated key Return the session phase, exchange timezone and trading date
GET /api/v1/halts Authenticated key Return currently-halted symbols
GET /api/v1/status Trading key Return the caller's gateway id and role
GET /api/v1/healthz Public Liveness probe
GET /api/v1/quotes Trading key Return the caller's active market-maker quotes
GET /api/v1/quotes/legs Trading key Return current quote-leg state
GET /api/v1/positions Trading key Return current net positions

Reference data

Endpoint Access Purpose
GET /api/v1/reference Authenticated key Return the full reference bundle
GET /api/v1/reference/config-version Authenticated key Return the reference bundle version
GET /api/v1/reference/symbols Authenticated key Return per-symbol tick and risk metadata
GET /api/v1/reference/risk Authenticated key Return risk-band definitions
GET /api/v1/reference/indexes Authenticated key Return configured exchange index definitions
GET /api/v1/reference/schedule Authenticated key Return session schedule metadata

Market data

Endpoint Access Purpose
GET /api/v1/book Authenticated key Return best bid, best ask and last price for every symbol
GET /api/v1/book/{symbol} Authenticated key Return one symbol's book levels, last trade and recent prints

History

Endpoint Access Purpose
GET /api/v1/history/orders Trading key Return the caller gateway's order lifecycle events
GET /api/v1/history/orders/{order_id} Trading key Return the full lifecycle of one order
GET /api/v1/history/fills Trading key Return fill events
GET /api/v1/history/trades Authenticated key Return public trade tape rows
GET /api/v1/history/daily Authenticated key Return daily OHLCV rows
GET /api/v1/history/price-snapshots Authenticated key Return intraday price snapshots
GET /api/v1/history/index-daily Authenticated key Return daily index OHLC rows
GET /api/v1/history/index-snapshots Authenticated key Return intraday index snapshots
GET /api/v1/history/indexes Authenticated key List index ids with recorded data
GET /api/v1/history/index-events Authenticated key Return index structural and audit events

Admin REST

Endpoint Access Purpose
POST /api/v1/admin/session/transition Admin role Request a session-phase transition
GET /api/v1/admin/sim Admin role Market simulator state and its true fair values
GET /api/v1/admin/gateways Admin role List configured gateways and live connection state
POST /api/v1/admin/gateways/{gateway_id}/disconnect Admin role Forcibly disconnect one gateway
POST /api/v1/admin/symbols/{symbol}/halt Admin role Halt one symbol through the circuit breaker
POST /api/v1/admin/symbols/{symbol}/resume Admin role Resume one halted symbol
GET /api/v1/admin/risk/state Admin role Return live per-symbol risk state
GET /api/v1/admin/orders Admin role Return the cross-gateway active-order table
GET /api/v1/admin/orders/{order_id}/audit Admin role Return the full cross-gateway lifecycle of one order
GET /api/v1/admin/gateways/{gateway_id}/positions Admin role Return net position and average cost for any one gateway
POST /api/v1/admin/symbols/{symbol}/cancel-orders Admin role Cancel all resting exposure on one symbol
POST /api/v1/admin/gateways/{gateway_id}/kill-switch Admin role Cancel all resting exposure for one gateway
POST /api/v1/admin/market/halt Admin role Halt every symbol and cancel every quote
POST /api/v1/admin/market/resume Admin role Resume every symbol halted by a market halt
GET /api/v1/admin/market/volume Admin role Today's traded volume per symbol and in total
POST /api/v1/admin/symbols/{symbol}/reopen Admin role Uncross and reopen a symbol, optionally at an operator price
GET /api/v1/admin/symbols/{symbol}/orders Admin role Every resting order on a symbol, optionally one price level
GET /api/v1/admin/gateways/{gateway_id}/orders Admin role One gateway's orders as the engine holds them
DELETE /api/v1/admin/gateways/{gateway_id}/quotes/{symbol} Admin role Withdraw one gateway's quote on one symbol
POST /api/v1/admin/market/kill-switch Admin role Cancel all resting exposure across every gateway and symbol
POST /api/v1/admin/indexes/{index_id}/rebalance Admin role Rebalance one configured index
POST /api/v1/admin/reference/reload Admin role Request a reference-data reload (currently refused by the engine)

Auth and roles

  • Trading and admin REST requests use Authorization: Bearer <api_key>.
  • gateway_id: null credentials are read-only and may access public data — reference data, public history, GET /session, GET /halts, news and company profiles — but no gateway-scoped or admin endpoint.
  • ADMIN endpoints require a credential whose resolved engine gateway role is ADMIN.

Key types and provisioning

This appendix uses two access labels in endpoint tables:

  • Authenticated key (also called Authentication key): any configured API key accepted by pm-api-gwy. This includes both read-only and trading keys.
  • Trading key: an authenticated key whose configured gateway_id is not null. This key is bound to one engine gateway identity and can submit and manage that gateway's orders.

How keys are specified:

  • Keys are configured by operators in engine_config.yaml under api_gateways.<INSTANCE>.credentials.
  • Each credential entry has api_key and gateway_id.
  • gateway_id: null creates a read-only authenticated key.
  • gateway_id: <GW_ID> creates a trading-capable key for that gateway.

Example:

api_gateways:
  default:
    credentials:
      - api_key: key-trader-demo
        gateway_id: TRADER01
        description: Trading client for TRADER01
      - api_key: key-dashboard-demo
        gateway_id: null
        description: Read-only dashboard client

How to get a key:

  • API keys are provisioned out-of-band by the venue/operator team.
  • There is no REST endpoint in pm-api-gwy to self-issue or rotate keys.
  • After updating credentials, restart/redeploy the API gateway instance so the new key set is loaded.

Common error codes

Every non-2xx reply carries one of these error.code values. reject_code is the engine vocabulary shared with ALF; it is UNKNOWN where no engine reject applies.

Every endpoint can answer AUTH, ENGINE_AUTH, ENGINE_UNAVAILABLE (the engine could not be reached to confirm a trading key) and INTERNAL_ERROR. Each endpoint's Errors table lists exactly the other codes it can return; the OpenAPI document lists all of them per operation.

Code HTTP status reject_code Meaning
VALIDATION 400 MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION A body field, query parameter or history filter (date, cursor, event_type) failed validation; field names it. A price off the instrument's tick grid carries TICK_VIOLATION
AUTH 401 AUTH_REQUIRED Missing, malformed or unknown API key. The reply carries WWW-Authenticate: Bearer
ENGINE_AUTH 403 AUTH_FAILED The engine refused the key's gateway identity
READ_ONLY 403 ROLE_DENIED Read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 ROLE_DENIED The key's gateway lacks the role the endpoint needs (ADMIN, or MARKET_MAKER for /bootstrap/mm)
NOT_FOUND 404 UNKNOWN No such path
UNKNOWN_ORDER 404 UNKNOWN Order id unknown to the gateway (GET /orders/{id}) or absent from the audit trail (GET /admin/orders/{id})
UNKNOWN_COMPANY 404 UNKNOWN No company profile for the symbol
UNKNOWN_SYMBOL 404 UNKNOWN The symbol is not in the venue's reference data (GET /book/{symbol})
METHOD_NOT_ALLOWED 405 UNKNOWN The path exists but not for this method; the reply carries Allow
TRANSITION_REJECTED 409 UNKNOWN The engine refused the session transition
RELOAD_REJECTED 409 UNKNOWN The engine refused the reference reload
HALT_REJECTED 409 UNKNOWN The engine refused the halt (for example Unknown symbol: XYZ)
RESUME_REJECTED 409 UNKNOWN The engine refused the resume (for example TSLA is not halted)
KILL_SWITCH_REJECTED 409 UNKNOWN The engine refused a symbol, gateway or global kill switch
REOPEN_REJECTED 409 UNKNOWN The engine refused a symbol reopen (force-uncross), for example the symbol is not halted
QUERY_REJECTED 409 UNKNOWN The engine refused an admin query, for example Unknown symbol: XYZ
REBALANCE_REJECTED 409 UNKNOWN pm-index refused the rebalance
REPLACE_REJECTED 409 engine's The engine refused replace's cancel (for example the order already filled); nothing was sent
IDEMPOTENCY_KEY_REUSED 409 UNKNOWN The Idempotency-Key was already used for a different request
RATE_LIMIT 429 RATE_LIMITED Per-key write limit exceeded
INTERNAL_ERROR 500 INTERNAL_ERROR A gateway bug; the message carries no internals
INDEX_ERROR 502 UNKNOWN pm-index answered with an error
ENGINE_TIMEOUT 503 INTERNAL_ERROR No engine reply in time to a read, or to replace's cancel. Transient — retry. A write whose ack is late is not an error: 202 with ack_timeout: true
ENGINE_UNAVAILABLE 503 INTERNAL_ERROR The command could not be handed to the engine (down or backpressured). Transient — retry
SYMBOLS_NOT_READY 503 SYMBOL_NOT_READY Order entry before the symbol's tick precision reached the gateway. Transient — retry
INDEX_TIMEOUT 503 UNKNOWN No pm-index reply in time
STATS_DB 503 UNKNOWN The pm-stats database is not present
AUDIT_INDEX_UNAVAILABLE 503 UNKNOWN The pm-audit index is not built or not running
COMPANY_DB_UNAVAILABLE 503 UNKNOWN The company database is missing or unreadable

For an engine refusal (409), message is the engine's own reason. New codes may be added within v1; clients must treat an unknown code by its HTTP status.

Swagger/OpenAPI documentation

The API gateway supports live API documentation through FastAPI:

  • OpenAPI schema: /openapi.json
  • Swagger UI: /docs

The same document is committed in the repository as spec/rest/openapi.json, so a client can be generated or reviewed without a running gateway. It does not depend on deployment configuration: every v1 gateway publishes exactly that document, and CI fails if the code and the committed copy disagree.

These endpoints are available when swagger_enabled: true in the api_gateways configuration. When disabled, /docs and /openapi.json are not exposed.

Common reply shapes

  • 200 OK means the response body carries the outcome: every read, and every command the gateway waited for (admin commands, POST /mass-cancel, and any order, cancel, amend, replace, OCO, combo or quote write with ?wait=ack whose verdict arrived in time).
  • 202 Accepted means the request was handed on and the outcome follows on a WebSocket: those writes without ?wait=ack (or with it, when the verdict was late: ack_timeout: true), and POST /admin/gateways/{gateway_id}/disconnect.
  • Pagination endpoints return count, has_more, and next_cursor where applicable; after is the opaque cursor input.

Response contract

Every reply has a declared schema in the OpenAPI document (/openapi.json), and the gateway validates what it sends against it. The schemas of replies the gateway forwards from the engine are generated from the same message specs as the engine's own bindings, so the REST shape and the bus payload cannot drift apart (see Messages).

  • Omitted versus null. A field the schema marks optional is omitted when it does not apply (an ordinary order has no oco_group_id); a field it marks required is always present, and is null only where the schema allows it.
  • Collections are { "<plural>": [...], "count": n }; a paginated one adds has_more and next_cursor. Envelopes do not nest: a bootstrap's orders is the list itself, not { "orders": [...] }.
  • Status fields are closed enums declared in the schema.
  • Time. *_ns fields are epoch nanoseconds; ts fields and every history from/to are ISO-8601 (or a YYYY-MM-DD date); date is a trading day in the exchange timezone (session_timezone in GET /session).
  • Prices are JSON numbers in display units (187.25), always on the symbol's tick grid. A field named *_ticks carries the same price as an integer tick count; it supplements a display field and never replaces one.
  • Extension. New optional fields, new endpoints and new enum members may be added within v1. A client must ignore fields and enum values it does not recognise.

Canonical error envelope

All non-2xx replies MUST use this JSON envelope shape — including an unknown path (404 NOT_FOUND), a wrong method (405 METHOD_NOT_ALLOWED) and an unhandled gateway error (500 INTERNAL_ERROR).

Field Type Required Description
error Object yes Top-level error container
error.code String yes Stable machine-readable code (for example VALIDATION)
error.message String yes Human-readable summary
error.field String no Field name when validation pinpoints one input field
error.reject_code String no Canonical order-rejection class for ALF/REST comparison, when applicable
error.client_tag String no Echo of the submitted order correlation tag, when available
error.request_tag String no Echo of the amend/cancel request tag, when available

Canonical example:

{
  "error": {
    "code": "VALIDATION",
    "reject_code": "INVALID_VALUE",
    "message": "Input should be greater than 0",
    "field": "quantity",
    "client_tag": "desk1-aapl-00042"
  }
}

Pagination contract

Endpoints that support pagination use keyset cursoring with these rules:

  • Clients MAY pass after (opaque cursor from a previous response).
  • Replies include count, has_more and next_cursor.
  • next_cursor is a string when another page exists and null on the last.
  • Clients fetch the next page by passing after=<next_cursor> unchanged.
  • Ordering is deterministic and backend-defined per endpoint; cursors are valid only for the same endpoint and compatible filter set.
  • A malformed, stale, or cross-endpoint cursor returns 400 VALIDATION with field: "after".

Write outcomes and safe retries

Every write that creates or changes orders (POST /orders, DELETE and PATCH /orders/{order_id}, POST /orders/{order_id}/replace, POST and DELETE on /oco, /combos and /quotes) answers in one of three ways, so a client can always tell whether something reached the engine:

Reply Sent to the engine? What to do
200 OK, status: "ACKED" Yes, and the verdict is in the body (accepted, reject_code, event) Done
202 Accepted, ack_timeout: false Yes; no ?wait=ack, the verdict follows on the private WebSocket Watch the WebSocket
202 Accepted, ack_timeout: true Yes; ?wait=ack gave up waiting (wait_ack_sec) Watch the WebSocket or GET /orders; do not resubmit
4xx or 5xx No (503 ENGINE_UNAVAILABLE, 400, 429, ...) Fix and retry

The reply always carries the id to cancel or track (order_id, oco_id, combo_id, symbol), including after an ack timeout. Two exceptions, both on replace, which only sends the replacement once the original's cancel is confirmed: a refused cancel is 409 REPLACE_REJECTED (nothing sent), and a cancel whose outcome did not arrive is 503 ENGINE_TIMEOUT (no replacement sent; check the original with GET /orders/{order_id}).

Idempotency-Key. A client whose connection dropped cannot know which of those replies it missed. The submit writes (POST /orders, /oco, /combos, /quotes and /orders/{order_id}/replace) accept an optional Idempotency-Key header (1–255 characters; a UUID per logical request):

  • The first request with a key runs. A later one with the same key, from the same gateway, is not sent again: it gets the first reply (same status, same body) with the header Idempotent-Replayed: true. A duplicate that arrives while the first is still waiting for its ack waits for it too.
  • The same key on a different request (another path, query or body) is 409 IDEMPOTENCY_KEY_REUSED.
  • A request that failed before the engine was reached is not remembered, so it may be retried with the same key.
  • replace is two writes — cancel, then submit the replacement — and the key records which of them succeeded. If the cancel is confirmed but the replacement fails, a retry with the same key resumes at the replacement: it does not cancel again, and it does not leave you with no order. A retry after both steps succeeded replays the first reply as usual.
  • Keys are kept for order_retention_sec and are lost when the gateway restarts.

client_tag is unrelated: it is an opaque, non-unique label (below).

Order correlation with client_tag

Order submit supports client_tag as an optional client-supplied correlation tag.

  • Scope: the tag is client-scoped and opaque.
  • Behavior: the gateway and engine do not enforce uniqueness.
  • Lifetime: when supplied, the tag is echoed on order lifecycle events and on cached order reads so clients can map exchange-assigned order_id values back to their own submissions. That includes events the client did not ask for — an unsolicited cancel, an expiry at the close — which are precisely the ones it cannot correlate any other way.

Unsolicited cancels and cancel_reason

An order.cancelled event does not always answer a DELETE. When the exchange cancels an order itself, request_tag is absent and cancel_reason says why:

cancel_reason Meaning
SELF_MATCH_PREVENTED The order would have traded against another order from the same gateway
INSUFFICIENT_LIQUIDITY A MARKET or IOC order exhausted the book; this cancels the remainder, which never rests
KILL_SWITCH The gateway's kill switch (KILL / KILL_ACK) cancelled this resting order
CIRCUIT_BREAKER_HALT A circuit-breaker halt cancelled this resting order
GATEWAY_DISCONNECT The owning gateway disconnected and its disconnect_behaviour cancelled this order
ADMIN_CANCEL_SYMBOL An operator-issued admin cancel for the symbol cancelled this order
QUOTE_REPLACED A market-maker's new quote replaced this quote leg
QUOTE_LEG_FILLED The sibling leg of a two-sided quote filled, cancelling this leg

Both fields are omitted rather than null when they do not apply, so request_tag absent with cancel_reason also absent means the exchange cancelled the order for a cause not yet classified under this vocabulary. cancel_reason is a separate vocabulary from reject_code: a cancel is not a rejection. New members may be added; existing members are never removed or renamed. Clients must ignore values they do not recognise. See Messages — order.cancelled.{gateway_id} for the same enum at the engine-message level.

Category examples

The examples below are normative shape examples for each category. Values are illustrative.

Trading example (POST /api/v1/orders)

Minimal request:

{
  "symbol": "AAPL",
  "side": "BUY",
  "order_type": "LIMIT",
  "quantity": 100,
  "price": 187.25
}

Full request:

{
  "symbol": "AAPL",
  "side": "BUY",
  "order_type": "LIMIT",
  "quantity": 100,
  "price": 187.25,
  "tif": "DAY",
  "smp_action": "CANCEL_AGGRESSOR",
  "client_tag": "desk1-aapl-00042"
}

Minimal response:

{
  "order_id": "ORD-20260806-00042",
  "status": "PENDING"
}

Full response:

{
  "order_id": "ORD-20260806-00042",
  "client_tag": "desk1-aapl-00042",
  "status": "ACKED",
  "accepted": true,
  "event": {
    "order_id": "ORD-20260806-00042",
    "accepted": true,
    "gateway_id": "G1"
  }
}

Reference data example (GET /api/v1/reference)

Minimal response:

{
  "symbols": [],
  "risk": {},
  "indexes": [],
  "schedule": {},
  "config_version": "sha256:..."
}

Full response:

{
  "symbols": [
    {
      "symbol": "AAPL",
      "tick_decimals": 2,
      "level": "L1"
    }
  ],
  "risk": {
    "default_level": "L1",
    "levels": {
      "L1": {
        "soft_pct": 5.0,
        "hard_pct": 10.0
      }
    }
  },
  "indexes": [
    {
      "id": "TECH10",
      "description": "Tech sample index"
    }
  ],
  "schedule": {
    "sessions_enabled": true,
    "country": "US"
  },
  "config_version": "sha256:6d8d..."
}

History example (GET /api/v1/history/trades)

Minimal response:

{
  "trades": [],
  "count": 0,
  "has_more": false
}

Full response:

{
  "trades": [
    {
      "symbol": "AAPL",
      "price": 187.3,
      "quantity": 50,
      "ts": "2026-08-06T09:30:00Z"
    }
  ],
  "count": 1,
  "has_more": true,
  "next_cursor": "eyJhZnRlciI6Ii4uLiJ9"
}

Admin example (POST /api/v1/admin/session/transition)

Minimal request:

{
  "to_state": "CONTINUOUS"
}

Full request:

{
  "to_state": "CONTINUOUS"
}

Minimal response:

{
  "requested_state": "CONTINUOUS",
  "status": "APPLIED",
  "command_id": "c_01K17P..."
}

Full response:

{
  "requested_state": "CONTINUOUS",
  "status": "APPLIED",
  "command_id": "c_01K17P9B7C6W9X0Y8Z7"
}

Compatibility and deprecation policy

  • Versioning: this surface is namespaced under /api/v1. The WebSocket streams under /api/v1/ws/ (the WebSocket API Reference) are part of the same v1 contract and follow every rule below.
  • Backward compatibility: additive changes (new optional fields/endpoints) MAY be introduced within v1 without changing the base path.
  • Breaking changes (field removal, required-field additions, semantic redefinition) MUST ship under a new versioned base path.
  • Deprecation: deprecated fields/endpoints SHOULD be documented with migration guidance before removal in a later major API version.
  • The contract: spec/rest/openapi.json is the v1 contract. Any change to it is a reviewed diff, and each diff is judged against these rules.
  • Additive in practice:
  • A new endpoint is additive.
  • A new optional request field is additive.
  • A new response field is additive. Clients must ignore unknown response fields.
  • A new error code is additive. Clients handle an unknown one by its HTTP status.
  • A new enum value in a response is additive only if the gateway and engine are deployed together. The gateway validates its responses, so a gateway older than the engine answers 500 on a value it does not know. EduMatcher releases both from one spec.
  • What clients MUST tolerate, so that the additive changes above stay additive for them:
  • Unknown fields, in any REST response body and in any WebSocket frame or data: ignore them.
  • Unknown enum values, in any enum (status, reject_code, cancel_reason, order_type, ...): treat them as "other", not as an error. New members may be added to any enum within v1.
  • Unknown WebSocket type values: ignore the frame. New event types may be added within v1.
  • Unknown error code values: handle them by HTTP status.
  • What is breaking for error codes: removing a code a route can return, changing a code's HTTP status or meaning, or returning a different code for a condition that already has one. Adding a code, or returning an existing code from one more route, is additive.

Bootstrap

Bootstrap endpoints return a single composed response that a browser client can fetch immediately after authenticating, replacing the 6–13 sequential round-trips the current login flow requires. Sub-queries inside each handler run in parallel using asyncio.gather.

Each response includes an incomplete array. When a field is listed there its value is null; the rest of the response is still usable. Required fields (marked below) return 503 ENGINE_TIMEOUT on failure — the response would be too incomplete to be useful without them.


GET /api/v1/bootstrap/trader

Purpose: one-request startup payload for TRADER, MARKET_MAKER, and ADMIN sessions. Returns identity, full reference data, live session state, positions, active orders, today's recent fills, and capability flags.

Access: any valid key (read-only keys receive gateway_role: "READ_ONLY", empty positions and orders and null recent_fills, but session and halts like any key).

Arguments

Name Type Req Description
fills_limit Int no Maximum number of today's fill events in recent_fills; 1..500, default 50

Reply

Status Shape Meaning
200 OK the object below Startup payload
{
  "ts": "2026-07-27T09:30:01.123Z",
  "incomplete": [],               // field names that timed out (values are null)

  "gateway_id": "TRADER01",       // null for read-only keys
  "gateway_role": "TRADER",       // TRADER | MARKET_MAKER | ADMIN | READ_ONLY
  "session_timezone": "Europe/Stockholm", // IANA name; the zone clients render wall-clock times in

  // identical to GET /reference
  "reference": {
    "symbols": [ { "symbol": "AAPL", "tick_decimals": 2, ... } ],
    "risk":     { "default_level": "L2", "levels": { ... } },
    "schedule": { "sessions_enabled": true, "country": "Sweden", "schedule": { ... } },
    "config_version": "7f3a2c1"
  },

  // identical to GET /session; null if engine timed out (optional)
  "session": { "state": "CONTINUOUS", "sessions_enabled": true, "next": { ... },
               "session_timezone": "Europe/Stockholm", "session_date": "2026-07-27" },

  // identical to GET /positions: an engine round trip (required — 503 on failure)
  "positions": [ { "symbol": "AAPL", "net_qty": 200, "avg_cost": 208.40, "last_price": 210.25,
                   "market_value": 42050.0, "unrealized_pnl": 370.0,
                   "realized_pnl_session": 0.0, "realized_pnl_mtd": 0.0, "realized_pnl_ytd": 0.0 } ],

  // the `orders` list of GET /orders (required — 503 on failure)
  "orders": [ /* OrderDisplay[] */ ],

  // today's fill events, at most fills_limit; null if stats DB absent (optional)
  "recent_fills": [ /* OrderEvent[] */ ],

  // the `halted` list of GET /halts; null if engine timed out (optional)
  "halts": [ ],

  // capability flags — assembled from config, never null
  "capabilities": {
    "sessions_enabled": true,    // from reference.schedule.sessions_enabled
    "stats_db_available": true,  // false → history unavailable
    "audit_db_available": false, // false → order lifecycle drill-down unavailable
    "index_available": false     // false → index tab unavailable
  }
}

Required fields (return 503 ENGINE_TIMEOUT if they fail): reference, orders and positions (the last two are empty, without an engine query, for read-only keys).

Optional fields (appear as null + listed in incomplete on failure): session, recent_fills, halts.

Errors

Code Status When
VALIDATION 400 fills_limit is not an integer in 1–500
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ENGINE_TIMEOUT 503 reference, orders or positions could not be fetched

GET /api/v1/bootstrap/mm

Purpose: one-request startup payload for MARKET_MAKER sessions. Superset of /bootstrap/trader — adds active quote bootstrap state and quote legs.

Access: MARKET_MAKER key only. TRADER and ADMIN keys receive 403.

Arguments

Name Type Req Description
fills_limit Int no As for /bootstrap/trader

Reply

Status Shape Meaning
200 OK the object below Startup payload

All fields from /bootstrap/trader, plus:

{
  // ...trader fields...
  "gateway_role": "MARKET_MAKER",

  // the `quotes` list of GET /quotes; null if engine timed out (optional)
  "quotes": [ /* ActiveQuote[] */ ],

  // the `legs` list of GET /quotes/legs; null if engine timed out (optional)
  "quote_legs": [ /* QuoteLeg[] */ ]
}

Required fields: reference, orders, positions.

Optional fields: session, recent_fills, halts, quotes, quote_legs.

Errors

Code Status When
VALIDATION 400 fills_limit is not an integer in 1–500
READ_ONLY 403 Read-only key (no gateway_id)
ROLE_DENIED 403 Key resolves to TRADER or ADMIN role
ENGINE_TIMEOUT 503 reference, orders or positions could not be fetched

GET /api/v1/bootstrap/admin

Purpose: one-request startup payload for ADMIN sessions. Returns reference data, session state, the full gateway roster, active halts, per-gateway active-order counts, per-gateway drop-copy sequence numbers, and capability flags.

Access: ADMIN role required. TRADER and MARKET_MAKER keys receive 403.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK the object below Startup payload
{
  "ts": "2026-07-27T09:30:01.250Z",
  "incomplete": [],

  "gateway_id": "INSTRUCTOR",
  "gateway_role": "ADMIN",

  // identical to GET /reference (required — 503 on failure)
  "reference": { /* symbols, risk, schedule, config_version */ },

  // identical to GET /session; null if engine timed out (optional)
  "session": { "state": "CONTINUOUS", "sessions_enabled": true, "session_timezone": "...", "session_date": "..." },

  // the `gateways` list of GET /admin/gateways; null if engine timed out (optional)
  "gateways": [
    { "id": "TRADER01", "role": "TRADER", "description": "...", "connected": true }
  ],

  // the `halted` list of GET /halts; null if engine timed out (optional)
  "halts": [ { "symbol": "AAPL", "level": "L2", ... } ],

  // per-gateway active (non-terminal) order count — pure cache, never null
  "active_order_counts": { "TRADER01": 3, "MM01": 12 },

  // each active gateway's private-stream stream_seq now — pure cache, never null
  "monitor_last_seq": { "TRADER01": 100482, "MM01": 88213 },

  "capabilities": {
    "sessions_enabled": true,
    "stats_db_available": true,
    "audit_db_available": false,
    "index_available": false
  }
}

Required fields: reference.

Optional fields: session, gateways, halts. The gateways and halts fields are also supplied by the monitor.snapshot frame that the WS /api/v1/ws/admin/monitor socket sends immediately after auth — a partial bootstrap response is therefore recoverable without a manual retry.

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Key is not ADMIN
ENGINE_TIMEOUT 503 reference could not be fetched

Trading REST

POST /api/v1/orders

Purpose: submit one order for the caller's gateway.

Arguments

Name Type Req Description
symbol Symbol yes Instrument symbol
side Side yes BUY or SELL
order_type OrderType yes MARKET, LIMIT, STOP, STOP_LIMIT, FOK, ICEBERG, IOC, or TRAILING_STOP
quantity Int yes Positive order quantity
tif Tif no Time-in-force; defaults to DAY
price Price conditional Required for LIMIT, FOK, IOC, STOP_LIMIT, ICEBERG
stop_price Price conditional Required for STOP, STOP_LIMIT; optional initial stop for TRAILING_STOP (default: last trade ± trail_offset)
visible_qty Qty conditional Required for ICEBERG; must be less than quantity
trail_offset Price conditional Required for TRAILING_STOP
smp_action SmpAction no Self-match prevention action
client_tag Str no Optional opaque client correlation tag
wait Bool no ?wait=ack waits for the engine's verdict (see Write outcomes and safe retries)
Idempotency-Key header no Makes a retry safe; see Write outcomes and safe retries

Reply

Status Shape Meaning
202 Accepted {"order_id": "...", "status": "PENDING", "ack_timeout": false, ...} Default immediate reply
200 OK {"order_id": "...", "status": "ACKED", "accepted": false, "reject_code": "COLLAR_BREACH", "event": {..., "reason": "collar breach"}, "ack_timeout": false} Returned when ?wait=ack waits for the matching ACK; a rejected ACK carries the stable machine-readable reject_code, and the engine's order.ack in event has the human-readable reason
202 Accepted {"order_id": "...", "status": "PENDING", "ack_timeout": true, ...} ?wait=ack and the ACK did not arrive in time; the order was sent

Takes an optional Idempotency-Key header; see Write outcomes and safe retries.

Errors

Code Status When
VALIDATION 400 Body does not match the order type, or a price is not an exact multiple of the instrument's tick size (reject_code: TICK_VIOLATION)
READ_ONLY 403 Read-only credential used
IDEMPOTENCY_KEY_REUSED 409 The Idempotency-Key was used for a different request
RATE_LIMIT 429 Write limit exceeded
SYMBOLS_NOT_READY 503 Symbol metadata has not reached the gateway yet (reject_code: SYMBOL_NOT_READY)

A conditional field the order_type does not use is rejected (400 VALIDATION, reject_code: UNSUPPORTED_FIELD, for example "LIMIT forbids trail_offset"), never silently dropped; a missing one is MISSING_FIELD. The same per-type rule applies to OCO and combo legs, and ALF, BALF and the engine apply it too, so an order gets the same reject_code on every transport.

Prices are refused rather than rounded to the nearest tick: rounding would rest the order at a price the client never sent. See Tick precision.

DELETE /api/v1/orders/{order_id}

Purpose: cancel one live order in the caller's gateway.

Arguments

Name Type Req Description
order_id Str yes (path) Order to cancel
wait Bool no ?wait=ack waits for order.cancelled.*
request_tag Str no Query parameter echoed on order.cancelled.* or rejected ACK; max 64 chars

Reply

Status Shape Meaning
202 Accepted {"order_id": "...", "request_tag": "...", "status": "PENDING_CANCEL"} Default: cancel sent; the outcome follows on the private WebSocket
200 OK {"order_id": "...", "status": "ACKED" \| "REJECTED", "accepted": ..., "reject_code": ..., "event": {...}} With ?wait=ack: the cancel's outcome
202 Accepted {..., "status": "PENDING_CANCEL", "ack_timeout": true} ?wait=ack and the outcome did not arrive in time; the cancel was sent

Without a request_tag, ?wait=ack waits for order.cancelled alone, so a refused cancel shows as an ack timeout; pass a request_tag to get it as REJECTED.

Errors

Code Status When
VALIDATION 400 Bad path or query
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

PATCH /api/v1/orders/{order_id}

Purpose: amend price and/or quantity on one live order.

Arguments

Name Type Req Description
order_id Str yes (path) Order to amend
price Price conditional New order price
quantity Qty conditional New order quantity
request_tag Str no Body field echoed on order.amended.* or rejected ACK; max 64 chars
wait Bool no ?wait=ack waits for order.amended.*

Reply

Status Shape Meaning
202 Accepted {"order_id": "...", "request_tag": "...", "status": "PENDING_AMEND"} Default: amend sent; the outcome follows on the private WebSocket
200 OK {"order_id": "...", "status": "ACKED" \| "REJECTED", "accepted": ..., "reject_code": ..., "event": {...}} With ?wait=ack: the amend's outcome
202 Accepted {..., "status": "PENDING_AMEND", "ack_timeout": true} ?wait=ack and the outcome did not arrive in time; the amend was sent

Errors

Code Status When
VALIDATION 400 Neither or both fields invalid
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

POST /api/v1/orders/{order_id}/replace

Purpose: cancel one live order and submit a replacement in one workflow.

Arguments

Name Type Req Description
order_id Str yes (path) Order to replace
body Order fields yes Same shape as POST /orders, on the same symbol
wait Bool no ?wait=ack also waits for the replacement's order.ack
Idempotency-Key header no See Write outcomes and safe retries

The gateway always waits for the original's cancel, and sends the replacement only once the engine has confirmed it.

Reply

Status Shape Meaning
202 Accepted {"cancelled_order_id": "...", "replacement_order_id": "...", "status": "PENDING", "accepted": null, ...} Original cancelled; replacement sent
200 OK {..., "status": "ACKED", "accepted": true \| false, "reject_code": ..., "event": {...}} With ?wait=ack: the replacement's verdict

Errors

Code Status When
VALIDATION 400 Replacement body invalid, or names another symbol than the original (field: symbol)
READ_ONLY 403 Read-only credential used
IDEMPOTENCY_KEY_REUSED 409 The Idempotency-Key was used for a different request
REPLACE_REJECTED 409 The engine refused the cancel (the order already filled, say; reject_code says why); nothing was sent
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 The cancel's outcome did not arrive in time; no replacement was sent
SYMBOLS_NOT_READY 503 The symbol's metadata is not loaded; nothing was cancelled

GET /api/v1/orders

Purpose: return the caller gateway's orders as the engine holds them: resting orders plus recently finished ones.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "orders": [...] } The engine's order snapshot for the caller

Errors

Code Status When
READ_ONLY 403 Read-only credential used
ENGINE_TIMEOUT 503 The engine did not answer in time. There is no fallback to the gateway's cache; the private WebSocket orders.snapshot is the cached view

GET /api/v1/orders/{order_id}

Purpose: return one cached order from the caller gateway.

Arguments

Name Type Req Description
order_id Str yes (path) Order to fetch

Reply

Status Shape Meaning
200 OK cached order object Current order state
404 Not Found error envelope Order id not in cache

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
UNKNOWN_ORDER 404 Order id unknown to the cache

POST /api/v1/oco

Purpose: submit an OCO pair.

Arguments

Name Type Req Description
oco_id Str yes Your id for the pair, unique among your live pairs: reusing one is refused (accepted: false). Free again once the pair is done
symbol Symbol yes Instrument symbol
quantity Qty yes Total quantity
leg1 OrderLeg yes First leg definition: side, order_type, and the price / stop_price / trail_offset that type needs (same rule as POST /orders; ICEBERG is not supported on a leg)
leg2 OrderLeg yes Second leg definition, same shape
tif Tif no Time-in-force of both legs; defaults to DAY
client_tag Str no Opaque label echoed on both legs' events; max 64 chars
wait Bool no ?wait=ack waits for the engine's verdict (see Write outcomes and safe retries)
Idempotency-Key header no Makes a retry safe; see Write outcomes and safe retries

Reply

Status Shape Meaning
202 Accepted { "oco_id": "...", "status": "PENDING", "accepted": null, "reason": null, "event": null, "ack_timeout": false } Sent; the verdict follows on the private WebSocket (oco.ack)
200 OK { "oco_id": "...", "status": "ACKED", "accepted": true \| false, "reason": ..., "event": {...}, "ack_timeout": false } With ?wait=ack: the engine's oco.ack
202 Accepted {..., "status": "PENDING", "ack_timeout": true} ?wait=ack and the verdict did not arrive in time; it was sent

Takes ?wait=ack and an optional Idempotency-Key header; see Write outcomes and safe retries.

Errors

Code Status When
VALIDATION 400 Body invalid
READ_ONLY 403 Read-only credential used
IDEMPOTENCY_KEY_REUSED 409 The Idempotency-Key was used for a different request
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
SYMBOLS_NOT_READY 503 Order entry before the symbol's tick precision reached the gateway. Transient; retry

DELETE /api/v1/oco/{oco_id}

Purpose: cancel an OCO pair.

Arguments

Name Type Req Description
oco_id Str yes (path) One of your own OCO pairs; another gateway's id is ORDER_NOT_FOUND
request_tag Str no Query: echoed on the engine's oco.cancel_ack; max 64 chars
wait Bool no ?wait=ack waits for the oco.cancel_ack

Reply

Status Shape Meaning
202 Accepted { "oco_id": "...", "request_tag": ..., "status": "PENDING_CANCEL", "accepted": null, ..., "ack_timeout": false } Default: cancel sent; the outcome follows on the private WebSocket (oco.cancel_ack)
200 OK { ..., "status": "ACKED" \| "REJECTED", "accepted": ..., "reject_code": ..., "event": {...} } With ?wait=ack: the engine's oco.cancel_ack. A refusal (ORDER_NOT_FOUND, ORDER_ALREADY_TERMINAL) is REJECTED, not an error
202 Accepted { ..., "status": "PENDING_CANCEL", "ack_timeout": true } ?wait=ack and the ack did not arrive in time; the cancel was sent

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

POST /api/v1/combos

Purpose: submit a combo order.

Arguments

Name Type Req Description
combo_id Str yes Combo group id
legs List<ComboLeg> yes 2–10 legs: symbol, side, order_type (default LIMIT), quantity, and the price / stop_price that type needs (same rule as POST /orders; ICEBERG and TRAILING_STOP are not supported on a leg)
combo_type ComboType no Fill rule; AON (all or none), the default and only value
tif Tif no Time-in-force of every leg; defaults to DAY
smp_action SmpAction no Self-match prevention action, as on POST /orders
client_tag Str no Opaque label echoed on every leg's events; max 64 chars
wait Bool no ?wait=ack waits for the engine's verdict (see Write outcomes and safe retries)
Idempotency-Key header no Makes a retry safe; see Write outcomes and safe retries

Reply

Status Shape Meaning
202 Accepted { "combo_id": "...", "status": "PENDING", "accepted": null, "reason": null, "event": null, "ack_timeout": false } Sent; the verdict follows on the private WebSocket (combo.ack)
200 OK { "combo_id": "...", "status": "ACKED", "accepted": true \| false, "reason": ..., "event": {...}, "ack_timeout": false } With ?wait=ack: the engine's combo.ack
202 Accepted {..., "status": "PENDING", "ack_timeout": true} ?wait=ack and the verdict did not arrive in time; it was sent

Takes ?wait=ack and an optional Idempotency-Key header; see Write outcomes and safe retries.

Errors

Code Status When
VALIDATION 400 Body invalid
READ_ONLY 403 Read-only credential used
IDEMPOTENCY_KEY_REUSED 409 The Idempotency-Key was used for a different request
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
SYMBOLS_NOT_READY 503 Order entry before the symbol's tick precision reached the gateway. Transient; retry

DELETE /api/v1/combos/{combo_id}

Purpose: cancel a combo order.

Arguments

Name Type Req Description
combo_id Str yes (path) Combo group to cancel
request_tag Str no Query: echoed on the engine's combo.cancel_ack; max 64 chars
wait Bool no ?wait=ack waits for the combo.cancel_ack

Reply

Status Shape Meaning
202 Accepted { "combo_id": "...", "request_tag": ..., "status": "PENDING_CANCEL", "accepted": null, ..., "ack_timeout": false } Default: cancel sent; the outcome follows on the private WebSocket (combo.cancel_ack)
200 OK { ..., "status": "ACKED" \| "REJECTED", "accepted": ..., "reject_code": ..., "event": {...} } With ?wait=ack: the engine's combo.cancel_ack. A refusal (ORDER_NOT_FOUND, ORDER_ALREADY_TERMINAL) is REJECTED, not an error
202 Accepted { ..., "status": "PENDING_CANCEL", "ack_timeout": true } ?wait=ack and the ack did not arrive in time; the cancel was sent

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

POST /api/v1/quotes

Purpose: submit a two-sided market-maker quote.

Arguments

Name Type Req Description
symbol Symbol yes Quoted instrument
bid_price Price yes Bid price
bid_qty Qty yes Bid quantity
ask_price Price yes Ask price
ask_qty Qty yes Ask quantity
tif Tif no Time-in-force of both sides; defaults to DAY
quote_id Str no Your id for the quote; defaults to the symbol
wait Bool no ?wait=ack waits for the engine's verdict (see Write outcomes and safe retries)
Idempotency-Key header no Makes a retry safe; see Write outcomes and safe retries

Reply

Status Shape Meaning
202 Accepted { "symbol": "...", "quote_id": "..." \| null, "status": "PENDING", "accepted": null, "reason": null, "event": null, "ack_timeout": false } Sent; the verdict follows on the private WebSocket (quote.ack)
200 OK { "symbol": "...", ..., "status": "ACKED", "accepted": true \| false, "reason": ..., "event": {...} } With ?wait=ack: the engine's quote.ack
202 Accepted {..., "status": "PENDING", "ack_timeout": true} ?wait=ack and the verdict did not arrive in time; it was sent

Takes ?wait=ack and an optional Idempotency-Key header; see Write outcomes and safe retries.

Errors

Code Status When
VALIDATION 400 Quote body invalid
READ_ONLY 403 Read-only credential used
IDEMPOTENCY_KEY_REUSED 409 The Idempotency-Key was used for a different request
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
SYMBOLS_NOT_READY 503 Order entry before the symbol's tick precision reached the gateway. Transient; retry

DELETE /api/v1/quotes/{symbol}

Purpose: cancel the active quote for one symbol.

Arguments

Name Type Req Description
symbol Symbol yes (path) Quoted instrument
request_tag Str no Query: echoed on the engine's quote.cancel_ack; max 64 chars
wait Bool no ?wait=ack waits for the quote.cancel_ack

Reply

Status Shape Meaning
202 Accepted { "symbol": "...", "request_tag": ..., "status": "PENDING_CANCEL", "accepted": null, ..., "ack_timeout": false } Default: cancel sent; the outcome follows on the private WebSocket (quote.cancel_ack)
200 OK { ..., "status": "ACKED" \| "REJECTED", "accepted": ..., "reject_code": ..., "event": {...} } With ?wait=ack: the engine's quote.cancel_ack. A refusal (ORDER_NOT_FOUND, ORDER_ALREADY_TERMINAL) is REJECTED, not an error
202 Accepted { ..., "status": "PENDING_CANCEL", "ack_timeout": true } ?wait=ack and the ack did not arrive in time; the cancel was sent

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

POST /api/v1/mass-cancel

Purpose: cancel all resting exposure for the caller or one symbol.

Arguments

Name Type Req Description
symbol Symbol no Restrict the cancel to one symbol

Reply

Status Shape Meaning
200 OK kill-switch ack What the engine cancelled (the call waits for the ack)

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not ACK in time

GET /api/v1/symbols

Purpose: return instrument metadata for the caller gateway.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "symbols": [...] } Current symbol metadata

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/news

Purpose: return the latest market-sim headlines, oldest first, plus the sector → symbol mapping. Access: Authenticated key (read-only keys included) — unlike most endpoints in this section, it does not require a trading key, since this is public information, not a trading action.

Arguments

Name Type Req Description
limit int No Maximum headlines to return, 1-200 (default 50)

Reply

Status Shape Meaning
200 OK { "news": [...], "sectors": {...} } news is a list of news.event-shaped objects (see Message Reference — news.event), oldest first; sectors maps each sector name to its sorted list of symbols. Empty news list when pm-market-sim is not running.

Errors

Code Status When
VALIDATION 400 limit outside 1-200

GET /api/v1/companies/{symbol}

Purpose: return the company profile behind one symbol, from the company database pm-compdb-cli init builds (companies.db in the data directory). The gateway opens it read-only. Access: Authenticated key (read-only keys included) — the profiles are public information.

Arguments

Name Type Req Description
symbol path Yes Ticker, case-insensitive

Reply

Status Shape Meaning
200 OK { "symbol": "AAPL", "name": ..., "pe_ratio": ..., ... } Every column of company_view: the stored fields plus the derived years_in_business, ceo_tenure_years, pe_ratio, ps_ratio and net_margin_pct. Money is in millions of USD; a field the database has no value for is null. pm-compdb-cli columns lists them all.

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
UNKNOWN_COMPANY 404 The database has no profile for symbol
COMPANY_DB_UNAVAILABLE 503 No companies.db in the data directory, or it cannot be read

GET /api/v1/session

Purpose: return the venue's session phase and the locale clients render it in. Open to every key, read-only included.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "state", "sessions_enabled", "next", "session_timezone", "session_date" } state and next come from the engine; session_timezone (IANA) is the exchange timezone every wall-clock time and history date filter is in, and session_date today's trading date in it

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/halts

Purpose: return the currently-halted symbols table, for any key, read-only included. Halt state is public: every client needs it at bootstrap and again on every market-data reconnect, or a halt missed while disconnected leaves a stale badge for the rest of the session.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "halted": [...] } Currently-halted symbols; each { symbol, resume_at_ns?, level?, halt_source? }

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/status

Purpose: return who the caller is: its gateway id and engine role.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "gateway_id", "gateway_role", "gateway_count"? } gateway_role is TRADER, MARKET_MAKER or ADMIN; ADMIN keys also get gateway_count, the connected gateways

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint

GET /api/v1/healthz

Purpose: liveness probe for the API gateway.

Arguments

Name Type Req Description
none — — No auth required

Reply

Status Shape Meaning
200 OK { "ok": true, ... } Gateway is enabled and its engine listener is alive
503 Service Unavailable { "ok": false, ... } Gateway is disabled or its engine listener has stopped; probes read the status code

Errors

Code When
none This endpoint does not require auth

GET /api/v1/quotes

Purpose: return the caller's active market-maker quotes: both legs' order ids, sizes, remaining quantities, statuses and prices.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "quotes": [...] } The caller's active quotes

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/quotes/legs

Purpose: return the current quote-leg state for the caller.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "legs": [...] } Current quote legs

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/positions

Purpose: return current net positions by symbol.

To query a different gateway's position (e.g. checking on a running pm-mm-bot), see GET /api/v1/admin/gateways/{gateway_id}/positions, which requires the ADMIN role.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "positions": [{ "symbol", "net_qty", "avg_cost", "last_price", "market_value", "unrealized_pnl", "realized_pnl_session", "realized_pnl_mtd", "realized_pnl_ytd" }, ...] } Net positions for the caller gateway, from the engine; P&L fields are null when unknown

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ENGINE_TIMEOUT 503 Engine did not reply in time

Reference data

Base path: /api/v1/reference. These endpoints expose compiled reference data that changes only when an admin reloads it.

GET /api/v1/reference

Purpose: return the full reference bundle in one round-trip.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "symbols", "risk", "indexes", "schedule", "config_version" } Full resolved reference bundle

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/reference/config-version

Purpose: return the content-hash version of the reference bundle.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "config_version": "..." } Opaque bundle version hash

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/reference/symbols

Purpose: return per-symbol tick and risk metadata.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "symbols": [...], "config_version": "..." } One object per symbol (each carries its own symbol)

Each symbol object carries tick_decimals, level, and — when configured — collar, order_limits (max_order_qty / max_order_value) and circuit_breaker. collar is the symbol's effective band, its risk level's defaults already merged under any per-symbol override; order_limits is configured per symbol only, so it is simply what that symbol declared. Either is omitted entirely when not configured. Full field law: ReferenceSymbol in the message reference.

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/reference/risk

Purpose: return risk-band definitions and the default risk level.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "default_level", "levels", "config_version" } Risk-band configuration

Each entry of levels carries its name and, when the level configures one, collar — as configured, before any per-symbol override. Levels do not carry order limits; those are per symbol and appear on /reference/symbols. Full field law: RiskLevel in the message reference.

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/reference/indexes

Purpose: return configured exchange index definitions.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "indexes": [...], "config_version": "..." } Static index definitions

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/reference/schedule

Purpose: return session schedule metadata.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK schedule object sessions_enabled, country, and session transition times

Errors

Code Status When
ENGINE_TIMEOUT 503 Engine did not reply in time

Market data

Base path: /api/v1/book. The order book as of now, without a WebSocket. Both endpoints read the gateway's copy of the book.{symbol} stream, the one a WebSocket snapshot is answered from, so a REST read and a WebSocket subscription agree on the same snapshot.

  • seq is the market-data sequence of the snapshot on topic book.{symbol}, the seq its WebSocket book frame carries. A client that reads here and then subscribes ignores book frames whose seq is at or below it (the subscription replays the latest snapshot first).
  • ts_ns is when the engine produced the snapshot. The engine publishes a book when it changes, so a quiet book keeps an old ts_ns and is still current.
  • Every price is a display price with a *_ticks sibling: the integer price * 10**tick_decimals (see Prices in Response contract). Use the ticks to compare or sum prices exactly.
  • Iceberg orders contribute their displayed size only.

GET /api/v1/book/{symbol}

Purpose: return one symbol's latest book: bid and ask levels (best first), the last trade, and the last five prints. Access: Authenticated key (read-only keys included) — market data is public.

Arguments

Name Type Req Description
symbol path Yes Ticker, case-insensitive
levels query No Price levels per side, 1–100, default 10

Reply

Status Shape Meaning
200 OK { "symbol", "tick_decimals", "ts_ns", "seq", "bids": [...], "asks": [...], "last_price", "last_price_ticks", "last_qty", "last_buy_price", "last_buy_price_ticks", "last_sell_price", "last_sell_price_ticks", "recent_trades": [...] } bids descending and asks ascending, each level { price, price_ticks, qty, count }; last_* are null until the symbol has traded; recent_trades are the last five prints, oldest first, each as in trade.executed plus price_ticks

Errors

Code Status When
VALIDATION 400 levels outside 1–100, or symbol over-long
UNKNOWN_SYMBOL 404 symbol is not in the reference data
ENGINE_TIMEOUT 503 The engine did not publish the book in time (only on the first read of a book the gateway has not seen)

GET /api/v1/book

Purpose: return best bid, best ask and last price for every symbol in the reference data, sorted by symbol, in one call. Access: Authenticated key (read-only keys included).

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "books": [...], "count": n } One entry per symbol: { symbol, tick_decimals, ts_ns, seq, best_bid, best_ask, last_price, last_price_ticks }. A side with no orders is null; best_bid and best_ask are levels as above

Errors

Code Status When
ENGINE_TIMEOUT 503 The engine did not reply in time. The list is all or nothing: one book that cannot be had fails the call

History

Base path: /api/v1/history. Trading endpoints are scoped to the caller's gateway id; public history accepts any valid key.

GET /api/v1/history/orders

Purpose: return the caller gateway's order lifecycle events.

Arguments

Name Type Req Description
symbol Symbol no Filter by symbol
event_type Str no Filter by event type
date Date no Single trading date
from DateTime no Range start
to DateTime no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Order lifecycle events for the caller gateway

Errors

Code Status When
VALIDATION 400 Query invalid
READ_ONLY 403 Read-only credential used
STATS_DB 503 pm-stats database missing

GET /api/v1/history/orders/{order_id}

Purpose: return the full lifecycle of one order for the caller gateway.

Arguments

Name Type Req Description
order_id Str yes (path) Order to fetch

Reply

Status Shape Meaning
200 OK { "order_id", "count", "events" } Full lifecycle for one order

Errors

Code Status When
VALIDATION 400 Path invalid
READ_ONLY 403 Read-only credential used
STATS_DB 503 pm-stats database missing

GET /api/v1/history/fills

Purpose: return fill events for the caller gateway.

Arguments

Name Type Req Description
symbol Symbol no Filter by symbol
date Date no Trading date
from DateTime no Range start
to DateTime no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Fill history for the caller gateway

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 Read-only credential used
STATS_DB 503 pm-stats database missing

GET /api/v1/history/trades

Purpose: return public trade tape rows.

Arguments

Name Type Req Description
symbol Symbol no Filter by symbol
date Date no Trading date
from DateTime no Range start
to DateTime no Range end
limit Int no Page size
after Cursor no Opaque page cursor
order asc | desc no asc (default) is oldest first; desc is newest first, so limit returns the most recent trades. The cursor continues in the chosen direction

Reply

Status Shape Meaning
200 OK paginated list envelope Public trade rows

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/daily

Purpose: return daily OHLCV rows.

Arguments

Name Type Req Description
symbol Symbol no Filter by symbol
date Date no Single trading day
from Date no Range start
to Date no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Daily OHLCV rows

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/price-snapshots

Purpose: return intraday price snapshots.

Arguments

Name Type Req Description
symbol Symbol yes Instrument symbol
date Date no Trading day
from DateTime no Range start
to DateTime no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Intraday mid/bid/ask series

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/index-daily

Purpose: return daily index OHLC rows.

Arguments

Name Type Req Description
index_id IndexId no Index id
date Date no Single trading day
from Date no Range start
to Date no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Daily index rows

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/index-snapshots

Purpose: return intraday index snapshots.

Arguments

Name Type Req Description
index_id IndexId yes Index id
date Date no Trading day
from DateTime no Range start
to DateTime no Range end
limit Int no Page size
after Cursor no Opaque page cursor

Reply

Status Shape Meaning
200 OK paginated list envelope Intraday index series

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/indexes

Purpose: list index ids with recorded data.

Arguments

Name Type Req Description
date Date no Filter by trading day

Reply

Status Shape Meaning
200 OK { "index_ids": [...], "count": N } Index ids with data

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
STATS_DB 503 pm-stats database missing

GET /api/v1/history/index-events

Purpose: return index structural and audit events from pm-index.

Arguments

Name Type Req Description
index_id IndexId yes Index id
from Timestamp no ISO-8601 or YYYY-MM-DD, like every history range (no offset = exchange time); default 30 days ago
to Timestamp no Same form; default now
types Str no Repeatable event-type filter
max_records Int no Reply cap

Reply

Status Shape Meaning
200 OK { "events": [...], "count": N } Structural audit log

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
INDEX_ERROR 502 pm-index rejected the request
INDEX_TIMEOUT 503 pm-index did not reply in time

Admin REST

Base path: /api/v1/admin. ADMIN role required.

POST /api/v1/admin/session/transition

Purpose: request a session-phase transition from the engine.

Arguments

Name Type Req Description
to_state SessionState yes Requested session phase

Reply

Status Shape Meaning
200 OK {"requested_state": "...", "status": "APPLIED", "command_id": "..."} Transition applied
409 Conflict error envelope The engine refused the transition

Errors

Code Status When
VALIDATION 400 Invalid to_state
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
TRANSITION_REJECTED 409 Engine rejected the transition
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/sim

Purpose: the market simulator's latest state and the model's true (hidden) fair value per symbol, from pm-market-sim. For instructors: students see only prices, never these values.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "available": true, "state": {...}, "values": {...} } available is false, and state/values null, when the simulator is not running

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 The key's gateway is not ADMIN

GET /api/v1/admin/gateways

Purpose: list configured gateways and live connection state.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "gateways": [...] } Gateway roster and connection status

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/gateways/{gateway_id}/disconnect

Purpose: forcibly disconnect one gateway.

Arguments

Name Type Req Description
gateway_id GatewayId yes (path) Gateway to disconnect (case-insensitive)
reason Str no Body, optional: note recorded in the engine log (default admin disconnect)

Reply

Status Shape Meaning
202 Accepted { "gateway_id": "...", "status": "PENDING_DISCONNECT" } Disconnect sent; not awaited — the roster in GET /admin/gateways shows the result

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause

POST /api/v1/admin/symbols/{symbol}/halt

Purpose: halt one symbol through the engine's circuit-breaker path. The body is optional.

Arguments

Name Type Req Description
symbol Symbol yes (path) Symbol to halt (case-insensitive)
level Str no A rung of the symbol's ladder; omit for an indefinite halt until resumed
reason Str no Optional note

Reply

Status Shape Meaning
200 OK halt ack The symbol is halted
409 Conflict error envelope The engine refused the halt

Errors

Code Status When
VALIDATION 400 Body invalid
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
HALT_REJECTED 409 Engine refused (unknown symbol, no circuit breaker, unknown level); message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/symbols/{symbol}/resume

Purpose: resume one halted symbol.

Arguments

Name Type Req Description
symbol Symbol yes (path) Symbol to resume (case-insensitive)
reason Str no Body, optional: note for the audit log

Reply

Status Shape Meaning
200 OK resume ack The symbol is trading again
409 Conflict error envelope The engine refused the resume

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
RESUME_REJECTED 409 Engine refused (for example the symbol is not halted); message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/risk/state

Purpose: return live per-symbol risk state.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "symbols": [...] } Current collar and circuit-breaker state (one object per symbol)

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/gateways/{gateway_id}/orders

Purpose: one gateway's resting and recently finished orders as the engine holds them: the authoritative view. GET /admin/orders is the gateway's own read model across all gateways, faster and filterable but limited to what this gateway has seen since it started.

Arguments

Name Type Req Description
gateway_id GatewayId yes (path) Whose orders (case-insensitive)

Reply

Status Shape Meaning
200 OK { "orders": [...], "count": N } Same shape as GET /orders

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/symbols/{symbol}/orders

Purpose: every resting order on one symbol across all gateways, each with its gateway_id, ordered by price then arrival (time priority). With price, only that level: the per-order composition behind the book's aggregated levels.

Arguments

Name Type Req Description
symbol Symbol yes (path) Instrument (case-insensitive)
price Price no Query: only this price level

Reply

Status Shape Meaning
200 OK { "symbol", "price"?, "rejected": false, "orders": [...], "count": N } The resting orders

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
QUERY_REJECTED 409 Engine refused (for example Unknown symbol: XYZ)
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/orders

Purpose: return the cross-gateway active-order table.

Arguments

Name Type Req Description
symbol Symbol no Filter by symbol
gateway_id GatewayId no Filter by gateway
status OrderStatus no Filter by status

Reply

Status Shape Meaning
200 OK { "count": N, "orders": [...], "retention_sec": N } Current cross-gateway active orders

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN

GET /api/v1/admin/orders/{order_id}/audit

Purpose: return the full cross-gateway lifecycle of one order.

Arguments

Name Type Req Description
order_id Str yes (path) Order to fetch
limit Int no Maximum number of events

Reply

Status Shape Meaning
200 OK { "order_id", "count", "events" } Full audited lifecycle

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
UNKNOWN_ORDER 404 No audited events for the order
AUDIT_INDEX_UNAVAILABLE 503 No audit index available

GET /api/v1/admin/gateways/{gateway_id}/positions

Purpose: return net position and average cost for any one gateway (not just the caller's own) -- the admin counterpart to GET /api/v1/positions, which serves the same engine round trip for the caller enriched with prices and P&L. It is the REST equivalent of pm-alf-console's and pm-alf-gwy's POS|GW=<gateway_id> command -- all three read the same system.position_request / system.position_snapshot.<gateway_id> pair, so they always agree.

Arguments

Name Type Req Description
gateway_id GatewayId yes (path) Gateway to query (any gateway, case-insensitive)

Reply

Status Shape Meaning
200 OK { "gateway_id", "count", "positions": [{ "symbol", "net_qty", "avg_cost" }, ...] } Net position and average cost per symbol for the target gateway

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/symbols/{symbol}/cancel-orders

Purpose: cancel all resting exposure on one symbol, across every gateway.

Arguments

Name Type Req Description
symbol Symbol yes (path) Symbol to clear (case-insensitive)
reason Str no Body, optional: note for the audit log

Reply

Status Shape Meaning
200 OK kill-switch ack What the engine cancelled
409 Conflict error envelope The engine refused the command

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
KILL_SWITCH_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/gateways/{gateway_id}/kill-switch

Purpose: cancel all resting exposure for one target gateway, without disconnecting it.

Arguments

Name Type Req Description
gateway_id GatewayId yes (path) Gateway whose orders and quotes to cancel (case-insensitive)
symbol Symbol no Query: only this instrument; omit for all
reason Str no Body, optional: note for the audit log

The target need not be connected, and the audit trail names the ADMIN.

Reply

Status Shape Meaning
200 OK kill-switch ack What the engine cancelled; symbol is echoed when the cancel was scoped
409 Conflict error envelope The engine refused the command

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
KILL_SWITCH_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

DELETE /api/v1/admin/gateways/{gateway_id}/quotes/{symbol}

Purpose: withdraw one participant's two-sided quote on one symbol, leaving its other orders. Runs through the ADMIN kill switch (quotes_only), so it works whether or not that gateway is connected and the audit trail names the ADMIN.

Arguments

Name Type Req Description
gateway_id GatewayId yes (path) Whose quote (case-insensitive)
symbol Symbol yes (path) Instrument (case-insensitive)

Reply

Status Shape Meaning
200 OK kill-switch ack, quotes_only: true The quote legs cancelled; cancelled_quotes is 0 when there was no quote

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
KILL_SWITCH_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/market/halt

Purpose: halt every symbol (halt source ADMIN) and cancel every market-maker quote. Resting orders stay on the book; POST /admin/market/kill-switch cancels those.

Arguments

Name Type Req Description
none — — No arguments

Reply

Status Shape Meaning
200 OK { "accepted": true, "halted_symbols": 3, "halted_symbol_ids": [...], "cancelled_quotes": 4, ... } What the engine halted

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
HALT_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/market/resume

Purpose: lift the halt of POST /admin/market/halt on every symbol.

Arguments

Name Type Req Description
none — — No arguments

Reply

Status Shape Meaning
200 OK { "accepted": true, "resumed_symbols": 3, "resumed_symbol_ids": [...], ... } What the engine resumed

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
RESUME_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

GET /api/v1/admin/market/volume

Purpose: today's traded quantity, value and trade count per symbol, and the venue totals.

Arguments

Name Type Req Description
none — — No query parameters

Reply

Status Shape Meaning
200 OK { "symbols": [{ "symbol", "qty", "value", "trades" }], "count": 2, "total_qty", "total_value", "total_trades" } Today's volume; value is display money

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/symbols/{symbol}/reopen

Purpose: force one symbol to uncross now and clear any halt in one step. Without price it opens at the natural equilibrium; with one, at that operator price, for when a failed auction left no equilibrium. dry_run reports the indicative print without changing anything.

Arguments

Name Type Req Description
symbol Symbol yes (path) Instrument (case-insensitive)
price Price no Body: operator opening price, on the tick grid (refused, not rounded, otherwise)
dry_run Bool no Body: report the indicative print only; default false
reason Str no Body: note for the audit log

Reply

Status Shape Meaning
200 OK { "accepted": true, "symbol", "dry_run", "indicative_price", "indicative_qty", "surplus", "imbalance_side"?, "printed_price", "traded_qty" } The uncross result; printed_price is null on a dry run

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
REOPEN_REJECTED 409 Engine refused (for example the symbol is not halted); message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time
SYMBOLS_NOT_READY 503 price given before the symbol's tick precision reached the gateway

POST /api/v1/admin/market/kill-switch

Purpose: cancel all resting exposure across every gateway and symbol.

Arguments

Name Type Req Description
reason Str no Optional note

Reply

Status Shape Meaning
200 OK kill-switch ack What the engine cancelled
409 Conflict error envelope The engine refused the command

Errors

Code Status When
VALIDATION 400 A body field, query parameter, header or history filter failed validation; field names it. reject_code is one of MISSING_FIELD, UNSUPPORTED_FIELD, INVALID_VALUE, TICK_VIOLATION
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
KILL_SWITCH_REJECTED 409 Engine refused; message is its reason
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

POST /api/v1/admin/indexes/{index_id}/rebalance

Purpose: rebalance one configured index through pm-index.

Arguments

Name Type Req Description
index_id IndexId yes (path) Index to rebalance
updates List<RebalanceUpdate> yes Shares-outstanding updates

Reply

Status Shape Meaning
200 OK rebalance ack Update applied
409 Conflict error envelope Rebalance rejected

Errors

Code Status When
VALIDATION 400 Batch body invalid
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
REBALANCE_REJECTED 409 pm-index rejected the request
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
INDEX_TIMEOUT 503 pm-index did not reply in time

POST /api/v1/admin/reference/reload

Purpose: request a reference-data reload. The current engine rejects this request because the active configuration cannot be both reloaded and reconstructed from the audit trail. Deploy a compiled config artifact and restart the engine to apply configuration changes.

Arguments

Name Type Req Description
none — — No request body

Reply (if an engine accepts the request)

Status Shape Meaning
200 OK { "status": "RELOADED", "config_version": "..." } Reload accepted by an engine that supports it
409 Conflict error envelope RELOAD_REJECTED: the current engine rejects reloads that cannot be recorded

Errors

Code Status When
READ_ONLY 403 A read-only key used on a gateway-scoped endpoint
ROLE_DENIED 403 Caller is not ADMIN
RELOAD_REJECTED 409 Reload would change the symbol or index set
RATE_LIMIT 429 Per-key write limit exceeded; retry after a short pause
ENGINE_TIMEOUT 503 Engine did not reply in time

Operational notes

  • Read-only dashboard keys can call reference and public history endpoints, but not trading or admin write endpoints.
  • order_retention_sec bounds the live order cache, the private orders.snapshot frame, and GET /api/v1/admin/orders.
  • market_data_cache_sec bounds the market-data stream cache that backs the snapshot-on-subscribe, snapshot, and resume controls on WS /api/v1/ws/market-data (latest book/depth/auction snapshots are kept regardless of age; only the trades tail is bounded).
  • WebSocket streams are specified in the WebSocket API Reference; this appendix is REST only.