> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://partner.ninjatrader.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://partner.ninjatrader.com/_mcp/server.

# Stage 2: Websocket Management

Websocket connections are essential for real-time data streaming and order management in the Tradovate API. This stage validates your integration's ability to maintain stable websocket connections and handle real-time events.

## Overview

The websocket management stage ensures your application can:

* Establish and maintain websocket connections
* Handle authentication over websocket
* Process real-time events and maintain idempotency
* Manage connection heartbeats and reconnection logic
* Handle rate limiting and synchronous requests

## Required Tests

### 1. Websocket Login

**Purpose:** Authenticate websocket connection using access token.

**Test Steps:**

1. Establish websocket connection to `wss://demo.tradovateapi.com/v1/websocket`
2. Send authentication message with valid access token
3. Verify successful authentication response
4. Test with invalid/expired token to ensure proper error handling

**Example Implementation:**

```javascript
const ws = new WebSocket('wss://demo.tradovateapi.com/v1/websocket');

ws.onopen = function() {
    // Send authentication message
    const authMessage = `authorize\n0\n\n${accessToken}`;
    ws.send(authMessage);
};

ws.onmessage = function(event) {
    const data = JSON.parse(event.data);
    if (data.i === 0) {
        // Authentication response
        console.log('Authentication successful:', data);
    }
};
```

**Expected Authentication Response:**

```json
{
    "i": 0,
    "s": 200,
}
```

**Expected Error Response:**

```json
{
    "i": 0,
    "s": 401,
    "d": "Invalid access token"
}
```

**Validation Criteria:**

* ✅ Websocket connection establishes successfully
* ✅ Authentication succeeds with valid token
* ✅ Authentication fails with invalid token
* ✅ Connection remains stable after authentication

### 2. Heartbeats

**Purpose:** Maintain connection health and detect disconnections.

**Test Steps:**

1. Send periodic heartbeat messages
2. Verify heartbeat responses are received
3. Test heartbeat timeout scenarios
4. Verify connection recovery after missed heartbeats

**Example Implementation:**

```javascript
// Track last message time
let lastMessageTime = new Date().getTime();

ws.onmessage = function(event) {
    const timestamp = new Date().getTime();
    if(timestamp - lastMessageTime > 2500) {
        ws.send('[]'); // Empty frame is a heartbeat in our protocol
        lastMessageTime = timestamp; // Update last message time
    }
    // carry on with message handling
    // ...
};
```

**Validation Criteria:**

* ✅ Heartbeats are sent at regular intervals
* ✅ Heartbeat mechanism works during high message volume

### 3. Event Handling

**Purpose:** Process real-time events from the websocket.

**Test Steps:**

1. Manage real-time subscriptions
   * Start event streaming using `user/syncrequest`
   * subscribe to relevant entity types using the `entityTypes` parameter
2. Process incoming events and maintain Idempotency

**Example `user/syncrequest`:**

```javascript
// Connect to the websocket
const ws = new WebSocket('wss://demo.tradovateapi.com/v1/websocket');

const requestBody = {
    splitResponses: true,
    // Optional, define sharding expression
    shardingExpression: {
        expressionType: 'modUserId', // | 'modAccountId'
        divisor: 1,   // each instance should have the same divisor
        remainder: 0, // modulus of the ID of user or account to process on this instance
    },
    entityTypes: [
        'account',
        'accountRiskStatus',
        'cashBalance',
        'commandReport',
        'command',
        'executionReport',
        'fill', 
        'fillPair', 
        'order',
        'orderStrategy', 
        'position', 
        'product', 
        'user'
    ],
}

ws.onopen = function() {
    ws.send(`authorize\n0\n\n${accessToken}`);
}

ws.onmessage = function(event) {
    const type = event.data.slice(0, 1);
    let data = null;
    if(type === 'a') {
        data = JSON.parse(event.data.slice(1));
    }
    if(data.i === 0) {
        // Authentication response
        console.log('Authentication successful:', data);
        // Send syncrequest
        ws.send(`user/syncrequest\n1\n\n${JSON.stringify(requestBody)}`);
    }
}

ws.onerror = function(event) {
    console.error('WebSocket error:', event);
}

```

**Validation Criteria:**

* ✅ Only one `syncrequest` is sent per socket lifecycle.
* ✅ Real-time events are received
* ✅ Idempotency is maintained
* ✅ Sharding is implemented correctly
* ✅ Entity types are subscribed to correctly
* ✅ Syncrequest is sent correctly
* ✅ Event handlers are robust and don't crash on malformed data
* ✅ Message processing performance meets requirements

