Skip to main content
The Totalis WebSocket delivers real-time notifications for trade confirmations, position settlements, and quote lifecycle events.
Live quote request streaming uses SSE, not WebSocket. Market makers receive quote requests via the Quote Service SSE stream. Users receive live pricing via the user SSE stream. The WebSocket is used for post-trade events like quote:accepted and position:settled.

Connecting

wss://api.totalis.trade/ws

Authentication

Send an auth message with your API key immediately after connecting:
{ "type": "auth", "api_key": "<your-api-key>" }
Success:
{ "type": "auth:success", "user_id": "did:privy:abc123", "user_type": "user" }
Error:
{ "type": "auth:error", "message": "Invalid API key" }

Subscribing to Channels

After authenticating, subscribe to channels to receive events:
{ "type": "subscribe", "channel": "mm:quotes:<your-mm-id>" }
ChannelDescription
mm:quotes:{mm_id}Your quote lifecycle updates (accepted, accepted details, confirmed, expired, withdrawn)
rfq:{rfq_id}Position and status updates for a specific trade

Events

Quote Events

EventDescriptionData
quote:acceptedA user committed your quote. You must confirm it before the deadline.{ quote_id, rfq_id, confirmation_deadline }
mm_quote:acceptedMM-only accepted quote details for the selected market maker. Use this to reserve local exposure or record analytics immediately. Its quote_id is valid for confirmation; do not depend on ordering versus quote:accepted.{ rfq_id, quote_id, user_id, quote, exposure_legs, confirmation_deadline, accepted_at }
quote:confirmedYou confirmed the quote. Vault settlement begins.{ quote_id, rfq_id, execution_id }
quote:executedOn-chain vault position created.{ quote_id, rfq_id }
quote:expiredQuote expired before confirmation.{ quote_id, rfq_id }
quote:rejectedAnother quote was accepted instead.{ quote_id, rfq_id, reason }
quote:withdrawnYou withdrew the quote.{ quote_id, rfq_id }
mm_quote:accepted payload:
{
  "rfq_id": "550e8400-e29b-41d4-a716-446655440000",
  "quote_id": "7d3a2f1e-4b8c-4d6e-9f0a-1b2c3d4e5f60",
  "market_maker_id": "did:privy:mm",
  "user_id": "did:privy:user",
  "source": "quote_request_commit",
  "quote_request_id": "1a6d1f06-9d4f-47cb-994b-3bdfbbef7e40",
  "draft_quote_id": "b2c3d4e5-6789-0abc-def1-234567890abc",
  "request_version": 3,
  "request_hash": "sha256:...",
  "confirmation_deadline": "2026-06-05T12:00:05.000Z",
  "accepted_at": "2026-06-05T12:00:00.000Z",
  "quote": {
    "payout_odds": 7.08,
    "user_cost": 50,
    "total_payout": 354,
    "mm_cost": 304,
    "valid_until": "2026-06-05T12:00:10.000Z"
  },
  "exposure_legs": [
    {
      "venue": "kalshi",
      "event_ticker": "KXCAR",
      "market_ticker": "KXCAR-YES",
      "side": "yes"
    }
  ]
}

Position Events

EventDescriptionData
position:createdVault position created, collateral locked.{ rfq_id, position_pda, timestamp }
position:settledPosition settled with outcome.{ rfq_id, position_pda, outcome, tx_signature, timestamp }
position:cancelledPosition cancelled, collateral unlocked.{ rfq_id, position_pda, reason, timestamp }
position:errorPosition execution error.{ rfq_id, error, timestamp }

Keep-Alive

Send periodic pings to keep the connection open:
{ "type": "ping" }
Response:
{ "type": "pong", "timestamp": "2026-06-01T12:00:00.000Z" }

Example

const ws = new WebSocket('wss://api.totalis.trade/ws');

ws.onopen = () => {
  ws.send(JSON.stringify({ type: 'auth', api_key: '<your-api-key>' }));
};

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

  if (msg.type === 'auth:success') {
    ws.send(JSON.stringify({
      type: 'subscribe',
      channel: `mm:quotes:${msg.user_id}`
    }));
  }

  if (msg.type === 'quote:accepted') {
    console.log('Quote accepted! Confirm before:', msg.data.confirmation_deadline);
    // POST https://api.totalis.trade/mm/quotes/{quoteId}/confirm
  }

  if (msg.type === 'mm_quote:accepted') {
    console.log('Accepted quote details:', msg.data.exposure_legs);
  }

  if (msg.type === 'position:settled') {
    console.log('Position settled:', msg.data.outcome);
  }
};

setInterval(() => {
  ws.send(JSON.stringify({ type: 'ping' }));
}, 30_000);