# Account Subscriptions

The relayer bridges to mx-notifier to deliver real-time account update events. Use the `address/{bech32}` topic for unicast notifications on a single address, or the `accounts` topic to receive all global account updates on one connection.

***

## Subscribe to a Single Address

```javascript
ws.send(JSON.stringify({
  action: 'subscribe',
  topic: 'address/erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th'
}));
```

### Topic Format

```
address/{bech32}
```

The address must be a valid MultiversX bech32 address beginning with `erd1`. Updates are delivered only to the connection that subscribed — they are not broadcast to other connections.

### Subscription Confirmation

```json
{
  "action": "subscribe",
  "status": "ok",
  "topic": "address/erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
}
```

***

## Subscribe to All Account Updates

The `accounts` topic broadcasts every account update across the network to all subscribers. Use it for indexers, analytics tools, or any application that needs a global view of account state changes.

```javascript
ws.send(JSON.stringify({
  action: 'subscribe',
  topic: 'accounts'
}));
```

{% hint style="warning" %}
The `accounts` topic delivers high-volume traffic. Only subscribe if your application can process all events. For monitoring specific addresses, use `address/{bech32}` instead.
{% endhint %}

***

## What Triggers an Update

An `accountDelta` event is emitted when any of the following change on-chain for the monitored address:

* EGLD balance
* Any ESDT token balance
* Account nonce (meaning a transaction from this account was executed)

***

## accountDelta Schema

```json
{
  "type": "accountDelta",
  "data": {
    "address": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
    "shardId": 0,
    "timestamp": 1738800000000,
    "balances": {
      "EGLD": "1000000000000000000",
      "USDC-c76f1f": "50000000"
    },
    "nonce": 42,
    "blockHash": "0x...",
    "blockNonce": 12345
  }
}
```

### Field Descriptions

| Field             | Type   | Description                                                                                           |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------------- |
| `type`            | string | Always `"accountDelta"`                                                                               |
| `data.address`    | string | The bech32 address of the account                                                                     |
| `data.shardId`    | number | Shard where this account resides                                                                      |
| `data.timestamp`  | number | Block timestamp in milliseconds (Unix epoch)                                                          |
| `data.balances`   | object | Map of token identifier to current balance in atomic units. Key `"EGLD"` represents the native token. |
| `data.nonce`      | number | Current account nonce after this block                                                                |
| `data.blockHash`  | string | Hash of the block that triggered this update                                                          |
| `data.blockNonce` | number | Nonce (height) of the block that triggered this update                                                |

{% hint style="info" %}
`data.balances` contains only the tokens whose balance changed in the triggering block, not the complete account portfolio. Maintain a local state map and apply deltas on each event.
{% endhint %}

***

## Multiple Address Monitoring

Subscribe to up to 64 address topics per connection. Each subscription is independent; events for different addresses are delivered as separate messages.

```javascript
const addresses = [
  'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th',
  'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx',
  'erd1a6zgyvsfyy53cpqkkfpfl9vjyfay3qfxrgenhem6dmxfdjepkd8szdrlwe'
];

ws.onopen = () => {
  for (const address of addresses) {
    ws.send(JSON.stringify({
      action: 'subscribe',
      topic: `address/${address}`
    }));
  }
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'accountDelta') {
    console.log(`Update for ${msg.data.address}:`, msg.data.balances);
  }
};
```

***

## Reconnection Pattern

WebSocket connections can drop due to network interruptions. After reconnecting, re-subscribe to all topics to resume updates. The example below implements exponential backoff and automatic re-subscription on close.

```javascript
class AccountMonitor {
  constructor(addresses) {
    this.addresses = addresses;
    this.balances = new Map();   // address -> { token -> balance }
    this.nonces = new Map();     // address -> nonce
    this.ws = null;
    this.retryDelay = 1000;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket('wss://relayer.xoxno.com/ws');

    this.ws.onopen = () => {
      console.log('Connected, subscribing to', this.addresses.length, 'addresses');
      this.retryDelay = 1000; // Reset backoff on successful connection

      for (const address of this.addresses) {
        this.ws.send(JSON.stringify({
          action: 'subscribe',
          topic: `address/${address}`
        }));
      }
    };

    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.type === 'accountDelta') {
        this.applyDelta(msg.data);
      }
    };

    this.ws.onclose = () => {
      console.log(`Connection closed, retrying in ${this.retryDelay}ms`);
      setTimeout(() => this.connect(), this.retryDelay);
      this.retryDelay = Math.min(this.retryDelay * 2, 30000);
    };

    this.ws.onerror = (err) => {
      console.error('WebSocket error:', err);
      this.ws.close();
    };
  }

  applyDelta(data) {
    if (!this.balances.has(data.address)) {
      this.balances.set(data.address, new Map());
    }
    const addrBalances = this.balances.get(data.address);

    for (const [token, balance] of Object.entries(data.balances)) {
      addrBalances.set(token, balance);
    }

    this.nonces.set(data.address, data.nonce);
    this.onUpdate?.(data.address, data);
  }

  getBalance(address, token = 'EGLD') {
    return this.balances.get(address)?.get(token) ?? '0';
  }

  getNonce(address) {
    return this.nonces.get(address) ?? 0;
  }
}

// Usage
const monitor = new AccountMonitor([
  'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th'
]);

monitor.onUpdate = (address, data) => {
  console.log(`[${address}] nonce=${data.nonce} block=${data.blockNonce}`);
  for (const [token, balance] of Object.entries(data.balances)) {
    console.log(`  ${token}: ${balance}`);
  }
};
```

***

## Unsubscribe

```javascript
ws.send(JSON.stringify({
  action: 'unsubscribe',
  topic: 'address/erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th'
}));
```

***

## Error Responses

### Invalid Address

```json
{
  "action": "subscribe",
  "status": "fail",
  "reason": "invalid bech32 address in topic"
}
```

### Topic Limit Reached

```json
{
  "action": "subscribe",
  "status": "fail",
  "reason": "topic limit reached"
}
```

{% hint style="warning" %}
Each connection supports a maximum of 64 active topic subscriptions. Unsubscribe from addresses no longer needed to stay within the limit.
{% endhint %}

***

## Related Pages

* [Gas Statistics](/developers/relayer-api/gas-stats.md) — Per-shard gas price and PPU streams
* [Transaction Broadcasting](/developers/relayer-api/transaction-broadcasting.md) — Submit pre-signed transactions
* [Transaction Relaying](/developers/relayer-api/transaction-relaying.md) — Co-signed relayed transactions
* [Error Reference](https://github.com/XOXNO/docs/blob/main/developers/relayer-api/error-reference.md) — All error codes and resolutions


---

# 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.xoxno.com/developers/relayer-api/account-subscriptions.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.