### 4. Rate Limit Handling (P-Ticket)

**Purpose:** Handle rate limiting and priority ticket system.

**Test Steps:**

1. Send requests that trigger rate limiting
2. Verify P-Ticket system responses
3. Implement proper queuing and retry logic
4. Test priority handling for different request types

**Example Implementation:**

```javascript
const send = requestData => {
    return new Promise((res, rej) => {
        let i = ++lastId;
        let race = setTimeout(() => { reject(new Error('Request timeout'))}, 30000);
        function handler(event) {
            const type = event.data.slice(0, 1);
            if(type === 'a') {
                const data = JSON.parse(event.data.slice(1));
                // data is always an array
                for(const datum of data) {
                    if(datum.i === i) {
                        if(datum.d && 'p-ticket' in datum.d) {
                            const pTicket = datum.d['p-ticket'];
                            const pTime = datum.d['p-time'];
                            const pCaptcha = datum.d['p-captcha'];
                            const pMessage = datum.d['p-message'];
                            console.log(`Rate limit reason: ${pMessage}`);
                            if(pCaptcha) {
                                // Handle captcha, if you receive it you have been locked out for 1 hour
                                console.error('p-captcha received. Operations locked for 1 hour.');
                                return;
                            } else {
                                // Handle rate limiting with P-Ticket, resend the request
                                // with the p-ticket after p-time seconds.
                                setTimeout(() => {
                                    send({
                                        ...requestData, 
                                        body: { ...requestData.body, 'p-ticket': pTicket }
                                    });
                                }, pTime * 1000);
                                ws.removeEventListener('message', handler);
                            }
                        } else {
                            clearTimeout(race);
                            res(datum.d);
                            ws.removeEventListener('message', handler);
                        }
                    }
                }
            }
        }
        // construct the message string
        const queryString = requestData.query || ''
        const bodyString = JSON.stringify(requestData.body || '')
        const messageString = `${requestData.endpoint}\n${i}\n${queryString}\n${bodyString}`;
        ws.addEventListener('message', handler);
        ws.send(messageString);
    });
}
```

**Validation Criteria:**

* ✅ Rate limiting responses are properly handled
* ✅ P-Ticket system is implemented correctly

### 5. Reconnection

**Purpose:** Automatically reconnect after connection loss.

**Test Steps:**

1. Simulate connection drops
2. Verify automatic reconnection logic
3. Test re-authentication after reconnection
4. Verify state recovery after reconnection

**Example Implementation:**

```javascript
let lastMessageTime = new Date().getTime();

ws.onmessage = async function(event) {
    const timestamp = new Date().getTime();
    if(timestamp - lastMessageTime > 15000) {
        // connection has been idle for 15 seconds, reconnect
        await reconnect();
    }
    lastMessageTime = timestamp;
}
```

**Validation Criteria:**

* ✅ Automatic reconnection works after connection loss
* ✅ Re-authentication occurs after reconnection
* ✅ State is properly recovered after reconnection

## Error Handling

Your websocket implementation must handle:

### Connection Errors

* Network timeouts
* DNS resolution failures
* SSL/TLS handshake failures
* Server unavailable errors

### Authentication Errors

* Invalid access tokens
* Expired tokens during connection
* Authentication timeout

### Message Errors

* Malformed JSON messages
* Unknown message types
* Invalid message parameters

## Performance Requirements

* **Connection Time**: Establish connection within 5 seconds
* **Authentication**: Complete authentication within 2 seconds
* **Heartbeat**: Send heartbeats every 30 seconds
* **Reconnection**: Attempt reconnection within 10 seconds of disconnection
* **Message Processing**: Process messages within 100ms

## Testing Checklist

* [ ] Websocket connection establishes successfully
* [ ] Authentication works with valid tokens
* [ ] Heartbeats are sent and received
* [ ] Events are processed correctly
* [ ] Idempotency is maintained
* [ ] Rate limiting is handled properly
* [ ] Syncrequests work as expected and are called only once per socket lifecycle
* [ ] Reconnection works automatically
* [ ] Error handling is robust

## Next Steps

After completing Stage 2 websocket management tests, proceed to [Stage 3: User Management](/overview/conformance-testing/stage-3-user-management).