> 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/builder-data/builderapprovedfills.md).

# builderApprovedFills

Stream every fill made by users who had an active fee approval for your builder at the time of the fill.

Every live data message includes both routing identifiers with the same value:

```json
{"type": "builderApprovedFills", "channel": "builderApprovedFills"}
```

Other payload fields are omitted above. Existing clients may continue routing on either field.

{% hint style="info" %}
💧New endpoint - this endpoint is not a part of original Hyperliquid API and is added by us for builder convenience.
{% endhint %}

### How this differs from `builderFills`

[`builderFills`](/readme/websocket/builder-data/builderfills.md) streams fills that were **routed through** your builder — the fill's own `builder` field matches your address.

`builderApprovedFills` streams every fill made by a user **while your builder-fee approval was active for them**, regardless of how that order was routed. A user who approved your builder and then traded through another frontend still appears here, and those fills carry `"builder": null`.

Consequences worth planning for:

* A fill can appear on both channels. Subscribe to both only if you want that.
* One fill can be attributed to several builders at once — a user may hold approvals from more than one. Each subscribed builder receives its own copy.
* The set is driven by approval state at the block of the fill, not by current approval state. Revoking an approval stops future fills from appearing; it does not retract past ones.
* An approval is treated as revoked only when its max fee rate is set to exactly `0%`.

### Subscribe

```
{
    "type": "subscribe",
    "subscription": {
        "type": "builderApprovedFills",
        "builder": "0xb84168cf3be63c6b8dad05ff5d755e97432ff80b"
    }
}
```

`builder` is required and must be a 20-byte hex address. It is matched case-insensitively.

### Unsubscribe

```
{
    "type": "unsubscribe",
    "subscription": {
        "type": "builderApprovedFills",
        "builder": "0xb84168cf3be63c6b8dad05ff5d755e97432ff80b"
    }
}
```

### Fill data format

Each entry is `[address, fill]`, where `address` is the user who made the fill.

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

```json
{
  "type": "builderApprovedFills",
  "channel": "builderApprovedFills",
  "seq": 1,
  "cursor": "500:1704067200000:3",
  "fills": [
    [
      "0x742d35cc6634c0532925a3b844bc9e7595f7f2e2",
      {
        "coin": "ETH",
        "px": "2150.50",              // price
        "sz": "1.5",                  // size
        "side": "B",                  // B=buy, A=sell
        "time": 1704067200000,        // timestamp (ms)
        "startPosition": "1.5",       // position before fill
        "dir": "Open Long",           // direction
        "closedPnl": "125.50",        // realized PnL
        "hash": "0xabc...def",        // fill hash
        "oid": 12345678,              // order ID
        "crossed": false,             // was crossed
        "fee": "2.50",                // fee amount
        "tid": 87654321,              // trade ID
        "cloid": "client-123",        // client order ID (optional)
        "builderFee": "0.10",         // builder fee (optional)
        "deployerFee": "0.05",        // deployer fee (optional, HIP-3 only)
        "priorityGas": null,          // priority gas fee in HYPE (optional)
        "feeToken": "USDC",           // fee token
        "builder": null,              // routed builder - null when not routed through a builder
        "twapId": 913412,             // null if not a twap
        "txIndex": 3                  // transaction index within block
      }
    ]
  ]
}
```

Note that `builder` inside the fill is the **routed** builder and is frequently `null`. Attribution to your builder is implied by the channel, not by this field.

### 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') {
        ws.send(JSON.stringify({
            type: 'subscribe',
            subscription: {
                type: 'builderApprovedFills',
                builder: '0xb84168cf3be63c6b8dad05ff5d755e97432ff80b'
            }
        }));
    } else if (msg.type === 'ping') {
        ws.send(JSON.stringify({ type: 'pong' }));
    } else if (msg.type === 'builderApprovedFills') {
        console.log(`Received ${msg.fills.length} approved fills`);
    }
});
```

{% 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": "builderApprovedFills",
                "builder": "0xb84168cf3be63c6b8dad05ff5d755e97432ff80b"
            }
        }))
    elif msg['type'] == 'ping':
        print("Received ping, sending pong")
        ws.send(json.dumps({'type': 'pong'}))
    elif msg['type'] == 'builderApprovedFills':
        print(f"Received {len(msg['fills'])} approved fills")
    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 %}

#### Error messages:

```
{
    "type": "error",
    "message": "Builder address required for builderApprovedFills subscription"
}
```

#### Common errors

1. ```
   Builder address required for builderApprovedFills subscription
   ```
2. ```
   Invalid address format
   ```
3. ```
   Too many subscriptions - Maximum builder subscriptions per API key
   ```
4. ```
   Invalid API key
   ```
