# stellar_subscribe

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

WebSocket · Send JSON-RPC messages after opening a connection.

Stellar Mainnet: wss://stellar-mainnet.g.alchemy.com/v2/YOUR_API_KEY
Stellar Testnet: wss://stellar-testnet.g.alchemy.com/v2/YOUR_API_KEY

# Stellar WebSocket subscriptions

Receive new Stellar ledgers, contract events, transactions, and classic payments over one WebSocket connection. You can subscribe to mainnet or testnet and narrow notifications with topic-specific filters.

## Connect with your API key

Create an API key in the [Alchemy Dashboard](https://dashboard.alchemy.com/). Choose the endpoint for your network and replace `YOUR_API_KEY`:

| Network | WebSocket URL |
| --- | --- |
| Mainnet | `wss://stellar-mainnet.g.alchemy.com/v2/YOUR_API_KEY` |
| Testnet | `wss://stellar-testnet.g.alchemy.com/v2/YOUR_API_KEY` |

Send the messages below over the WebSocket after it opens. These methods use positional JSON-RPC `params` arrays. They are WebSocket methods, not HTTP JSON-RPC calls, and do not use `eth_subscribe`.

This JavaScript example uses the built-in WebSocket client in Node.js 22 or later. Set `ALCHEMY_API_KEY` in your environment before running it:

```javascript
const apiKey = process.env.ALCHEMY_API_KEY;
if (!apiKey) throw new Error('Set ALCHEMY_API_KEY');

const ws = new WebSocket(
  `wss://stellar-mainnet.g.alchemy.com/v2/${encodeURIComponent(apiKey)}`,
);

ws.addEventListener('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'stellar_subscribe',
    params: ['newLedgers'],
  }));
});

let subscriptionId;
ws.addEventListener('message', ({ data }) => {
  const message = JSON.parse(data);
  if (message.error) {
    console.error(message.error);
  } else if (message.id === 1) {
    subscriptionId = message.result;
    console.log('Subscribed:', subscriptionId);
  } else if (message.method === 'stellar_subscription') {
    console.log(message.params.subscription, message.params.result);
  }
});
ws.addEventListener('error', () => console.error('WebSocket connection failed'));
ws.addEventListener('close', ({ code, reason }) => console.log('Closed:', code, reason));

