Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.agg.market/llms.txt

Use this file to discover all available pages before exploring further.

WebSocket Protocol

Connect to the AGG WebSocket API to receive live aggregated orderbook updates, public trades, JSON heartbeat events, and authenticated user events across connected venues.
wss://ws.agg.market/ws?appId=YOUR_APP_ID

Getting connected

1

Open a connection

Pass your appId as a query parameter.
const ws = new WebSocket(`wss://ws.agg.market/ws?appId=${appId}`);
If the user is already signed in, you can also include &token=eyJ... for immediate user-level authentication.
2

Wait for the connected message

The server acknowledges the connection with app and optional user context:
{
  "type": "connected",
  "appId": "app_demo123",
  "userId": "usr_xyz789"
}
userId is present only when a valid JWT is supplied on connect.
3

Subscribe to outcome streams

Send one or more outcome IDs with a channel to start receiving orderbook or trade updates. Each outcome has its own orderbook — there is no complement derivation.
{
  "action": "subscribe",
  "channel": "orderbook",
  "outcomeIds": ["vmo_1", "vmo_2"]
}
outcomeId is a VenueMarketOutcome.id (venue-scoped, not cross-venue).
A single connection supports up to 100 total subscriptions across orderbook and trade channels. Requests beyond that limit return an error message.

1. Orderbook stream

Subscribe to an outcome’s aggregated orderbook to receive a full snapshot followed by incremental deltas. Each outcome has its own orderbook — there is no complement derivation. Aggregated levels include per-venue attribution. Per-venue books are also included.

Subscribe

{
  "action": "subscribe",
  "channel": "orderbook",
  "outcomeIds": ["vmo_1"]
}
You receive a confirmation before the initial snapshot:
{
  "type": "subscribed",
  "outcomeIds": ["vmo_1"],
  "channel": "orderbook"
}

Initial snapshot

{
  "type": "orderbook_snapshot",
  "outcomeId": "vmo_1",
  "seq": 1710000001,
  "checksum": 2918476531,
  "bids": [
    [0.55, 1500, { "kalshi": 800, "polymarket": 700 }],
    [0.54, 900, { "kalshi": 400, "polymarket": 500 }]
  ],
  "asks": [
    [0.56, 1200, { "kalshi": 600, "polymarket": 600 }],
    [0.57, 800, { "polymarket": 800 }]
  ],
  "venueOrderbooks": {
    "kalshi": {
      "bids": [[0.55, 800], [0.54, 400]],
      "asks": [[0.56, 600]]
    },
    "polymarket": {
      "bids": [[0.55, 700], [0.54, 500]],
      "asks": [[0.56, 600], [0.57, 800]]
    }
  },
  "venues": {
    "kalshi": { "bestBid": 0.55, "bestAsk": 0.56 },
    "polymarket": { "bestBid": 0.55, "bestAsk": 0.56 }
  },
  "midpoint": 0.555,
  "spread": 0.01,
  "timestamp": 1710000000000
}
Aggregated levels use the wire format [price, totalSize, { venue: sizeAtPrice }]. Per-venue levels use [price, size]. Bids are sorted descending by price, asks ascending.

Incremental deltas

{
  "type": "orderbook_delta",
  "outcomeId": "vmo_1",
  "seq": 1710000002,
  "checksum": 3847261950,
  "bidChanges": [
    [0.55, 1600, { "kalshi": 900, "polymarket": 700 }]
  ],
  "askChanges": [],
  "venueDeltaBooks": {
    "kalshi": {
      "bidChanges": [[0.55, 900]],
      "askChanges": []
    }
  },
  "venues": {
    "kalshi": { "bestBid": 0.55, "bestAsk": 0.56 },
    "polymarket": { "bestBid": 0.55, "bestAsk": 0.56 }
  },
  "midpoint": 0.555,
  "spread": 0.01,
  "timestamp": 1710000001000
}

Sequencing and resync

Every orderbook message includes a monotonic seq value for that outcome. Your client is responsible for maintaining local book state and detecting integrity issues. The gateway is a stateless fan-out — it forwards deltas from the aggregation engine without server-side seq tracking. Apply deltas when delta.seq === book.seq + 1. After applying, recompute the checksum (XOR of per-level CRC32 values) and compare it to checksum in the message. If they match, the book is consistent. Drop stale deltas where delta.seq <= book.seq — these can arrive after a subscribe when Kafka deltas predate the initial snapshot. Silently discard them. Request a resnapshot if you detect a forward gap (delta.seq > book.seq + 1) or a checksum mismatch:
{
  "action": "resnapshot",
  "outcomeIds": ["vmo_1"]
}
The @agg-build/sdk handles all of this automatically — seq tracking, stale delta filtering, checksum validation, and resnapshot requests. Use AggWebSocket or the useLiveMarket hook and these details are managed for you.

Raw WebSocket example

This keeps the protocol logic visible without pulling in SDK helpers:
const ws = new WebSocket(`wss://ws.agg.market/ws?appId=${appId}`);

ws.onopen = () => {
  ws.send(
    JSON.stringify({
      action: "subscribe",
      channel: "orderbook",
      outcomeIds: ["vmo_1"],
    }),
  );
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case "subscribed":
      console.log("Subscribed:", msg.outcomeIds, msg.channel);
      break;
    case "orderbook_snapshot":
      replaceLocalBook(msg);
      break;
    case "orderbook_delta":
      applyDeltaToLocalBook(msg);
      break;
    case "heartbeat":
      lastHeartbeatAt = msg.ts;
      break;
    case "error":
      console.error(msg.message);
      break;
  }
};
For SDK, hooks, and UI implementations, see Real-Time Orderbook.

