> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aultblockchain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Perpetual Subscriptions

> [!NOTE]
>
> **Testnet-Only.** Connect to `wss://test-dex-api.cloud.aultblockchain.xyz/ws/perp`. This endpoint is not available on Mainnet.

`/ws/perp` is a separate connection from Spot `/ws`, and a Spot connection does not receive Perpetual frames. The command protocol, TTL, limits, heartbeats, and reconnect rules follow [WebSocket Overview](https://docs.aultblockchain.com/dex-api/websocket/overview.md).

Use the exact `symbol` returned by [Perpetual Market Data](https://docs.aultblockchain.com/dex-api/rest/perpetual-markets.md) as `coin`, not a hex market ID or oracle feed ID. User subscriptions take the **owner wallet** address, while rows identify derived `accountId` values.

## Supported channels

| Type | Key | Initial snapshot | Live behavior |
| - | - | - | - |
| `l2Book` | `coin` | Yes | Complete published depth on book changes; replace both sides |
| `bbo` | `coin` | No | Best bid/ask changes |
| `trades` | `coin` | No | Public fills |
| `candle` | `coin`, `interval` | Yes | Bars for blocks with trades |
| `activeAssetCtx` | `coin` | No | Mark, funding, impact-price, internal-index, and book context changes |
| `allMids` | None | No | Changed market mid prices |
| `chainBlock` | None | No | Block metadata |
| `orderUpdates` | `user` | No | Changed orders; includes `accountId` and optional `reduceOnly` |
| `openOrders` | `user` | Yes, at most 500 executable orders | Snapshot only; excludes strategy parents |
| `userFills` | `user` | Yes, up to 200 | Fills, including `accountId` |
| `userEvents` | `user` | No | Delivered on the `user` channel: position updates, ledger, liquidations, and ADL fills |
| `perpState` | `user` | Yes | Account, margin configuration, position, and order-exposure deltas |
| `clearinghouseState` | `user` | Yes | Full evaluated state after account, configuration, position, or exposure changes |
| `userFundings` | `user` | Yes, up to 200 | Non-zero funding settlements |

`spotState` and `userTwapHistory` are rejected on this connection as `not applicable for perpetuals`. There are no Lending, Margin Trading, Vault, or Oracle channels.

```json theme={null}
{"method":"subscribe","subscription":{"type":"l2Book","coin":"BTC/USD"}}
```

```json theme={null}
{"method":"subscribe","subscription":{"type":"activeAssetCtx","coin":"BTC/USD"}}
```

```json theme={null}
{"method":"subscribe","subscription":{"type":"perpState","user":"ault1..."}}
```

Data frames have `{channel, data}`. Only `userEvents` uses a different channel name, `user`.

`openOrders` has no continuation token or truncation flag. For a complete owner inventory, follow every page of [REST orders](https://docs.aultblockchain.com/dex-api/rest/perpetual-accounts.md) with `status=open`. Omit `account_id` when collecting TWAP parents because their account is chosen per slice.

## Market payloads

`l2Book`, `bbo`, `trades`, `candle`, `allMids`, and `chainBlock` use the common [Spot payload shapes](https://docs.aultblockchain.com/dex-api/websocket/subscriptions.md), in the Perpetual namespace. Book `sz` is in raw base units, while trade `sz` is in normalized human base units. There is no sequence shared with REST, and `chainBlock` does not mark a point at which all channels are in sync.

`activeAssetCtx` is `{coin, ctx, time}`, where `time` is in milliseconds. Decimal values are strings. `ctx` is partial, so a field can be absent until its value is available:

| Fields | Meaning |
| - | - |
| `markPx`, `midPx`, `last` | Mark, midpoint, and last trade. `markPx` falls back to the midpoint or last trade until a mark is available |
| `bid`, `bidVolume`, `ask`, `askVolume` | Published top of book. A missing side means no liquidity on that side |
| `openInterest` | Base quantity in human units, distinct from REST notional open interest |
| `funding` | Latest published funding rate |
| `cumulativeFundingIndex`, `fundingWindowStart` | Indexed funding state. The window start is in Unix seconds |
| `impactBidPx`, `impactAskPx` | Impact-price inputs |
| `internalIndexPx`, `internalAnchorPx`, `maxLeverage` | Closed-session internal-index state when present. `maxLeverage` is a number |

Frames are sent when the context changes, so a quiet market can go without one. A frame does not mean that `markPx` is fresh or that the chain will allow a withdrawal. For the effective index and timestamps, use the [Perpetual ticker](https://docs.aultblockchain.com/dex-api/rest/perpetual-markets.md) and [native price queries](https://docs.aultblockchain.com/dex-api/advanced/chain-queries.md#perpetual-account-and-price-queries).

## Account-state payloads

`perpState.data` contains `user`, `accounts`, `marginConfigs`, `positions`, and `orderExposures`. The initial frame carries `isSnapshot: true` but no block height or time. Live frames are deltas with numeric block `height` and millisecond `time`.

| Array | Fields |
| - | - |
| `accounts` | `accountId`, `marginMode`, `scopeMarketId`, `collateral` |
| `marginConfigs` | `marketId`, `marginMode`, `leverage` |
| `positions` | `accountId`, `marketId`, `netBase`, `netQuote`, `lastFundingIndex`, `deleted` |
| `orderExposures` | `marketId`, `openBids`, `openAsks`, `deleted` |

Merge accounts by `accountId`, positions by `(accountId, marketId)`, and owner-scoped configurations and exposures by `marketId`. A position or exposure with `deleted: true` removes the item. Monetary values are decimal strings in human units; leverage, height, and time are numbers.

`clearinghouseState.data` contains `{user, accounts, positions, marginConfigs}` using the [REST response fields](https://docs.aultblockchain.com/dex-api/rest/perpetual-accounts.md#response-field-reference). Each frame replaces the previous state. It refreshes after account-state changes, **not on every mark price change**, so refresh REST before relying on mark-dependent values. Null mark-dependent fields remain null.

## Fills and reconciliation

Use the `userFills` snapshot for recent context, not as an accounting baseline. Use paginated [REST fills](https://docs.aultblockchain.com/dex-api/rest/perpetual-accounts.md) for historical quantity and notional reconciliation. REST `quantity` is already a per-event amount, so do not difference adjacent rows. Count each unique fill ID once, and do not add overlapping WebSocket snapshots to REST totals.

Perpetual `userFills.fee` uses the indexed collateral fee when available, with `feeToken` naming the asset. Older records without an indexed fee use an estimate. Use `oidHex`, not the lossy numeric `oid`, to join to full REST IDs.

## Funding and event history

`userFundings.data` is `{isSnapshot, user, fundings}`. Each item is `{id, timestamp, accountId, marketId, amount}`. `amount` is in human collateral units, positive when received and negative when paid, matching REST funding payments. Pending funding on a REST position uses the opposite sign.

`userEvents` adds these arrays on the `user` channel when present:

| Array | Item fields |
| - | - |
| `positionUpdates` | `id`, `timestamp`, `accountId`, `marketId`, `reason`, `orderId`, `realizedPnl`, `funding`, `netBase`, `netQuote` |
| `ledger` | `id`, `timestamp`, `kind`, `accountId`, `fromAccountId`, `toAccountId`, `collateralDenom`, `amount`, `reason` |
| `liquidations` | `id`, `timestamp`, `accountId`, `equity`, `maintenanceMargin`, `orderId` |
| `adlFills` | `id`, `timestamp`, `targetAccountId`, `counterpartyAccountId`, `marketId`, `isLong`, `bankruptcyPrice`, `markPrice`, `quantity`, `repaidDebt` |

`positionUpdates.funding` uses the chain sign, positive when paid, unlike `userFundings.amount`. Amounts and quantities are human-unit decimal strings, prices are quote per base, and timestamps are milliseconds.

Streams have no guaranteed replay or transactional ordering across channels. After a reconnect, resubscribe and reconcile missed history with the REST ledger, funding payments, and closeouts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.