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.

See WebSocket Protocol for the full auth upgrade flow, heartbeat behavior, and close-code reference. This page focuses on user-specific events.
Authenticated users automatically receive order and balance notifications over the WebSocket. No separate subscription action is required once the connection has user-level auth.

Connect and authenticate

let accessToken = initialAccessToken;

const ws = new WebSocket(`wss://ws.agg.market/ws?appId=${appId}&token=${accessToken}`);
You will receive a connection acknowledgement:
{ "type": "connected", "appId": "your-app-id", "userId": "user-123" }
If the user signs in after the socket is already open, upgrade the connection in-place:
ws.send(JSON.stringify({ action: "authenticate", token: accessToken }));
// Response: { "type": "authenticated", "userId": "user-123" }
No initial state is sent on authentication. Use GET /execution/balances, GET /execution/positions, and GET /execution/orders for the current user state, then use the WebSocket to keep the UI fresh.

Events

Order submitted

Sent after a trade is accepted for execution:
{
  "type": "order_submitted",
  "venue": "kalshi",
  "orderId": "order-abc-123",
  "side": "buy",
  "price": 0.55,
  "size": 10,
  "outcomeId": "vmo_123",
  "timestamp": 1710705123456
}

Balance update

Sent when a trade changes the user’s venue balance:
{
  "type": "balance_update",
  "venue": "kalshi",
  "tradingBalanceCents": 50000,
  "walletBalanceCents": 100000,
  "timestamp": 1710705123456
}

Order event

Venue-agnostic stream of DAG lifecycle and terminal order status. Discriminate on the inner event field. Common shapes:
// Step lifecycle (drives progress UI)
{
  "type": "order_event",
  "event": "step_started",
  "userId": "user-123",
  "orderId": "order-abc-123",
  "dagRunId": "dag_xyz",
  "stepId": "execute-...",
  "stepType": "submit-order",
  "sequence": 3,                  // 1-based step index within the DAG
  "totalSteps": 7,                // total step count
  "venue": "polymarket",
  "timestamp": 1710705123456
}

// Terminal fill (triggers success toast + position refresh)
{
  "type": "order_event",
  "event": "filled",            // or "partial_fill"
  "userId": "user-123",
  "orderId": "order-abc-123",
  "venue": "polymarket",
  "filledAmountRaw": "1000000",
  "remainingAmountRaw": "0",
  "timestamp": 1710705123456
}

// Failure (triggers error toast)
{
  "type": "order_event",
  "event": "failed",
  "userId": "user-123",
  "orderId": "order-abc-123",
  "venue": "polymarket",
  "errorReason": "Polymarket CLOB error (400): ...",
  "timestamp": 1710705123456
}
event values: dag_started, dag_completed, dag_failed, dag_cancelled, step_started, step_completed, step_waiting, step_failed, step_retrying, filled, partial_fill, failed. The gateway broadcasts every event verbatim — switch on event and ignore types your UI doesn’t care about.

Handle messages

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

  switch (msg.type) {
    case "order_submitted":
      // Legacy direct-trade event
      addOrder({
        id: msg.orderId,
        venue: msg.venue,
        side: msg.side,
        price: msg.price,
        size: msg.size,
      });
      break;

    case "balance_update":
      updateBalance(msg.venue, {
        trading: msg.tradingBalanceCents / 100,
        wallet:
          msg.walletBalanceCents != null ? msg.walletBalanceCents / 100 : null,
      });
      break;

    case "order_event":
      // Venue-agnostic DAG + order lifecycle
      switch (msg.event) {
        case "step_started":
        case "step_completed":
        case "step_waiting":
          // msg.sequence + msg.totalSteps available for "Step 3 of 7" UI
          updateOrderProgress(
            msg.orderId,
            msg.event,
            msg.stepType,
            msg.sequence,
            msg.totalSteps,
          );
          break;
        case "filled":
        case "partial_fill":
          markOrderTerminal(msg.orderId, msg.event, msg.filledAmountRaw);
          refreshPosition(msg.venue);
          break;
        case "failed":
        case "dag_failed":
        case "step_failed":
          markOrderFailed(msg.orderId, msg.errorReason ?? msg.error);
          break;
      }
      break;

    case "error":
      console.error(msg.message);
      break;
  }
};

Refresh-aware reconnect

If the socket closes and your access token may have expired, refresh it before reconnecting:
function connect() {
  const ws = new WebSocket(`wss://ws.agg.market/ws?appId=${appId}&token=${accessToken}`);

  ws.onclose = async () => {
    try {
      accessToken = await client.refreshAccessToken();
    } catch {
      await client.signOut();
      return;
    }

    setTimeout(connect, 1000);
  };

  ws.onmessage = handleMessage;
}

WebSocket Protocol

Full auth upgrade, heartbeat, error, and reconnect reference.

Real-Time Orderbook

Outcome-level orderbook subscriptions on the same socket connection.

Real-Time Charts

Build live charts from orderbook and trade events.

Token Refresh

Session renewal patterns for REST and WebSocket clients.