2. Trade stream

Subscribe to the public trade feed for an outcome. No user auth is required.

Subscribe

{
  "action": "subscribe",
  "channel": "trades",
  "outcomeIds": ["vmo_1"]
}
Confirmation:
{
  "type": "subscribed",
  "outcomeIds": ["vmo_1"],
  "channel": "trades"
}

Trade event

{
  "type": "trade",
  "outcomeId": "vmo_1",
  "venue": "kalshi",
  "side": "buy",
  "price": 0.55,
  "size": 100,
  "timestamp": 1710000000000
}

Unsubscribe

{
  "action": "unsubscribe",
  "channel": "trades",
  "outcomeIds": ["vmo_1"]
}
For chart-building patterns on top of orderbook and trade data, see Real-Time Charts.

3. Authenticated user events

Receive user-specific events, such as order confirmations and balance updates, after the connection has user-level auth.

Authenticate

Include the JWT in the connection URL:
wss://ws.agg.market/ws?appId=YOUR_APP_ID&token=eyJhbG...
Mid-session auth supports re-authentication after token refresh or user switching without opening a second socket.

Order submitted

{
  "type": "order_submitted",
  "venue": "kalshi",
  "orderId": "ord_abc123",
  "side": "buy",
  "price": 0.55,
  "size": 100,
  "outcomeId": "vmo_1",
  "timestamp": 1710000000000
}

Balance update

{
  "type": "balance_update",
  "venue": "kalshi",
  "tradingBalanceCents": 94500,
  "walletBalanceCents": 100000,
  "timestamp": 1710000000000
}

Order event

Venue-agnostic stream of DAG lifecycle and terminal order status events. Emitted for every venue (Polymarket, Kalshi, dFlow, …) automatically — no per-venue handling on the client. Discriminate on the event field.

Step lifecycle (progress UI)

{
  "type": "order_event",
  "event": "step_started",
  "userId": "usr_xyz789",
  "orderId": "ord_abc123",
  "dagRunId": "dag_mnp...",
  "stepId": "execute-...",
  "stepType": "submit-order",
  "sequence": 3,
  "totalSteps": 7,
  "venue": "polymarket",
  "timestamp": 1710000000000
}
event values: step_started | step_completed | step_waiting | step_failed | step_retrying. sequence is the 1-based declaration index of the step within the DAG, and totalSteps is the total step count. Pair them to render progress like “Step 3 of 7”. dag_started also carries totalSteps so the UI can show an empty progress bar before the first step runs. For branching DAGs, sequence reflects declaration order, not strict execution order — parallel branches may emit non-monotonic sequence values.

Terminal fill (success toast + position refresh)

{
  "type": "order_event",
  "event": "filled",
  "userId": "usr_xyz789",
  "orderId": "ord_abc123",
  "venue": "polymarket",
  "filledAmountRaw": "1000000",
  "remainingAmountRaw": "0",
  "timestamp": 1710000000000
}
event values: filled | partial_fill. remainingAmountRaw is present only on partial_fill.

Failure (error toast)

{
  "type": "order_event",
  "event": "failed",
  "userId": "usr_xyz789",
  "orderId": "ord_abc123",
  "venue": "polymarket",
  "errorReason": "Polymarket CLOB error (400): ...",
  "timestamp": 1710000000000
}
event values: failed (order-level) | dag_failed (DAG-level).

DAG lifecycle (optional)

{
  "type": "order_event",
  "event": "dag_started",
  "userId": "usr_xyz789",
  "orderId": "ord_abc123",
  "dagRunId": "dag_mnp...",
  "templateName": "route-execution",
  "totalSteps": 7,
  "timestamp": 1710000000000
}
event values: dag_started | dag_completed | dag_failed | dag_cancelled.
All order_event messages are broadcast verbatim — the gateway does not filter by event type. Switch on event and ignore types your UI doesn’t care about.
For a notification-focused recipe, see User Notifications.

Connection management

Heartbeat

The service emits a JSON heartbeat event that clients can observe directly:
{
  "type": "heartbeat",
  "ts": 1710000000000
}
Use the JSON heartbeat event if you want an app-level liveness timer in a raw client.

Reconnection

If the connection closes, reconnect with exponential backoff and re-subscribe to your channels:
function connect(attempt = 0) {
  const ws = new WebSocket(`wss://ws.agg.market/ws?appId=${appId}`);

  ws.onclose = () => {
    const delay = Math.min(1000 * 2 ** attempt + Math.random() * 1000, 30000);
    setTimeout(() => connect(attempt + 1), delay);
  };

  ws.onopen = () => {
    attempt = 0;
    resubscribeAll(ws);
  };
}

Error handling

Protocol or validation errors arrive as JSON messages:
{
  "type": "error",
  "message": "Invalid outcomeIds: must be a non-empty array"
}
Fatal connection failures use close codes:
CodeMeaning
4001Missing appId query parameter
4003Invalid app or token scope not accepted
1001Connection closed; reconnect with backoff

Setup Guide

Provider wiring, auth setup, and SDK client initialization.

Real-Time Orderbook

SDK, hooks, and UI orderbook implementations.

Real-Time Charts

Build live candles from REST history plus WebSocket events.

User Notifications

Authenticated order and balance events with refresh-aware reconnect logic.