For the complete documentation index, see llms.txt. This page is also available as Markdown.

WebSocket Events

Receive real-time position updates via WebSocket. All position state changes are delivered as events over a persistent Socket.IO connection.

SDK support: The @dimes-dot-fi/sdk/ws entry point ships PositionSocket and MarketSocket clients for the user gateways (JWT auth). They wrap Socket.IO, manage reconnection, and camelize every payload to match the rest of the SDK. Prefer them over wiring up Socket.IO by hand — see Using the SDK below. For the partner gateways (API key auth) the SDK has no client yet; use Socket.IO directly as shown in the raw examples. Runnable: 05-websocket.ts (Node) and react/streams.tsx (React reconciliation).

Two WebSocket gateways are available. Both deliver the same event types and payload shapes — they differ only in authentication and scope.

Gateway
Namespace
Auth
Scope

Partner

/v1/ws/prediction-markets/positions

API key

All positions for the partner

User

/v1/ws/prediction-markets/user-positions

User JWT

Only positions for the authenticated user


Using the SDK

For the user gateways, the SDK's @dimes-dot-fi/sdk/ws entry point gives you typed clients so you don't touch Socket.IO directly. Both clients take a getToken callback (so they always reconnect with a fresh JWT) and an optional baseUrl (defaults to https://api.dimes.fi — set it to https://api-sandbox.dimes.fi for sandbox). Payloads arrive camelCased (event.createdAt, event.data) to match the REST types returned elsewhere by the SDK.

Position events

PositionSocket connects to the user-positions gateway. Subscribe to a specific event type, to "*" for all of them, or to onNotification for the user-gateway notification stream. Every on* call returns an unsubscribe function.

import { PositionSocket } from "@dimes-dot-fi/sdk/ws";

const socket = new PositionSocket({
  getToken: () => currentJwt, // called on every (re)connect
  // baseUrl: "https://api-sandbox.dimes.fi", // for sandbox
});

socket.onConnect(() => console.log("connected"));

const off = socket.on("position.opened", (event) => {
  // event.data is a full Position (camelCased), same shape as the REST API
  addOpenPosition(event.data);
});

socket.on("position.opening", (event) => showFinalizing(event.data.id));

// Transient, informational messages (user gateway only)
socket.onNotification((event) => showToast(event.data.message));

socket.onError((err) => console.error("ws error", err));

socket.connect();

// later: off(); socket.disconnect();

When the JWT rotates, call socket.reconnectWithToken() to drop and re-establish the connection with the new token.

Market events

MarketSocket connects to the user-markets gateway with the same API. Remember that market event data is always an array (see Market events below):

The raw Socket.IO examples below remain accurate for the partner gateways and non-JS clients; the SDK clients simply wrap that same protocol for the user gateways.


Partner gateway

Namespace:

Environment
WebSocket URL

Production

wss://api.dimes.fi/v1/ws/prediction-markets/positions

Sandbox

wss://api-sandbox.dimes.fi/v1/ws/prediction-markets/positions

Sandbox emits the same event types and payload shapes as production — only the host and the key prefix differ. See Environments.

Transport: Socket.IO

Auth: Partner API key, passed as either:

  • Socket.IO auth payload (recommended): auth: { apiKey: "dm_live_skey_..." } (or dm_sbx_skey_... in sandbox). Transmitted in the handshake body — never appears in URLs, server logs, or browser history.

  • HTTP header: Authorization: Api-Key dm_live_skey_... (or dm_sbx_skey_... in sandbox). For Socket.IO clients, set this via the extraHeaders option.

On successful auth, the connection is scoped to the partner. All events for positions created through that partner's API keys are delivered.


User gateway

Environment
WebSocket URL

Production

wss://api.dimes.fi/v1/ws/prediction-markets/user-positions

Sandbox

wss://api-sandbox.dimes.fi/v1/ws/prediction-markets/user-positions

Transport: Socket.IO

Auth: User JWT (the same token from POST /tokens), passed as either:

  • Socket.IO auth payload (recommended): auth: { token: "<jwt>" }.

  • HTTP header: Authorization: Bearer <jwt>. For Socket.IO clients, set this via the extraHeaders option.

On successful auth, the connection is scoped to the authenticated user. Only events for positions belonging to that specific wallet address and partner are delivered — other users' positions are never visible.


Authentication errors

If authentication fails on either gateway, the server emits an error event and disconnects:


Event envelope

All events follow the resource.action naming pattern with a consistent envelope:

Field
Type
Description

id

string

Unique event identifier (prefixed evt_)

type

string

Event type in resource.action format

created_at

string (ISO 8601)

When the event was emitted

data

object

Event payload — always contains the full position object matching the REST API shape

The data object contains the same representation you would get from GET /positions/{id}. This means event consumers can reuse the same types/parsers as their REST integration.


Event types

Event Type
Trigger

position.created

On-chain position account created (user submitted tx)

position.opening

Finalize-open tx confirmed on-chain (early signal, before indexer picks up)

position.opened

Position finalized on-chain, tokens acquired (indexer confirmed)

position.close_requested

User submitted close request

position.closed

Close finalized, proceeds distributed

position.settled

Market resolved, position settled

position.liquidated

Position liquidated due to insufficient margin

position.force_unwound

Position partially unwound to reduce leverage (position remains open)

position.partial_close_requested

Owner requested a partial close; slice queued (position remains open)

position.partial_close_initiated

Partial-close slice withdrawn, sell order in flight (position remains open)

position.partial_closed

Partial close finalized; position remains open at reduced size

position.partial_close_aborted

Partial-close slice never filled; position restored to original size

position.reverted

Position open failed, collateral refunded

position.cancelled

Position cancelled before opening (EVM only)


Event payloads

The data field in every event is a full position object — the same shape returned by the REST API. See the Positions section of the API Reference for complete field definitions.

Event

status

Position shape

position.created

pending

Open shape (entry, current, risk, fees, timing)

position.opening

pending

Open shape (early signal — position not yet indexed)

position.opened

open

Open shape

position.close_requested

closing

Open shape

position.closed

closed

Closed shape — close_reason: "closed", entry, result, fees

position.settled

settled

Closed shape — close_reason: "settled", entry, result, fees

position.liquidated

liquidated

Closed shape — close_reason: "liquidated", entry, result, fees

position.force_unwound

open

Open shape (reduced size — current values reflect new position)

position.partial_close_requested

open

Open shape (slice queued — size unchanged until initiated)

position.partial_close_initiated

open

Open shape (current reflects the survivor while the slice sells)

position.partial_closed

open

Open shape (reduced size — current values reflect new position)

position.partial_close_aborted

open

Open shape (slice never filled — size restored to original)

position.reverted

cancelled

Closed shape — close_reason: "reverted", revert_reason, entry, result, fees

position.cancelled

cancelled

Closed shape — close_reason: "reverted", entry, result, fees

Open positions include current, risk, fees, and timing sections. Closed, settled, and liquidated positions replace those with close_reason and result (including collected_liquidation_fee_* for liquidations).

The position.reverted payload additionally carries revert_reason, classifying why the open failed before it landed: exchange_unavailable (the prediction-market venue was temporarily unavailable — safe to retry), slippage_exceeded (price moved beyond your slippage tolerance before the order filled), or unknown. It is non-null only when close_reason is reverted; collateral is always returned regardless of reason. Surface exchange_unavailable as a transient, retryable issue and slippage_exceeded as a price-moved message.

The four position.partial_close_* events track an owner-requested partial close — the position stays open throughout and ends at a reduced size. Treat position.partial_closed like position.force_unwound: re-render the still-open position from the smaller current.* values.

position.opening vs position.opened

position.opening fires as soon as the finalize-open transaction is confirmed on-chain — before the event indexer picks it up. This gives the UI an early signal (~5-15 seconds faster) that the position is about to open.

position.opened fires later when the indexer delivers the confirmed PositionOpened event.

Recommended UI behavior: On position.opening, show a "finalizing" state. On position.opened, transition to the full open position view with live data. If position.reverted arrives instead, the open failed — show the error state.


Listening to events

Subscribe to specific event types using Socket.IO's standard event listeners:


Notification events (user gateway only)

In addition to position state-transition events, the user gateway emits notification events. These are informational messages that provide feedback during asynchronous operations — they are not errors and do not indicate a state change.

Envelope:

Field
Type
Description

data.code

string

Machine-readable notification code

data.message

string

Human-readable description

data.params

object

Additional context (varies by notification code)

Notification codes:

Code
When
Params

ORDER_FULFILLMENT_RETRYING

Order could not be filled at current slippage; retrying

position_id, slippage_bps

PARTIAL_OPEN_PROGRESS

Floor or retry leg of a FAK partial-open fulfilled; reports cumulative progress

position_id, cumulative_filled_making_amount, target_making_amount, attempt_number

PARTIAL_OPEN_FLOOR_MISSED

Floor FOK of a FAK partial-open failed to fill after retries; position reverted

position_id

Listening:

Notifications are transient — display them as a toast or inline status message. They do not require user action.


Market events

In addition to position events, two WebSocket gateways stream market updates: new markets coming online, eligibility changes, and max-leverage changes. They mirror the position gateways exactly — same Socket.IO transport, same auth, same envelope — and differ only in scope.

Gateway
Namespace
Auth
Scope

Partner

/v1/ws/prediction-markets/markets

API key

All markets visible to the partner

User

/v1/ws/prediction-markets/user-markets

User JWT

Markets visible to the authenticated user

Auth is identical to the position gateways: the partner gateway takes an API key (auth: { apiKey: "dm_live_skey_..." } or Authorization: Api-Key ...), and the user gateway takes a user JWT (auth: { token: "<jwt>" } or Authorization: Bearer <jwt>). See Partner gateway and User gateway above for the full handshake details — they apply unchanged.

Environment
Partner URL
User URL

Production

wss://api.dimes.fi/v1/ws/prediction-markets/markets

wss://api.dimes.fi/v1/ws/prediction-markets/user-markets

Sandbox

wss://api-sandbox.dimes.fi/v1/ws/prediction-markets/markets

wss://api-sandbox.dimes.fi/v1/ws/prediction-markets/user-markets

data is always an array

Important: Unlike position events — where data is a single object — market events always deliver data as an array. A single market change arrives as an array of one; when many markets change at once (e.g. a batch eligibility sweep), they are batched into a single event. Always iterate data as an array.

The envelope is otherwise identical to position events:

Market event types

Event Type

Trigger

data items

market.discovered

A new market comes online

Full market objects (same shape as GET /v1/markets)

market.eligibility_changed

A market's tradeability / capacity / notional limits change

Delta objects — only the changed fields

market.max_leverage_changed

An eligible market's max leverage changes

Delta objects — id, polymarket, leverage (no min_bps/step_bps)

market.discovered carries the complete market payload — identical to an item from GET /v1/markets, including the optional prices block when available. The two *_changed events carry deltas: each item contains the market id, the always-present polymarket identifiers (so you can map the delta onto your cached market), and only the fields that actually changed. Merge those fields into your cached copy of the market by id.

market.discovered

data is an array of full market objects, each identical to an item from GET /v1/markets. See the Markets section of the API Reference for complete field definitions.

market.eligibility_changed

data is an array of deltas. Each delta is { id, polymarket, ...only the changed fields }. Changed fields may include any of: accepting_new_positions, rejection_reason_code, sided_eligibility, max_notional_yes_usd(_pips), max_notional_no_usd(_pips), capacity_max_notional_yes_usd(_pips), capacity_max_notional_no_usd(_pips), slippage_max_notional_yes_usd(_pips), slippage_max_notional_no_usd(_pips). It never includes leverage.

market.max_leverage_changed

data is an array of deltas. Each delta is { id, polymarket, leverage }, where leverage carries the same fields as the leverage block in a full market object except the constants min_bps and step_bps — those never change, so read them from the market discovery payload. Only emitted for markets that are currently allowed/eligible.

Connecting to market events

The connection flow is identical to the position gateways — only the namespace changes. Remember to iterate data:

For the partner gateway, swap the namespace to /v1/ws/prediction-markets/markets and authenticate with your API key (auth: {apiKey: process.env.DIMES_API_KEY}) exactly as shown for the partner position gateway.


Best practices

Deduplicate on event id. Store processed event IDs and skip duplicates. The same event may be delivered more than once.

Handle events out of order. A position.closed event could arrive before position.close_requested due to network timing. Use the created_at timestamp and the position status from the REST API as the source of truth if ordering matters.

Reconnect on disconnect. Configure your client's reconnection settings for production reliability.

Use REST API as fallback. If your WebSocket connection drops, use GET /positions to catch up on any missed state changes.

Last updated