# userOrderUpdates

### Subscribe

```
{ 
    "type": "subscribe",
    "subscription": {
        "type": "userOrderUpdates",
        "addresses": ["0x742d35Cc6634C0532925a3b844Bc9e7595f7F2e2"]
    }
} 
```

### Unsubscribe

```
{ 
    "type": "unsubscribe",
    "subscription": {
        "type": "userOrderUpdates",
        "addresses": ["0x742d35Cc6634C0532925a3b844Bc9e7595f7F2e2"]
    }
}
```

### Order status data format

{% hint style="info" %}
**Reconnection Note:** When reconnecting with a session, replay and live events may overlap. Deduplicate using `(time, txIndex)` - skip updates where this tuple is at or before your last processed update. See [Session Management](/readme/websocket/session-management-and-reconnection.md#deduplication) for details.
{% endhint %}

{% hint style="info" %}
`time` is an ISO 8601 string in UTC (e.g. `"2024-01-01T00:00:00.000000000"`). `order.timestamp` is unix ms.
{% endhint %}

Each order contains an address and order details, orders will be batched per block like:

```json
  {
    "type": "userOrderUpdates",
    "seq": 1,
    "cursor": "500:1704067200000:3",
    "updates": [
      {
        "time": "2024-01-01T00:00:00.000000000",
        "user": "0x742d35cc6634c0532925a3b844bc9e7595f7f2e2",
        "hash": "0xabc...def",
        "builder": {
          "b": "0x123...456",
          "f": 100
        },
        "status": "filled",
        "txIndex": 3,
        "order": {
          "coin": "ETH",
          "side": "B",
          "limitPx": "3500.0",
          "sz": "0.5",
          "oid": 123456789,
          "timestamp": 1704067200000,
          "triggerCondition": "none",
          "isTrigger": false,
          "triggerPx": "0.0",
          "children": [],
          "isPositionTpsl": false,
          "reduceOnly": false,
          "orderType": "Limit",
          "origSz": "1.5",
          "tif": "Gtc",
          "cloid": "custom-order-123"
        }
      }
    ]
  }
```

### Examples

{% tabs %}
{% tab title="Javascript" %}

```javascript
const WebSocket = require('ws');

const ws = new WebSocket(`wss://api.hydromancer.xyz/ws?token=${process.env.HYDROMANCER_API_KEY}`);

ws.on('message', (data) => {
    const msg = JSON.parse(data);

    if (msg.type === 'connected') {
        // Subscribe to order updates for specific users
        ws.send(JSON.stringify({
            type: 'subscribe',
            subscription: {
                type: 'userOrderUpdates',
                addresses: [
                    '0x742d35cc6634c0532925a3b844bc9e7595f7f2e2',
                    '0x123456789abcdef0123456789abcdef012345678'
                ]
            }
        }));
    } else if (msg.type === 'ping') {
        ws.send(JSON.stringify({ type: 'pong' }));
    } else if (msg.type === 'userOrderUpdates') {
        console.log(`Received ${msg.updates.length} order updates`);
        msg.updates.forEach(update => {
            console.log(`Order ${update.order.oid} for ${update.user}: ${update.status}`);
        });
    }
});
```

{% endtab %}

{% tab title="Python" %}

```python
import websocket
import json
import os

def on_message(ws, message):
    msg = json.loads(message)

    if msg['type'] == 'connected':
        ws.send(json.dumps({
            "type": "subscribe",
            "subscription": {
                "type": "userOrderUpdates",
                "addresses": [
                    "0x742d35cc6634c0532925a3b844bc9e7595f7f2e2",
                    "0x123456789abcdef0123456789abcdef012345678"
                ]
            }
        }))
    elif msg['type'] == 'ping':
        print("Received ping, sending pong")
        ws.send(json.dumps({'type': 'pong'}))
    elif msg['type'] == 'userOrderUpdates':
        print(f"Received {len(msg['updates'])} order updates")
        for update in msg['updates']:
            print(f"Order {update['order']['oid']} for {update['user']}: {update['status']}")
    elif msg['type'] == 'subscriptionUpdate':
        print(f"Subscription update: {msg}")
    elif msg['type'] == 'error':
        print(f"Error: {msg}")
    else:
        print(f"Unknown message type: {msg}")

ws = websocket.WebSocketApp(
    f"wss://api.hydromancer.xyz/ws?token={os.environ.get('HYDROMANCER_API_KEY')}",
    on_message=on_message
)
ws.run_forever()
```

{% endtab %}
{% endtabs %}

<details>

<summary>Message formats</summary>

**Order object**

```json
  {
    "coin": "ETH",                   // Trading pair
    "side": "B",                     // B for Buy, A for Ask/Sell  
    "limitPx": "3500.0",             // Limit price
    "sz": "1.5",                     // Current order size
    "oid": 123456789,                // Order ID
    "timestamp": 1704067200000,      // Order placement timestamp
    "triggerCondition": "none",      // Trigger condition (none/tp/sl)
    "isTrigger": false,              // Is trigger order
    "triggerPx": "0.0",              // Trigger price
    "children": [],                  // Child orders
    "isPositionTpsl": false,         // Is position TP/SL
    "reduceOnly": false,             // Reduce only flag
    "orderType": "Limit",            // Order type (Limit/Market/Stop)
    "origSz": "2.0",                 // Original order size
    "tif": "Gtc",                    // Time in force (Gtc/Ioc/Alo) (optional)
    "cloid": "custom-123"            // Client order ID (optional)
  }
```

**Status values**

Active:

* `open` - Order is resting on the orderbook
* `triggered` - Stop/trigger order was triggered
* `scheduledCancel` - Order is scheduled for cancellation

Filled:

* `filled` - Order completely filled

Canceled:

* `canceled` - Canceled by user
* `marginCanceled` - Insufficient margin
* `liquidatedCanceled` - Position was liquidated
* `delistedCanceled` - Asset was delisted
* `reduceOnlyCanceled` - Reduce-only order canceled (no position)
* `selfTradeCanceled` - Would have resulted in self-trade
* `siblingFilledCanceled` - Sibling TP/SL order filled
* `vaultWithdrawalCanceled` - Vault withdrawal triggered cancellation
* `openInterestCapCanceled` - Open interest cap reached

Rejected:

* `badAloPxRejected` - ALO price would cross the book
* `iocCancelRejected` - IOC order couldn't fill
* `reduceOnlyRejected` - Reduce-only order rejected
* `oracleRejected` - Oracle price check failed
* `perpMarginRejected` - Insufficient perp margin
* `perpMaxPositionRejected` - Max position size exceeded
* `openInterestIncreaseRejected` - Open interest increase rejected
* `positionFlipAtOpenInterestCapRejected` - Position flip at OI cap rejected
* `positionIncreaseAtOpenInterestCapRejected` - Position increase at OI cap rejected
* `tooAggressiveAtOpenInterestCapRejected` - Too aggressive at OI cap
* `minTradeNtlRejected` - Below minimum trade notional
* `insufficientSpotBalanceRejected` - Insufficient spot balance

**Order types**

* Limit - Limit order
* Market - Market order
* Stop - Stop market order
* StopLimit - Stop limit order

**Time in Force (TIF)**

* Gtc - Good till canceled
* Ioc - Immediate or cancel
* Alo - Add liquidity only (post-only)

</details>

#### Error messages:

```
{
    "type": "error",
    "message": "Invalid API key"
}
```

#### Common errors

1. ```
   Connection timeout - Respond to ping messages
   ```
2. ```
   Too many subscriptions - Maximum 1000 addresses
   ```
3. ```
   Invalid address format - Use valid Ethereum addresses
   ```
4. ```
   Invalid API key
   ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hydromancer.xyz/readme/websocket/userorderupdates.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
