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

# Authenticated WebSocket

`/ws` serves every channel without authentication, delayed by 20 seconds. For live updates on
your own account, connect to `/ws/user` and authenticate with a
[read credential](/api-reference/read-authentication#read-credential).

```text theme={null}
Mainnet: wss://api.notional.xyz/ws/user
Testnet: wss://api.notional-testnet.xyz/ws/user
```

## Authenticate

Send `auth` as the first message. `credential` is the full `Authorization` value, including the
`NotionalRead` scheme.

```json theme={null}
{
  "method": "auth",
  "credential": "NotionalRead eyJtZXNzYWdlIjp7…"
}
```

```json theme={null}
{
  "channel": "authResponse",
  "data": {
    "ok": true,
    "kind": "user",
    "user": "0x1111111111111111111111111111111111111111",
    "expiresAtMs": 1760000900000
  }
}
```

A refused credential returns `{ "ok": false, "code": "<reason>" }`. Without an authenticated
session, the server closes the connection with code `1008`. A failed renewal keeps the existing
session, subscriptions and connection open; it does not extend the credential's expiry.
A new connection that has not authenticated within 10 seconds is also closed with code `1008`.

## Channels

After `auth`, use `subscribe`, `unsubscribe` and `ping` as on [`/ws`](/api-reference/websocket/overview).

| Channels | Allowed |
| - | - |
| `orderUpdates`, `userFills`, `accountSummary`, `spotState`, `twapUpdates`, `userTransactions`, `userOrderRejections`, `activeAssetData`, `userFundings`, `userNonFundingLedgerUpdates`, `userBorrowLendInterest` | Only with `user` equal to the credential's user |
| `perpMeta`, `spotMeta`, `predictionMeta`, `borrowLendState` | Yes |
| `newBlocks`, `newTransactions` | No; use `/ws` |

A refused subscription returns:

```json theme={null}
{
  "channel": "subscriptionRefused",
  "data": {
    "subscription": { "type": "newTransactions" },
    "code": "channel_not_allowed",
    "message": "Subscription refused: channel_not_allowed"
  }
}
```

## Renew Before Expiry

Send another `auth` message with a fresh credential before `expiresAtMs`; the connection and its
subscriptions stay open. If the new credential is for a different account, subscriptions the new
credential cannot read are dropped with `subscriptionRefused`.

The server re-checks each session every 30 seconds, and immediately when the account's API wallets
change. When the credential expires or its signing API wallet is revoked, the server drops every
subscription and sends:

```json theme={null}
{
  "channel": "authExpired",
  "data": {
    "code": "expired",
    "subscriptions": [{ "type": "userFills", "user": "0x1111111111111111111111111111111111111111" }]
  }
}
```

`authRevoked` has the same shape, with `code: "unauthorized_signer"`. After either notification,
the connection stays open for 10 seconds to authenticate again. Send a fresh `auth`, then
subscribe again. A refused credential or failure to authenticate within that window closes
the connection with code `1008`.

## Refusal Codes

| Code | Meaning |
| - | - |
| `missing` | No credential, or a subscription before a successful `auth` |
| `malformed`, `unsupported_version` | The credential does not parse, or its version is unsupported |
| `wrong_network` | `notionalChain` does not match the deployment |
| `expired`, `not_yet_valid`, `lifetime_too_long` | The validity window check failed |
| `bad_signature`, `unauthorized_signer` | Invalid signature, or the signer is not the account or an approved API wallet |
| `wrong_user` | The subscription is for another account |
| `channel_not_allowed` | The channel is not served on `/ws/user` |


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