// When finished, call this while the connection is still open.
function unsubscribe() {
  if (subscriptionId && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({
      jsonrpc: '2.0', id: 2, method: 'stellar_unsubscribe',
      params: [subscriptionId],
    }));
  }
}
```

The example shows one connection lifecycle. Add reconnect, backfill, and deduplication for applications that need complete records; see [Recover after a disconnect](#recover-after-a-disconnect).

## Subscribe and receive notifications

Send `stellar_subscribe` with `[topic]` or `[topic, filter]`. An omitted filter or `{}` matches all eligible records. `null` is not a filter object. Use a unique request ID to associate each acknowledgement with its request.

```json
{"jsonrpc":"2.0","id":1,"method":"stellar_subscribe","params":["newLedgers"]}
```

The response contains an opaque subscription ID. Keep it to route notifications and unsubscribe on the same connection:

```json
{"jsonrpc":"2.0","id":1,"result":"0x0123456789abcdef0123456789abcdef"}
```

Notifications have no request `id`. `params.result` contains one record, not an array or an RPC pagination envelope:

```json
{
  "jsonrpc": "2.0",
  "method": "stellar_subscription",
  "params": {
    "subscription": "0x0123456789abcdef0123456789abcdef",
    "result": {
      "ledger": 5193687,
      "txHash": "de3197681ae894f6256ec97c9b2e472bd139f59194c40de0b4b92de603369a84",
      "createdAt": 1791992022,
      "operationIndex": 0,
      "type": "payment",
      "from": "GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST",
      "to": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
      "operation": {
        "destination": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
        "asset": "native",
        "amount": "1"
      }
    }
  }
}
```

This is a `payments` notification. Keep the mapping between each subscription ID and its topic; the notification envelope does not repeat the topic name.

| Topic | One notification contains | Optional filters |
| --- | --- | --- |
| `newLedgers` | One closed ledger | None |
| `events` | One Stellar event, including Soroban events | `type`, `contractIds`, `topics` |
| `transactions` | One included transaction, successful or failed | `status`, `accounts` |
| `payments` | One classic payment or path-payment operation from a successful transaction | `accounts`, `from`, `to` |

Different subscriptions can overlap. The same transaction can produce transaction, event, and payment notifications. Do not treat notifications across subscriptions as a globally ordered stream.

## New ledgers

```json
{"jsonrpc":"2.0","id":10,"method":"stellar_subscribe","params":["newLedgers"]}
```

The result fields are:

| Field | JSON type | Meaning |
| --- | --- | --- |
| `sequence` | integer | Ledger sequence number |
| `hash` | string | Ledger hash as hexadecimal |
| `ledgerCloseTime` | string | Unix timestamp in seconds, encoded as a decimal string |
| `headerXdr` | string | Base64-encoded ledger header XDR |
| `metadataXdr` | string | Base64-encoded ledger metadata XDR |

Example result (XDR strings are abbreviated placeholders):

```json
{
  "sequence": 5193684,
  "hash": "0255a52580756425bdf01702907832873c1afdb574f843411dd31429e329540a",
  "ledgerCloseTime": "1791992007",
  "headerXdr": "<base64 ledger header XDR>",
  "metadataXdr": "<base64 ledger metadata XDR>"
}
```

There is no ledger-range, starting-ledger, or replay filter. Ledger payloads can be large; configure your WebSocket client's incoming message size appropriately.

## Events and Soroban transfers

Use `events` for contract events, including Soroban token transfers:

```json
{"jsonrpc":"2.0","id":20,"method":"stellar_subscribe","params":["events",{"type":"contract","contractIds":["CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"]}]}
```

| Filter | Shape | Meaning |
| --- | --- | --- |
| `type` | `"contract"` or `"system"` | Match the exact event type; omit to receive all returned event types |
| `contractIds` | Array of 1–100 C StrKeys | Match any listed contract |
| `topics` | Array of 1–5 topic patterns | Match any pattern; each pattern contains 1–4 segments |

Within a pattern, each position must match. `"*"` matches exactly one segment. Other segments are base64 XDR values, not decoded text or addresses. Each encoded segment is limited to 4,096 characters. A pattern must have the same number of segments as the event.

To match common three-segment and four-segment transfer event formats, include both patterns:

```json
{
  "jsonrpc": "2.0",
  "id": 21,
  "method": "stellar_subscribe",
  "params": ["events", {
    "type": "contract",
    "topics": [
      ["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*"],
      ["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*", "*"]
    ]
  }]
}
```

`AAAADwAAAAh0cmFuc2Zlcg==` encodes the XDR symbol `transfer`. Custom contracts can emit different event formats. Add `contractIds` to limit the stream to the token contracts you want. The stream does not normalize token transfers or convert their amounts for you.

Example result:

```json
{
  "type": "contract",
  "ledger": 5193684,
  "ledgerClosedAt": "2026-10-14T15:33:27Z",
  "contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
  "id": "0022306702925758464-0000000000",
  "operationIndex": 0,
  "transactionIndex": 0,
  "txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
  "inSuccessfulContractCall": false,
  "topic": ["AAAADwAAAANmZWUA", "AAAAEgAAAAAAAAAAA+0oimKONzC713z+Cl8orRHR8/s0JY+JMothA0fY7bY="],
  "value": "AAAACgAAAAAAAAAAAAAAAAAAAGQ="
}
```

This example is a fee event, not a transfer. The result field is singular `topic`; the filter field is plural `topics`. `topic` entries and `value` remain base64 XDR. `ledgerClosedAt` is an ISO-8601 timestamp. `contractId` can be absent or empty for events without an associated contract; tolerate additional fields and event types.

A contract event is not by itself proof that a transfer succeeded. Check `inSuccessfulContractCall` and, when your application requires transaction success, the transaction result. There is no subscription filter for `inSuccessfulContractCall`, transfer amount, or decoded event value; apply those checks in your client.

## Transactions

```json
{"jsonrpc":"2.0","id":30,"method":"stellar_subscribe","params":["transactions",{"status":"SUCCESS","accounts":["GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST"]}]}
```

`status` accepts `SUCCESS` or `FAILED`. Omit it for both. `accounts` accepts 1–100 G or M StrKeys and matches any listed account in the transaction source, fee-bump fee payer, operation sources, or direct operation counterparties: destination, trustor, source (`from`), sponsored account, and account-merge destination.

This is not an account balance or full state-change subscription. It does not match asset issuers, claimants, memo text, or arbitrary smart-contract arguments. There is no pending-transaction subscription.

| Field | JSON type | Meaning |
| --- | --- | --- |
| `status` | string | `SUCCESS` or `FAILED` |
| `txHash` | string | Transaction hash as hexadecimal |
| `ledger` | integer | Ledger containing the transaction |
| `createdAt` | integer | Ledger close time as Unix seconds |
| `applicationOrder` | integer | Transaction application order within the ledger |
| `feeBump` | boolean | Whether the transaction uses a fee-bump envelope |
| `envelopeJson` | object | Transaction envelope in Stellar XDR-JSON format |
| `resultJson` | object | Transaction result in XDR-JSON format |
| `resultMetaJson` | object | Transaction result metadata in XDR-JSON format |
| `events` | object | Transaction event data, when provided |

Example selected fields (the three XDR-JSON objects and `events` are omitted here):

```json
{
  "status": "FAILED",
  "txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
  "applicationOrder": 1,
  "feeBump": false,
  "ledger": 5193684,
  "createdAt": 1791992007
}
```

The full envelope uses a tagged union: regular transactions are under `envelopeJson.tx.tx`, v0 transactions under `envelopeJson.tx_v0.tx`, and fee-bump inner transactions under `envelopeJson.tx_fee_bump.tx.inner_tx.tx.tx`. These objects use Stellar's [XDR-JSON format (SEP-0051)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0051.md), including decimal strings for 64-bit integers. Keep those values as strings or use arbitrary-precision integers; do not convert them to JavaScript `Number` indiscriminately.

## Classic payments

```json
{"jsonrpc":"2.0","id":40,"method":"stellar_subscribe","params":["payments",{"to":["GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR"]}]}
```

`accounts` matches a payment's sender or recipient. `from` matches its sender; `to` matches its recipient. Each filter accepts an array of 1–100 G or M StrKeys. Combine filters to require all of their conditions.

A result contains `ledger` (integer), `txHash` (string), `createdAt` (Unix seconds as an integer), `operationIndex` (zero-based integer), `type`, `from`, `to`, and an `operation` object. The notification example above shows a complete classic payment result. If an operation has its own source account, `from` is that account.

| `type` | `operation` fields | Amount semantics |
| --- | --- | --- |
| `payment` | `destination`, `asset`, `amount` | Amount transferred |
| `path_payment_strict_receive` | `send_asset`, `send_max`, `destination`, `dest_asset`, `dest_amount`, `path` | Exact destination amount; `send_max` is an upper bound, not the actual amount spent |
| `path_payment_strict_send` | `send_asset`, `send_amount`, `destination`, `dest_asset`, `dest_min`, `path` | Exact source amount; `dest_min` is a lower bound, not the actual amount received |

Amounts are decimal strings in Stellar integer units, not human-readable token balances. For classic Stellar assets, divide by `10^7` to obtain standard units: `"1"` is 0.0000001 XLM when the asset is `"native"`. Assets retain their XDR-JSON representation: native XLM is `"native"`; issued assets use `credit_alphanum4` or `credit_alphanum12` objects containing `asset_code` and `issuer`.

Only successful transactions produce payment notifications. A transaction with multiple payment operations produces multiple records. Account creation, account merge, and Soroban contract transfers do not produce `payments` records. Use `events` for Soroban transfers. Use transaction results if you need the actual executed amounts of path payments.

## Filter rules and errors

Filter fields are ANDed together; addresses within each array are ORed. Addresses are case-sensitive and must be valid StrKeys, including checksums. M addresses match exactly; they do not automatically match the underlying G address. Account filters do not accept C contract addresses.

Unknown topics or filter fields, empty address arrays, malformed addresses, invalid enum values, and unsupported filter shapes are rejected. For example:

```json
{"jsonrpc":"2.0","id":50,"method":"stellar_subscribe","params":["payments",{"unknown":true}]}
```

returns a JSON-RPC error with code `-32602` (`Invalid params`). Inspect the response's `error` object instead of treating its `result` as a subscription ID. Authentication and access errors can also prevent the WebSocket connection from opening.

## Unsubscribe

Send the ID returned by `stellar_subscribe` over the same connection:

```json
{"jsonrpc":"2.0","id":60,"method":"stellar_unsubscribe","params":["0x0123456789abcdef0123456789abcdef"]}
```

```json
{"jsonrpc":"2.0","id":60,"result":true}
```

The result is `true` when the subscription was removed and `false` if the ID was not found on that connection. Already-in-flight notifications may arrive while you unsubscribe. Other subscriptions on the connection remain active. Closing the connection ends all of its subscriptions; IDs cannot be reused after reconnecting.

## Recover after a disconnect

Subscriptions deliver new activity after initialization. The acknowledgement confirms that the subscription was accepted; it does not mean that a record is immediately available or guarantee delivery from the instant you sent the request. There is no `startLedger`, historical replay, resume token, or acknowledgement-based delivery option.

Delivery is best effort. Disconnections and interruptions can cause gaps; there is no durable exactly-once or at-least-once guarantee. There is no fixed notification latency guarantee. A quiet subscription can mean that no new records match its filters.

If your application needs complete history:

1. Persist the last processed ledger and record IDs for each network.
2. Reconnect with backoff and jitter, then recreate your subscriptions with new IDs.
3. Buffer new live notifications while you retrieve an overlapping historical range through the corresponding HTTP RPC methods: `getLedgers`, `getEvents`, or `getTransactions`. Recover classic payments from successful transaction operations.
4. Merge historical and live records and deduplicate by network plus ledger hash/sequence, event `id`, transaction `txHash`, or payment `(txHash, operationIndex)`.

Historical RPC availability is bounded by retained history. Recover promptly; a gap older than retained history may need another archive source. Do not infer that an empty filtered stream is fully caught up or that the last notification's ledger marks the end of all records for that ledger.

Close code `1011` can indicate a service interruption or an unrecoverable delivery gap. Reconnect and recover missing records. Close code `1008` can indicate a policy or slow-consumer limit; address the cause before reconnecting. Process notifications promptly, bound your client queues, and reduce subscription scope if your client cannot keep up. Your plan's connection, subscription, and usage limits still apply.

## Machine-readable contracts

Use the [OpenRPC reference](https://multi-network-ws-api-specs.docs.alchemy.com/stellar-websocket/openrpc.json) for subscribe/unsubscribe method contracts and the [message JSON Schema](https://multi-network-ws-api-specs.docs.alchemy.com/stellar-websocket/messages.schema.json) for positional parameters and notification shapes. Result records may include additional fields; clients should ignore unknown fields for forward compatibility.


Reference: https://www.alchemy.com/docs/chains/stellar/websocket/stellar-subscribe

## Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| topic | enum | Yes | Select the result record type. |
| filter | object | No | Optional topic-specific filter. Omit or use {} for all records. Only fields for the selected topic are accepted. |

## Result

**subscriptionId** (string): Opaque ID scoped to this WebSocket connection. Later records arrive as stellar_subscription notifications.

## Example

### Request

```json
{
  "jsonrpc": "2.0",
  "method": "stellar_subscribe",
  "params": [
    "newLedgers"
  ],
  "id": 1
}
```

### Response

```json
{
  "jsonrpc": "2.0",
  "result": "0x0123456789abcdef0123456789abcdef",
  "id": 1
}
```

## OpenRPC Method Specification

```yaml
name: stellar_subscribe
summary: Subscribe to new Stellar activity over WebSocket
description: |
  # Stellar WebSocket subscriptions

  Receive new Stellar ledgers, contract events, transactions, and classic payments over one WebSocket connection. You can subscribe to mainnet or testnet and narrow notifications with topic-specific filters.

  ## Connect with your API key

  Create an API key in the [Alchemy Dashboard](https://dashboard.alchemy.com/). Choose the endpoint for your network and replace `YOUR_API_KEY`:

  | Network | WebSocket URL |
  | --- | --- |
  | Mainnet | `wss://stellar-mainnet.g.alchemy.com/v2/YOUR_API_KEY` |
  | Testnet | `wss://stellar-testnet.g.alchemy.com/v2/YOUR_API_KEY` |

  Send the messages below over the WebSocket after it opens. These methods use positional JSON-RPC `params` arrays. They are WebSocket methods, not HTTP JSON-RPC calls, and do not use `eth_subscribe`.

  This JavaScript example uses the built-in WebSocket client in Node.js 22 or later. Set `ALCHEMY_API_KEY` in your environment before running it:

  ```javascript
  const apiKey = process.env.ALCHEMY_API_KEY;
  if (!apiKey) throw new Error('Set ALCHEMY_API_KEY');

  const ws = new WebSocket(
    `wss://stellar-mainnet.g.alchemy.com/v2/${encodeURIComponent(apiKey)}`,
  );

  ws.addEventListener('open', () => {
    ws.send(JSON.stringify({
      jsonrpc: '2.0',
      id: 1,
      method: 'stellar_subscribe',
      params: ['newLedgers'],
    }));
  });

  let subscriptionId;
  ws.addEventListener('message', ({ data }) => {
    const message = JSON.parse(data);
    if (message.error) {
      console.error(message.error);
    } else if (message.id === 1) {
      subscriptionId = message.result;
      console.log('Subscribed:', subscriptionId);
    } else if (message.method === 'stellar_subscription') {
      console.log(message.params.subscription, message.params.result);
    }
  });
  ws.addEventListener('error', () => console.error('WebSocket connection failed'));
  ws.addEventListener('close', ({ code, reason }) => console.log('Closed:', code, reason));

  // When finished, call this while the connection is still open.
  function unsubscribe() {
    if (subscriptionId && ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({
        jsonrpc: '2.0', id: 2, method: 'stellar_unsubscribe',
        params: [subscriptionId],
      }));
    }
  }
  ```

  The example shows one connection lifecycle. Add reconnect, backfill, and deduplication for applications that need complete records; see [Recover after a disconnect](#recover-after-a-disconnect).

  ## Subscribe and receive notifications

  Send `stellar_subscribe` with `[topic]` or `[topic, filter]`. An omitted filter or `{}` matches all eligible records. `null` is not a filter object. Use a unique request ID to associate each acknowledgement with its request.

  ```json
  {"jsonrpc":"2.0","id":1,"method":"stellar_subscribe","params":["newLedgers"]}
  ```

  The response contains an opaque subscription ID. Keep it to route notifications and unsubscribe on the same connection:

  ```json
  {"jsonrpc":"2.0","id":1,"result":"0x0123456789abcdef0123456789abcdef"}
  ```

  Notifications have no request `id`. `params.result` contains one record, not an array or an RPC pagination envelope:

  ```json
  {
    "jsonrpc": "2.0",
    "method": "stellar_subscription",
    "params": {
      "subscription": "0x0123456789abcdef0123456789abcdef",
      "result": {
        "ledger": 5193687,
        "txHash": "de3197681ae894f6256ec97c9b2e472bd139f59194c40de0b4b92de603369a84",
        "createdAt": 1791992022,
        "operationIndex": 0,
        "type": "payment",
        "from": "GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST",
        "to": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
        "operation": {
          "destination": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
          "asset": "native",
          "amount": "1"
        }
      }
    }
  }
  ```

  This is a `payments` notification. Keep the mapping between each subscription ID and its topic; the notification envelope does not repeat the topic name.

  | Topic | One notification contains | Optional filters |
  | --- | --- | --- |
  | `newLedgers` | One closed ledger | None |
  | `events` | One Stellar event, including Soroban events | `type`, `contractIds`, `topics` |
  | `transactions` | One included transaction, successful or failed | `status`, `accounts` |
  | `payments` | One classic payment or path-payment operation from a successful transaction | `accounts`, `from`, `to` |

  Different subscriptions can overlap. The same transaction can produce transaction, event, and payment notifications. Do not treat notifications across subscriptions as a globally ordered stream.

  ## New ledgers

  ```json
  {"jsonrpc":"2.0","id":10,"method":"stellar_subscribe","params":["newLedgers"]}
  ```

  The result fields are:

  | Field | JSON type | Meaning |
  | --- | --- | --- |
  | `sequence` | integer | Ledger sequence number |
  | `hash` | string | Ledger hash as hexadecimal |
  | `ledgerCloseTime` | string | Unix timestamp in seconds, encoded as a decimal string |
  | `headerXdr` | string | Base64-encoded ledger header XDR |
  | `metadataXdr` | string | Base64-encoded ledger metadata XDR |

  Example result (XDR strings are abbreviated placeholders):

  ```json
  {
    "sequence": 5193684,
    "hash": "0255a52580756425bdf01702907832873c1afdb574f843411dd31429e329540a",
    "ledgerCloseTime": "1791992007",
    "headerXdr": "<base64 ledger header XDR>",
    "metadataXdr": "<base64 ledger metadata XDR>"
  }
  ```

  There is no ledger-range, starting-ledger, or replay filter. Ledger payloads can be large; configure your WebSocket client's incoming message size appropriately.

  ## Events and Soroban transfers

  Use `events` for contract events, including Soroban token transfers:

  ```json
  {"jsonrpc":"2.0","id":20,"method":"stellar_subscribe","params":["events",{"type":"contract","contractIds":["CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"]}]}
  ```

  | Filter | Shape | Meaning |
  | --- | --- | --- |
  | `type` | `"contract"` or `"system"` | Match the exact event type; omit to receive all returned event types |
  | `contractIds` | Array of 1–100 C StrKeys | Match any listed contract |
  | `topics` | Array of 1–5 topic patterns | Match any pattern; each pattern contains 1–4 segments |

  Within a pattern, each position must match. `"*"` matches exactly one segment. Other segments are base64 XDR values, not decoded text or addresses. Each encoded segment is limited to 4,096 characters. A pattern must have the same number of segments as the event.

  To match common three-segment and four-segment transfer event formats, include both patterns:

  ```json
  {
    "jsonrpc": "2.0",
    "id": 21,
    "method": "stellar_subscribe",
    "params": ["events", {
      "type": "contract",
      "topics": [
        ["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*"],
        ["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*", "*"]
      ]
    }]
  }
  ```

  `AAAADwAAAAh0cmFuc2Zlcg==` encodes the XDR symbol `transfer`. Custom contracts can emit different event formats. Add `contractIds` to limit the stream to the token contracts you want. The stream does not normalize token transfers or convert their amounts for you.

  Example result:

  ```json
  {
    "type": "contract",
    "ledger": 5193684,
    "ledgerClosedAt": "2026-10-14T15:33:27Z",
    "contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
    "id": "0022306702925758464-0000000000",
    "operationIndex": 0,
    "transactionIndex": 0,
    "txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
    "inSuccessfulContractCall": false,
    "topic": ["AAAADwAAAANmZWUA", "AAAAEgAAAAAAAAAAA+0oimKONzC713z+Cl8orRHR8/s0JY+JMothA0fY7bY="],
    "value": "AAAACgAAAAAAAAAAAAAAAAAAAGQ="
  }
  ```

  This example is a fee event, not a transfer. The result field is singular `topic`; the filter field is plural `topics`. `topic` entries and `value` remain base64 XDR. `ledgerClosedAt` is an ISO-8601 timestamp. `contractId` can be absent or empty for events without an associated contract; tolerate additional fields and event types.

  A contract event is not by itself proof that a transfer succeeded. Check `inSuccessfulContractCall` and, when your application requires transaction success, the transaction result. There is no subscription filter for `inSuccessfulContractCall`, transfer amount, or decoded event value; apply those checks in your client.

  ## Transactions

  ```json
  {"jsonrpc":"2.0","id":30,"method":"stellar_subscribe","params":["transactions",{"status":"SUCCESS","accounts":["GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST"]}]}
  ```

  `status` accepts `SUCCESS` or `FAILED`. Omit it for both. `accounts` accepts 1–100 G or M StrKeys and matches any listed account in the transaction source, fee-bump fee payer, operation sources, or direct operation counterparties: destination, trustor, source (`from`), sponsored account, and account-merge destination.

  This is not an account balance or full state-change subscription. It does not match asset issuers, claimants, memo text, or arbitrary smart-contract arguments. There is no pending-transaction subscription.

  | Field | JSON type | Meaning |
  | --- | --- | --- |
  | `status` | string | `SUCCESS` or `FAILED` |
  | `txHash` | string | Transaction hash as hexadecimal |
  | `ledger` | integer | Ledger containing the transaction |
  | `createdAt` | integer | Ledger close time as Unix seconds |
  | `applicationOrder` | integer | Transaction application order within the ledger |
  | `feeBump` | boolean | Whether the transaction uses a fee-bump envelope |
  | `envelopeJson` | object | Transaction envelope in Stellar XDR-JSON format |
  | `resultJson` | object | Transaction result in XDR-JSON format |
  | `resultMetaJson` | object | Transaction result metadata in XDR-JSON format |
  | `events` | object | Transaction event data, when provided |

  Example selected fields (the three XDR-JSON objects and `events` are omitted here):

  ```json
  {
    "status": "FAILED",
    "txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
    "applicationOrder": 1,
    "feeBump": false,
    "ledger": 5193684,
    "createdAt": 1791992007
  }
  ```

  The full envelope uses a tagged union: regular transactions are under `envelopeJson.tx.tx`, v0 transactions under `envelopeJson.tx_v0.tx`, and fee-bump inner transactions under `envelopeJson.tx_fee_bump.tx.inner_tx.tx.tx`. These objects use Stellar's [XDR-JSON format (SEP-0051)](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0051.md), including decimal strings for 64-bit integers. Keep those values as strings or use arbitrary-precision integers; do not convert them to JavaScript `Number` indiscriminately.

  ## Classic payments

  ```json
  {"jsonrpc":"2.0","id":40,"method":"stellar_subscribe","params":["payments",{"to":["GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR"]}]}
  ```

  `accounts` matches a payment's sender or recipient. `from` matches its sender; `to` matches its recipient. Each filter accepts an array of 1–100 G or M StrKeys. Combine filters to require all of their conditions.

  A result contains `ledger` (integer), `txHash` (string), `createdAt` (Unix seconds as an integer), `operationIndex` (zero-based integer), `type`, `from`, `to`, and an `operation` object. The notification example above shows a complete classic payment result. If an operation has its own source account, `from` is that account.

  | `type` | `operation` fields | Amount semantics |
  | --- | --- | --- |
  | `payment` | `destination`, `asset`, `amount` | Amount transferred |
  | `path_payment_strict_receive` | `send_asset`, `send_max`, `destination`, `dest_asset`, `dest_amount`, `path` | Exact destination amount; `send_max` is an upper bound, not the actual amount spent |
  | `path_payment_strict_send` | `send_asset`, `send_amount`, `destination`, `dest_asset`, `dest_min`, `path` | Exact source amount; `dest_min` is a lower bound, not the actual amount received |

  Amounts are decimal strings in Stellar integer units, not human-readable token balances. For classic Stellar assets, divide by `10^7` to obtain standard units: `"1"` is 0.0000001 XLM when the asset is `"native"`. Assets retain their XDR-JSON representation: native XLM is `"native"`; issued assets use `credit_alphanum4` or `credit_alphanum12` objects containing `asset_code` and `issuer`.

  Only successful transactions produce payment notifications. A transaction with multiple payment operations produces multiple records. Account creation, account merge, and Soroban contract transfers do not produce `payments` records. Use `events` for Soroban transfers. Use transaction results if you need the actual executed amounts of path payments.

  ## Filter rules and errors

  Filter fields are ANDed together; addresses within each array are ORed. Addresses are case-sensitive and must be valid StrKeys, including checksums. M addresses match exactly; they do not automatically match the underlying G address. Account filters do not accept C contract addresses.

  Unknown topics or filter fields, empty address arrays, malformed addresses, invalid enum values, and unsupported filter shapes are rejected. For example:

  ```json
  {"jsonrpc":"2.0","id":50,"method":"stellar_subscribe","params":["payments",{"unknown":true}]}
  ```

  returns a JSON-RPC error with code `-32602` (`Invalid params`). Inspect the response's `error` object instead of treating its `result` as a subscription ID. Authentication and access errors can also prevent the WebSocket connection from opening.

  ## Unsubscribe

  Send the ID returned by `stellar_subscribe` over the same connection:

  ```json
  {"jsonrpc":"2.0","id":60,"method":"stellar_unsubscribe","params":["0x0123456789abcdef0123456789abcdef"]}
  ```

  ```json
  {"jsonrpc":"2.0","id":60,"result":true}
  ```

  The result is `true` when the subscription was removed and `false` if the ID was not found on that connection. Already-in-flight notifications may arrive while you unsubscribe. Other subscriptions on the connection remain active. Closing the connection ends all of its subscriptions; IDs cannot be reused after reconnecting.

  ## Recover after a disconnect

  Subscriptions deliver new activity after initialization. The acknowledgement confirms that the subscription was accepted; it does not mean that a record is immediately available or guarantee delivery from the instant you sent the request. There is no `startLedger`, historical replay, resume token, or acknowledgement-based delivery option.

  Delivery is best effort. Disconnections and interruptions can cause gaps; there is no durable exactly-once or at-least-once guarantee. There is no fixed notification latency guarantee. A quiet subscription can mean that no new records match its filters.

  If your application needs complete history:

  1. Persist the last processed ledger and record IDs for each network.
  2. Reconnect with backoff and jitter, then recreate your subscriptions with new IDs.
  3. Buffer new live notifications while you retrieve an overlapping historical range through the corresponding HTTP RPC methods: `getLedgers`, `getEvents`, or `getTransactions`. Recover classic payments from successful transaction operations.
  4. Merge historical and live records and deduplicate by network plus ledger hash/sequence, event `id`, transaction `txHash`, or payment `(txHash, operationIndex)`.

  Historical RPC availability is bounded by retained history. Recover promptly; a gap older than retained history may need another archive source. Do not infer that an empty filtered stream is fully caught up or that the last notification's ledger marks the end of all records for that ledger.

  Close code `1011` can indicate a service interruption or an unrecoverable delivery gap. Reconnect and recover missing records. Close code `1008` can indicate a policy or slow-consumer limit; address the cause before reconnecting. Process notifications promptly, bound your client queues, and reduce subscription scope if your client cannot keep up. Your plan's connection, subscription, and usage limits still apply.

  ## Machine-readable contracts

  Use the [OpenRPC reference](https://multi-network-ws-api-specs.docs.alchemy.com/stellar-websocket/openrpc.json) for subscribe/unsubscribe method contracts and the [message JSON Schema](https://multi-network-ws-api-specs.docs.alchemy.com/stellar-websocket/messages.schema.json) for positional parameters and notification shapes. Result records may include additional fields; clients should ignore unknown fields for forward compatibility.
paramStructure: by-position
x-api-explorer: false
params:
  - name: topic
    required: true
    schema:
      type: string
      enum:
        - newLedgers
        - events
        - transactions
        - payments
    description: Select the result record type.
  - name: filter
    required: false
    description: Optional topic-specific filter. Omit or use {} for all records. Only fields for the selected topic are accepted.
    schema:
      anyOf:
        - type: object
          properties: {}
          required: []
          additionalProperties: false
        - type: object
          properties:
            type:
              type: string
              enum:
                - contract
                - system
            contractIds:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^C[A-Z2-7]{55}$
                description: Valid contract StrKey, including checksum.
            topics:
              type: array
              minItems: 1
              maxItems: 5
              items:
                type: array
                minItems: 1
                maxItems: 4
                items:
                  type: string
                  minLength: 1
                  maxLength: 4096
                  description: A base64 XDR segment or *. Encoded bytes must have a positive length divisible by four.
          required: []
          additionalProperties: false
        - type: object
          properties:
            status:
              type: string
              enum:
                - SUCCESS
                - FAILED
            accounts:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^(G[A-Z2-7]{55}|M[A-Z2-7]{68})$
                description: Valid case-sensitive account StrKey, including checksum. M and G addresses match exactly.
          required: []
          additionalProperties: false
        - type: object
          properties:
            accounts:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^(G[A-Z2-7]{55}|M[A-Z2-7]{68})$
                description: Valid case-sensitive account StrKey, including checksum. M and G addresses match exactly.
            from:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^(G[A-Z2-7]{55}|M[A-Z2-7]{68})$
                description: Valid case-sensitive account StrKey, including checksum. M and G addresses match exactly.
            to:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^(G[A-Z2-7]{55}|M[A-Z2-7]{68})$
                description: Valid case-sensitive account StrKey, including checksum. M and G addresses match exactly.
          required: []
          additionalProperties: false
result:
  name: subscriptionId
  description: Opaque ID scoped to this WebSocket connection. Later records arrive as stellar_subscription notifications.
  schema:
    type: string
    minLength: 1
errors:
  - code: -32602
    message: Invalid params
    data:
      type: string
      description: Details of the invalid topic or filter.
examples:
  - name: Ledgers
    params:
      - name: topic
        value: newLedgers
    result:
      name: subscriptionId
      value: '0x0123456789abcdef0123456789abcdef'
  - name: All events
    params:
      - name: topic
        value: events
    result:
      name: subscriptionId
      value: '0x0123456789abcdef0123456789abcdef'
  - name: Soroban transfers
    params:
      - name: topic
        value: events
      - name: filter
        value:
          type: contract
          topics:
            - - AAAADwAAAAh0cmFuc2Zlcg==
              - '*'
              - '*'
            - - AAAADwAAAAh0cmFuc2Zlcg==
              - '*'
              - '*'
              - '*'
    result:
      name: subscriptionId
      value: '0x0123456789abcdef0123456789abcdef'
  - name: Successful transactions
    params:
      - name: topic
        value: transactions
      - name: filter
        value:
          status: SUCCESS
          accounts:
            - GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST
    result:
      name: subscriptionId
      value: '0x0123456789abcdef0123456789abcdef'
  - name: Classic payments
    params:
      - name: topic
        value: payments
      - name: filter
        value:
          accounts:
            - GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST
    result:
      name: subscriptionId
      value: '0x0123456789abcdef0123456789abcdef'
```
