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/wsentry point shipsPositionSocketandMarketSocketclients 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) andreact/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.
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:
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
authpayload (recommended):auth: { apiKey: "dm_live_skey_..." }(ordm_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_...(ordm_sbx_skey_...in sandbox). For Socket.IO clients, set this via theextraHeadersoption.
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
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
authpayload (recommended):auth: { token: "<jwt>" }.HTTP header:
Authorization: Bearer <jwt>. For Socket.IO clients, set this via theextraHeadersoption.
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:
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
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 vs position.openedposition.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:
data.code
string
Machine-readable notification code
data.message
string
Human-readable description
data.params
object
Additional context (varies by notification code)
Notification codes:
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.
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.
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
data is always an arrayImportant: Unlike position events — where
datais a single object — market events always deliverdataas 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 iteratedataas 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
market.discovereddata 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
market.eligibility_changeddata 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
market.max_leverage_changeddata 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

