> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/websocket/builderorderupdates.md).

# builderOrderUpdates

### Subscribe

```
{ 
    "type": "subscribe",
    "subscription": {
        "type": "builderOrderUpdates",
        "builder": "0x742d35Cc6634C0532925a3b844Bc9e7595f7F2e2"
    }
} 
```

### Unsubscribe

```
{ 
    "type": "unsubscribe",
    "subscription": {
        "type": "builderOrderUpdates",
        "builder": "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": "builderOrderUpdates",
    "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: 'builderOrderUpdates',
                builder: '0x742d35cc6634c0532925a3b844bc9e7595f7f2e2'
            }
        }));
    } else if (msg.type === 'ping') {
        ws.send(JSON.stringify({ type: 'pong' }));
    } else if (msg.type === 'builderOrderUpdates') {
        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": "builderOrderUpdates",
                "builder": "0x123.."
            }
        }))
    elif msg['type'] == 'ping':
        print("Received ping, sending pong")
        ws.send(json.dumps({'type': 'pong'}))
    elif msg['type'] == 'builderOrderUpdates':
        print(f"Received {len(msg['updates'])} orders")
    elif msg['type'] == 'subscriptionUpdate':
        print(f"Subscription update: {msg}")
    elif msg['type'] == 'error':
        print(f"Error: {msg}")
    else:
        print(f"Unknown message type: {msg}")

def on_open(ws):
    print("WebSocket connection opened")

# Get API key from environment variable
api_key = os.environ.get('HYDROMANCER_API_KEY')
if not api_key:
    raise ValueError("HYDROMANCER_API_KEY environment variable not set")

# Construct WebSocket URL with token parameter
ws_url = f'wss://api.hydromancer.xyz/ws?token={api_key}'

ws = websocket.WebSocketApp(ws_url,
                            on_open=on_open,
                            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 builder subscriptions per API key
   ```
3. ```
   Invalid builder code
   ```
4. ```
   Invalid API key
   ```
