Websocket Messages

List of messages subscriptions that are available on the Websocket

Connectivity Model

Authentication

During authentication, a client can obtain multiple short-lived auth tokens by calling the /api/3/zm/rest/auth/token endpoint. Each received token can then be used to establish a WebSocket connection:

wss://trade-uk.sandbox.zodiamarkets.com/zm/ws/ws-client?token=<token>

Connection Isolation

Each WebSocket connection for a given client is fully independent. Connections have their own subscriptions and can send and receive order requests separately.

Key Points:

  • Messages are not duplicated across a user's active connections
  • Only the WebSocket connection that initiated a given request will receive the corresponding response
  • Each connection maintains its own price subscriptions
  • Quote IDs can be shared across connections (as long as they are not expired)

WebSocket Session Behavior

Key Concepts

ConceptBehavior
Price subscriptionsBound to the connection that created them. Lost on disconnect.
QuoteIDsRemain valid after disconnect. Can be used from any authenticated connection while the quote is still active.
Order responsesDelivered only to the connection that submitted the order.

Connection Scenarios

The examples below use Connection A and Connection B to represent separate WebSocket connections from the same client.

Scenario 1: Reconnection After Disconnect

Connection A: subscribe → receives quoteID, price stream active
Connection A: disconnects
Connection A: reconnects

Result:

  • Price stream: Stopped. Client must re-subscribe.
  • QuoteID: Still usable. Can be submitted on the new connection, if the quote remains active.

Scenario 2: Order Submitted on Different Connection

Connection A: subscribe → receives quoteID
Connection B: connects, submits order using quoteID from Connection A

Result:

  • Order response: Delivered to Connection B only (the submitting connection).

Scenario 3: Submitting Connection Disconnects Before Response

Connection A: subscribe → receives quoteID
Connection B: submits order using quoteID
Connection B: disconnects before response

Result:

  • Order response: Lost. Neither connection receives it.
⚠️

Warning

The order may still be executed. Ensure your connection remains stable until you receive the order response or subscribe to transaction endpoint or webhook to check transaction has been created


Scenario 4: Subscription Connection Disconnects After Order Submission

Connection A: subscribe → receives quoteID
Connection B: submits order using quoteID
Connection A: disconnects

Result:

  • Order response: Delivered to Connection B.

Trade Subscription Types

The following types of subscription methods are supported:

Subscription TypeDescriptionRequirementsDetails
Normal Price subscriptionsExecute trade using own funds and delivered to own account/walletAccount With ZM with Authorized Traders access to RFSPrice Channel→
Beneficiary Price SubscriptionExecute trade using own funds but delivered to third party wallet addressThird Party Beneficiary registered with ZM with delivery wallet whitelisted and verified.Beneficiary Price Channel→
Sender Price SubscriptionExecute trade using third party crypto funds but delivered to own account/walletThird Party Sender registered with ZM with collection (sending) wallet whitelisted and verified.Sender Price Channel→



Best Practices

Managing Multiple Connections

# Example: Managing multiple WebSocket connections
class WebSocketManager:
    def __init__(self):
        self.connections = {}
        self.subscriptions = {}
    
    def add_connection(self, name, ws):
        """Add a new WebSocket connection"""
        self.connections[name] = ws
        self.subscriptions[name] = []
    
    def subscribe(self, connection_name, instrument):
        """Subscribe to prices on specific connection"""
        ws = self.connections[connection_name]
        ws.send(json.dumps({
            'messageType': 'subscribe',
            'instrument': instrument,
            ...
        }))
        self.subscriptions[connection_name].append(instrument)
    
    def submit_order(self, connection_name, quote_id):
        """Submit order on specific connection"""
        ws = self.connections[connection_name]
        ws.send(json.dumps({
            'messageType': 'order',
            'quoteId': quote_id,
            ...
        }))
        # Order response will come back on this connection

# Usage
manager = WebSocketManager()
manager.add_connection('pricing', ws_connection_1)
manager.add_connection('trading', ws_connection_2)

# Subscribe on pricing connection
manager.subscribe('pricing', 'BTC.USD')

# Submit order on trading connection (can use quoteID from pricing)
manager.submit_order('trading', quote_id_from_pricing_connection)

Handling Disconnections

def handle_disconnect(connection_name):
    """Handle connection disconnect"""
    # Price subscriptions are lost
    subscriptions = self.subscriptions[connection_name]
    
    # Reconnect
    new_ws = reconnect()
    self.connections[connection_name] = new_ws
    
    # Re-subscribe to all previous subscriptions
    for instrument in subscriptions:
        self.subscribe(connection_name, instrument)
    
    # Note: QuoteIDs from before disconnect are still valid
    # Can continue using them if quotes are still active

Ensuring Order Response Receipt

def submit_order_safely(ws, quote_id):
    """Submit order and wait for response"""
    order_submitted = threading.Event()
    order_response = None
    
    def on_message(msg):
        nonlocal order_response
        if msg['messageType'] == 'order':
            order_response = msg
            order_submitted.set()
    
    # Submit order
    ws.send(json.dumps({
        'messageType': 'order',
        'quoteId': quote_id,
        ...
    }))
    
    # Wait for response (with timeout)
    if order_submitted.wait(timeout=10):
        return order_response
    else:
        # Timeout - order may still execute
        # Check order status via REST API
        return None

Important Reminders

  • 🔄 Subscriptions do not persist across disconnections - always re-subscribe after reconnecting
  • 🎫 Quote IDs remain valid even after the subscribing connection disconnects
  • 📨 Order responses go to the submitting connection - not the connection that subscribed to prices
  • ⚠️ Orders may execute even if the connection drops before receiving a response