From 076fac028cb8330c89386fe2a51ed59676e58773 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Mon, 8 Dec 2025 14:01:57 +0200 Subject: [PATCH 1/9] add error handling docs --- .../rest-api/ws-subscriptions.md | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 377c3eb4..79b648be 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -364,6 +364,52 @@ main().catch(console.error); --- +## Error Handling + +Unexpected behaviors, such as sending an invalid payload or exceeding the server's subscription limits, will trigger an `error` event emitted by the server. + +You should listen to this event to handle failures gracefully. + +### Payload (DTO) + +The error object contains context about which subscription failed and why. + +| Field | Type | Description | +|---------|--------|------------------------------------------------------------------------------------| +| pattern | string | The subscription topic (event name) that was requested (e.g., `subscribeTransactions`). | +| data | object | The original payload sent by the client that caused the error. | +| error | object | The specific error returned by the server. | + +### Example usage + +```js +import { io } from "socket.io-client"; + +// ... setup socket connection ... + +// Listen for generic errors from the server +socket.on("error", (errorData) => { + console.error("Received error from server:"); + console.dir(errorData, { depth: null }); +}); +``` + +### Error Example + +**Scenario:** The client attempts to open more subscriptions than the server allows (e.g., limit of X). + +```json +{ + "pattern": "subscribeCustomTransactions", + "data": { + "function": "newGame" + }, + "error": "Maximum number of X global subscriptions accepted by server reached!" +} +``` + +--- + ## Summary - WebSocket endpoint is dynamically obtained via `/websocket/config`. @@ -371,6 +417,7 @@ main().catch(console.error); - Payload DTOs define allowed fields and required/optional rules. - Update messages mirror REST API and include `Count` fields. - `Count` reflects **total items at the moment of update**. +- Errors are emitted via the standard `error` event, containing the pattern, original data, and error message. - Uses `socket.io-client`. -This document contains everything required to use MultiversX WebSocket Subscriptions effectively. +This document contains everything required to use MultiversX WebSocket Subscriptions effectively. \ No newline at end of file From 07e2ebf719491d03f3d295d4202cd08380ffb049 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Mon, 8 Dec 2025 14:13:59 +0200 Subject: [PATCH 2/9] error example --- .../rest-api/ws-subscriptions.md | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 79b648be..3f7e1f4b 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -376,7 +376,7 @@ The error object contains context about which subscription failed and why. | Field | Type | Description | |---------|--------|------------------------------------------------------------------------------------| -| pattern | string | The subscription topic (event name) that was requested (e.g., `subscribeTransactions`). | +| pattern | string | The subscription topic (event name) that was requested (e.g., `subscribePool`). | | data | object | The original payload sent by the client that caused the error. | | error | object | The specific error returned by the server. | @@ -400,11 +400,27 @@ socket.on("error", (errorData) => { ```json { - "pattern": "subscribeCustomTransactions", - "data": { - "function": "newGame" + "pattern": "subscribePool", + "data": { + "from": 0, + "size": 25, + "type": "badInput" }, - "error": "Maximum number of X global subscriptions accepted by server reached!" + "error": [ + { + "target": { + "from": 0, + "size": 25, + "type": "badInput" + }, + "value": "badInput", + "property": "type", + "children": [], + "constraints": { + "isEnum": "type must be one of the following values: Transaction, SmartContractResult, Reward" + } + } + ] } ``` From a5fd250271377c4c0f52edf717998812b9343a9e Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Tue, 9 Dec 2025 16:51:54 +0200 Subject: [PATCH 3/9] update ws docs --- .../rest-api/ws-subscriptions.md | 245 +++++++++++++++--- 1 file changed, 206 insertions(+), 39 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 3f7e1f4b..5cb18125 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -7,30 +7,32 @@ title: MultiversX API WebSocket Starting with the release [v1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality. -It is useful for subscribing to new events, rather than performing polling (requesting latest events with a given refresh period). +It is useful for subscribing to new events in real-time, rather than performing polling (requesting latest events with a given refresh period). -## Update Frequency and Duplicate Management -Subscribers receive the most recent events at regular intervals defined by the API. +## Update Frequency and Data Modes -This means: -* You are **not** notified only when new events occur. +The WebSocket API supports two primary modes of data consumption: **Latest Data** and **Fresh Data**. -* Instead, you receive an update every round (or according to the configured interval configured in MultiversX API). +### 1. Latest Data (Snapshot & Loop) +Subscribers receive the most recent events for a specific timeframe at regular intervals defined by the API. +* **Behavior:** You receive an update every round (or configured interval). +* **Content:** Each update contains the latest events for the requested buffer (e.g., latest 25 blocks). +* **Duplicates:** Because of the repeating interval, **duplicate events may appear across batches**. It is the user’s responsibility to filter these duplicates. -* Each update contains the latest events for that timeframe. - -*For example*: - -If you subscribe to the latest 25 blocks, you will receive those 25 blocks every second. -Because of this repeating interval, **duplicate events may appear across batches**, and it is the user’s responsibility to filter or handle those duplicates on their side. +### 2. Fresh Data (Custom Real-time Streams) +Subscribers receive events strictly as they occur on the blockchain, filtered by specific criteria. +* **Behavior:** You are notified immediately when a new event matches your filter. +* **Content:** Data flows in real-time from the moment of subscription. +* **Duplicates:** **No duplicate events are sent.** You receive each item exactly once. +* **Available Streams:** Only `CustomTransactions` and `CustomEvents` are supported in this mode. ## Rest API models compatibility The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses: -``` -https://api.multiversx.com/ -https://devnet-api.multiversx.com/ -https://testnet-api.multiversx.com/ +```text +https://api.multiversx.com +https://devnet-api.multiversx.com +https://testnet-api.multiversx.com ``` All updates mirror REST responses and include a `Count` field representing **the total number of existing items at the moment the update was delivered**. @@ -70,27 +72,23 @@ https:///ws/subscription ## Subscription Events Overview -| Stream | Subscribe Event | Update Event | Mirrors REST Route | -|--------------|-------------------------|--------------------|---------------------| -| Transactions | `subscribeTransactions` | `transactionUpdate`| `/transactions` | -| Blocks | `subscribeBlocks` | `blocksUpdate` | `/blocks` | -| Pool | `subscribePool` | `poolUpdate` | `/pool` | -| Events | `subscribeEvents` | `eventsUpdate` | `/events` | -| Stats | `subscribeStats` | `statsUpdate` | `/stats` | +| Stream Type | Stream Name | Subscribe Event | Update Event | Description | +|---|---|---|---|---| +| **Latest** | Transactions | `subscribeTransactions` | `transactionUpdate` | Recurring latest buffer | +| **Latest** | Blocks | `subscribeBlocks` | `blocksUpdate` | Recurring latest buffer | +| **Latest** | Pool | `subscribePool` | `poolUpdate` | Recurring mempool dump | +| **Latest** | Events | `subscribeEvents` | `eventsUpdate` | Recurring latest events | +| **Latest** | Stats | `subscribeStats` | `statsUpdate` | Recurring chain stats | +| **Fresh** | Custom Txs | `subscribeCustomTransactions` | `customTransactionUpdate` | Real-time filtered Txs | +| **Fresh** | Custom Events| `subscribeCustomEvents` | `customEventUpdate` | Real-time filtered Events | --- -## Subscriptions +## Latest Data Subscriptions -Each stream includes: +**Note:** This mode pushes the latest buffer of data repeatedly. **Duplicate events may appear across batches**, and it is the user’s responsibility to filter or handle those duplicates on their side. -- DTO payload table -- Single code block with connect + payload + subscribe + listen -- Update example - ---- - -### Transactions Subscription +### Transactions (Latest) #### Payload (DTO) @@ -155,7 +153,7 @@ main().catch(console.error); --- -### Blocks Subscription +### Blocks (Latest) #### Payload (DTO) @@ -210,7 +208,7 @@ main().catch(console.error); --- -### Pool Subscription +### Pool (Latest) #### Payload (DTO) @@ -263,7 +261,7 @@ main().catch(console.error); --- -### Events Subscription +### Events (Latest) #### Payload (DTO) @@ -319,7 +317,7 @@ main().catch(console.error); --- -### Stats Subscription +### Stats (Latest) #### Payload (DTO) @@ -364,6 +362,174 @@ main().catch(console.error); --- +## Fresh Data Subscriptions (Custom Streams) + +**Note:** These streams provide real-time data with **no duplicates**. You must provide at least one filter criterion in the payload. + +### Custom Transactions (Fresh) + +Subscribes to transactions matching specific criteria (Sender, Receiver, or Function) as they happen. + +#### Subscribe Event +`subscribeCustomTransactions` + +#### Payload (DTO) + +| Field | Type | Required | Description | +|---|---|---|---| +| sender | string | NO* | Filter by sender address (bech32) | +| receiver | string | NO* | Filter by receiver address (bech32) | +| function | string | NO* | Filter by smart contract function name | + +*\*At least one field must be provided.* + +#### Example usage + +```js +import { io } from "socket.io-client"; + +async function main() { + const { url } = await fetch("https://api.multiversx.com/websocket/config") + .then((r) => r.json()); + + const socket = io(`https://${url}`, { path: "/ws/subscription" }); + + // Subscribe to all transactions sent by a specific address + const payload = { + sender: "erd1..." + }; + + socket.emit("subscribeCustomTransactions", payload); + + socket.on("customTransactionUpdate", (data) => { + // data.transactions: Transaction[] + // data.timestampMs: number + console.log("New Custom Transaction:", data); + }); +} +``` + +#### Update Example + +```json +{ + "transactions": [ + { + "txHash": "7f172e468e61210805815f33af8500d827aff36df6196cc96783c6d592a5fc76", + "sender": "erd1srdxd75cg7nkaxxy3llz4hmwqqkmcej0jelv8ults8m86g29aj3sxjkc45", + "receiver": "erd19waq9tlhj32ane9duhkv6jusm58ca5ylnthhg9h8fcumtp8srh4qrl3hjj", + "nonce": 211883, + "status": "pending", + "timestamp": 1763718888 + } + ], + "timestampMs": 1763718888000 +} +``` + +--- + +### Custom Events (Fresh) + +Subscribes to smart contract events matching specific criteria as they happen. + +#### Subscribe Event +`subscribeCustomEvents` + +#### Payload (DTO) + +| Field | Type | Required | Description | +|---|---|---|---| +| address | string | NO* | Filter by the address associated with the event | +| identifier | string | NO* | Filter by event identifier (name) | +| logAddress | string | NO* | Filter by the contract address that emitted the log | + +*\*At least one field must be provided.* + +#### Example usage + +```js +import { io } from "socket.io-client"; + +async function main() { + const { url } = await fetch("https://api.multiversx.com/websocket/config") + .then((r) => r.json()); + + const socket = io(`https://${url}`, { path: "/ws/subscription" }); + + // Subscribe to a specific event identifier + const payload = { + identifier: "swap" + }; + + socket.emit("subscribeCustomEvents", payload); + + socket.on("customEventUpdate", (data) => { + // data.events: Events[] + // data.timestampMs: number + console.log("New Custom Event:", data); + }); +} +``` + +#### Update Example + +```json +{ + "events": [ + { + "txHash": "b5bde891df72e26fb36e7ab3acc14b74044bd9aa82b4852692f5b9a767e0391f-1-0", + "identifier": "signalError", + "address": "erd1jv5m4v3yr0wy6g2jtz2v344sfx572rw6aclum9c6r7rd4ej4l6csjej2wh", + "timestamp": 1763718864, + "topics": [ + "9329bab2241bdc4d21525894c8d6b049a9e50ddaee3fcd971a1f86dae655feb1", + "4865616c7468206e6f74206c6f7720656e6f75676820666f72206c69717569646174696f6e2e" + ], + "shardID": 1 + } + ], + "timestampMs": 1763718864000 +} +``` + +--- + +## Unsubscribing + +To stop receiving updates for any stream, you must emit the corresponding unsubscribe event. + +**The Rule:** +1. Add the prefix `un` to the subscription event name (e.g., `subscribeTransactions` → `unsubscribeTransactions`, `subscribeCustomTransactions` → `unsubscribeCustomTransactions`). +2. Send the **exact same payload** used for the subscription. + +### Example: Unsubscribe from Custom Transactions + +If you subscribed with: +```js +const payload = { sender: "erd1..." }; +socket.emit("subscribeCustomTransactions", payload); +``` + +You must unsubscribe with: +```js +socket.emit("unsubscribeCustomTransactions", payload); +``` + +### Example: Unsubscribe from Blocks +If you subscribed with: +```js +const payload = { from: 0, size: 25 }; +socket.emit("subscribeBlocks", payload); +``` + +You must unsubscribe with: +```js +socket.emit("unsubscribeBlocks", payload); +``` + +--- + ## Error Handling Unexpected behaviors, such as sending an invalid payload or exceeding the server's subscription limits, will trigger an `error` event emitted by the server. @@ -429,11 +595,12 @@ socket.on("error", (errorData) => { ## Summary - WebSocket endpoint is dynamically obtained via `/websocket/config`. -- Each stream has its own subscribe and update events. +- **Latest Data Subscriptions:** periodic updates with possible duplicates (Transactions, Blocks, Pool, Events, Stats). +- **Fresh Data Subscriptions:** real-time updates with only new data (CustomTransactions, CustomEvents). +- **Unsubscribing:** Use `un` prefix + same payload. - Payload DTOs define allowed fields and required/optional rules. - Update messages mirror REST API and include `Count` fields. -- `Count` reflects **total items at the moment of update**. -- Errors are emitted via the standard `error` event, containing the pattern, original data, and error message. +- Errors are emitted via the standard `error` event. - Uses `socket.io-client`. This document contains everything required to use MultiversX WebSocket Subscriptions effectively. \ No newline at end of file From 8581ad97a405d6e9976b506fb93550378117d5a8 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 10 Dec 2025 09:49:02 +0200 Subject: [PATCH 4/9] fixes after review --- .../rest-api/ws-subscriptions.md | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 5cb18125..864f45e6 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -5,21 +5,22 @@ title: MultiversX API WebSocket ## MultiversX WebSocket Subscription API -Starting with the release [v1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality. +Starting with the release [v.1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality. It is useful for subscribing to new events in real-time, rather than performing polling (requesting latest events with a given refresh period). -## Update Frequency and Data Modes +## Update Frequency and Stream Modes -The WebSocket API supports two primary modes of data consumption: **Latest Data** and **Fresh Data**. +The WebSocket API supports two primary modes of data consumption: **Pulse Stream** and **Filtered Stream**. -### 1. Latest Data (Snapshot & Loop) +### 1. Pulse Stream (Snapshot & Loop) Subscribers receive the most recent events for a specific timeframe at regular intervals defined by the API. * **Behavior:** You receive an update every round (or configured interval). * **Content:** Each update contains the latest events for the requested buffer (e.g., latest 25 blocks). * **Duplicates:** Because of the repeating interval, **duplicate events may appear across batches**. It is the user’s responsibility to filter these duplicates. +* **Available Streams:** Transactions, Blocks, Pool, Events, Stats. -### 2. Fresh Data (Custom Real-time Streams) +### 2. Filtered Stream (Custom Real-time Streams) Subscribers receive events strictly as they occur on the blockchain, filtered by specific criteria. * **Behavior:** You are notified immediately when a new event matches your filter. * **Content:** Data flows in real-time from the moment of subscription. @@ -30,9 +31,9 @@ Subscribers receive events strictly as they occur on the blockchain, filtered by The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses: ```text -https://api.multiversx.com -https://devnet-api.multiversx.com -https://testnet-api.multiversx.com +[https://api.multiversx.com](https://api.multiversx.com) +[https://devnet-api.multiversx.com](https://devnet-api.multiversx.com) +[https://testnet-api.multiversx.com](https://testnet-api.multiversx.com) ``` All updates mirror REST responses and include a `Count` field representing **the total number of existing items at the moment the update was delivered**. @@ -74,21 +75,21 @@ https:///ws/subscription | Stream Type | Stream Name | Subscribe Event | Update Event | Description | |---|---|---|---|---| -| **Latest** | Transactions | `subscribeTransactions` | `transactionUpdate` | Recurring latest buffer | -| **Latest** | Blocks | `subscribeBlocks` | `blocksUpdate` | Recurring latest buffer | -| **Latest** | Pool | `subscribePool` | `poolUpdate` | Recurring mempool dump | -| **Latest** | Events | `subscribeEvents` | `eventsUpdate` | Recurring latest events | -| **Latest** | Stats | `subscribeStats` | `statsUpdate` | Recurring chain stats | -| **Fresh** | Custom Txs | `subscribeCustomTransactions` | `customTransactionUpdate` | Real-time filtered Txs | -| **Fresh** | Custom Events| `subscribeCustomEvents` | `customEventUpdate` | Real-time filtered Events | +| **Pulse** | Transactions | `subscribeTransactions` | `transactionUpdate` | Recurring latest buffer | +| **Pulse** | Blocks | `subscribeBlocks` | `blocksUpdate` | Recurring latest buffer | +| **Pulse** | Pool | `subscribePool` | `poolUpdate` | Recurring mempool dump | +| **Pulse** | Events | `subscribeEvents` | `eventsUpdate` | Recurring latest events | +| **Pulse** | Stats | `subscribeStats` | `statsUpdate` | Recurring chain stats | +| **Filtered** | Custom Txs | `subscribeCustomTransactions` | `customTransactionUpdate` | Real-time filtered Txs | +| **Filtered** | Custom Events| `subscribeCustomEvents` | `customEventUpdate` | Real-time filtered Events | --- -## Latest Data Subscriptions +## Pulse Stream Subscriptions **Note:** This mode pushes the latest buffer of data repeatedly. **Duplicate events may appear across batches**, and it is the user’s responsibility to filter or handle those duplicates on their side. -### Transactions (Latest) +### Transactions (Pulse) #### Payload (DTO) @@ -153,7 +154,7 @@ main().catch(console.error); --- -### Blocks (Latest) +### Blocks (Pulse) #### Payload (DTO) @@ -208,7 +209,7 @@ main().catch(console.error); --- -### Pool (Latest) +### Pool (Pulse) #### Payload (DTO) @@ -261,7 +262,7 @@ main().catch(console.error); --- -### Events (Latest) +### Events (Pulse) #### Payload (DTO) @@ -317,7 +318,7 @@ main().catch(console.error); --- -### Stats (Latest) +### Stats (Pulse) #### Payload (DTO) @@ -362,11 +363,11 @@ main().catch(console.error); --- -## Fresh Data Subscriptions (Custom Streams) +## Filtered Stream Subscriptions (Custom Streams) -**Note:** These streams provide real-time data with **no duplicates**. You must provide at least one filter criterion in the payload. +**Note:** These streams provide real-time data (with a small delay after the actions are committed on-chain) for specific criteria. You must provide at least one filter criteria when subscribing. -### Custom Transactions (Fresh) +### Custom Transactions (Filtered) Subscribes to transactions matching specific criteria (Sender, Receiver, or Function) as they happen. @@ -389,7 +390,7 @@ Subscribes to transactions matching specific criteria (Sender, Receiver, or Func import { io } from "socket.io-client"; async function main() { - const { url } = await fetch("https://api.multiversx.com/websocket/config") + const { url } = await fetch("[https://api.multiversx.com/websocket/config](https://api.multiversx.com/websocket/config)") .then((r) => r.json()); const socket = io(`https://${url}`, { path: "/ws/subscription" }); @@ -429,7 +430,7 @@ async function main() { --- -### Custom Events (Fresh) +### Custom Events (Filtered) Subscribes to smart contract events matching specific criteria as they happen. @@ -595,8 +596,8 @@ socket.on("error", (errorData) => { ## Summary - WebSocket endpoint is dynamically obtained via `/websocket/config`. -- **Latest Data Subscriptions:** periodic updates with possible duplicates (Transactions, Blocks, Pool, Events, Stats). -- **Fresh Data Subscriptions:** real-time updates with only new data (CustomTransactions, CustomEvents). +- **Pulse Stream Subscriptions:** periodic updates with possible duplicates (Transactions, Blocks, Pool, Events, Stats). +- **Filtered Stream Subscriptions:** real-time updates with only new data (CustomTransactions, CustomEvents). - **Unsubscribing:** Use `un` prefix + same payload. - Payload DTOs define allowed fields and required/optional rules. - Update messages mirror REST API and include `Count` fields. From 1e4064e0141b6bd87e5da6858dcbfc19e10efb7e Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 10 Dec 2025 09:50:17 +0200 Subject: [PATCH 5/9] fix --- docs/sdk-and-tools/rest-api/ws-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 864f45e6..26da81c7 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -25,7 +25,7 @@ Subscribers receive events strictly as they occur on the blockchain, filtered by * **Behavior:** You are notified immediately when a new event matches your filter. * **Content:** Data flows in real-time from the moment of subscription. * **Duplicates:** **No duplicate events are sent.** You receive each item exactly once. -* **Available Streams:** Only `CustomTransactions` and `CustomEvents` are supported in this mode. +* **Available Streams:** Only `Transactions` and `Events` are supported in this mode. ## Rest API models compatibility The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses: From 0cf9ea2803f885a932b57da9ac2796c29a43dbd0 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 10 Dec 2025 09:53:11 +0200 Subject: [PATCH 6/9] fix --- docs/sdk-and-tools/rest-api/ws-subscriptions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 26da81c7..2803c4d1 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -30,11 +30,11 @@ Subscribers receive events strictly as they occur on the blockchain, filtered by ## Rest API models compatibility The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses: -```text -[https://api.multiversx.com](https://api.multiversx.com) -[https://devnet-api.multiversx.com](https://devnet-api.multiversx.com) -[https://testnet-api.multiversx.com](https://testnet-api.multiversx.com) -``` + +[https://api.multiversx.com](https://api.multiversx.com) +[https://devnet-api.multiversx.com](https://devnet-api.multiversx.com) +[https://testnet-api.multiversx.com](https://testnet-api.multiversx.com) + All updates mirror REST responses and include a `Count` field representing **the total number of existing items at the moment the update was delivered**. From 5fa3a7aa183a066d47b021e1c623e12e337b6b97 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 10 Dec 2025 09:57:50 +0200 Subject: [PATCH 7/9] fixes --- docs/sdk-and-tools/rest-api/ws-subscriptions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index 2803c4d1..c123e9b6 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -5,7 +5,7 @@ title: MultiversX API WebSocket ## MultiversX WebSocket Subscription API -Starting with the release [v.1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality. +Starting with the release [v1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality. It is useful for subscribing to new events in real-time, rather than performing polling (requesting latest events with a given refresh period). @@ -390,7 +390,7 @@ Subscribes to transactions matching specific criteria (Sender, Receiver, or Func import { io } from "socket.io-client"; async function main() { - const { url } = await fetch("[https://api.multiversx.com/websocket/config](https://api.multiversx.com/websocket/config)") + const { url } = await fetch("https://api.multiversx.com/websocket/config") .then((r) => r.json()); const socket = io(`https://${url}`, { path: "/ws/subscription" }); From 7768d1781dfe5f3b02136bad743110f21333d839 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 17 Dec 2025 13:45:53 +0200 Subject: [PATCH 8/9] add filtered streams --- .../rest-api/ws-subscriptions.md | 127 +++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index c123e9b6..c7b58d91 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -25,7 +25,7 @@ Subscribers receive events strictly as they occur on the blockchain, filtered by * **Behavior:** You are notified immediately when a new event matches your filter. * **Content:** Data flows in real-time from the moment of subscription. * **Duplicates:** **No duplicate events are sent.** You receive each item exactly once. -* **Available Streams:** Only `Transactions` and `Events` are supported in this mode. +* **Available Streams:** `Transactions`, `Events`, and `Transfers` are supported in this mode. ## Rest API models compatibility The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses: @@ -81,6 +81,7 @@ https:///ws/subscription | **Pulse** | Events | `subscribeEvents` | `eventsUpdate` | Recurring latest events | | **Pulse** | Stats | `subscribeStats` | `statsUpdate` | Recurring chain stats | | **Filtered** | Custom Txs | `subscribeCustomTransactions` | `customTransactionUpdate` | Real-time filtered Txs | +| **Filtered** | Custom Transfers | `subscribeCustomTransfers` | `customTransferUpdate` | Real-time filtered Transfers | | **Filtered** | Custom Events| `subscribeCustomEvents` | `customEventUpdate` | Real-time filtered Events | --- @@ -430,6 +431,116 @@ async function main() { --- +### Custom Transfers (Filtered) + +Subscribes to value transfers (EGLD, ESDT, NFT) matching specific criteria. This stream is optimized for tracking movement of assets and supports flexible filtering like generic address matching and token identifiers. + +#### Subscribe Event +`subscribeCustomTransfers` + +#### Payload (DTO) + +| Field | Type | Required | Description | +|---|---|---|---| +| sender | string | NO* | Filter by sender address (bech32) | +| receiver | string | NO* | Filter by receiver address (bech32) | +| relayer | string | NO* | Filter by the relayer address (for meta-transactions) | +| function | string | NO* | Filter by smart contract function name | +| token | string | NO* | Filter by Token Identifier (e.g., `USDC-c76f1f` or `EGLD` for native egld) | +| address | string | NO** | **Universal Filter:** Matches if the address is the Sender **OR** Receiver **OR** Relayer. | + +*\*At least one field from the list above must be provided.* +*\*\*The `address` field cannot be combined with `sender`, `receiver`, or `relayer` in the same payload.* + +#### Example usage + +**Scenario: Listen for specific Token transfers (e.g., USDC)** +Useful for tracking volume on a specific token. + +```js +import { io } from "socket.io-client"; + +async function main() { + const { url } = await fetch("https://api.multiversx.com/websocket/config") + .then((r) => r.json()); + + const socket = io(`https://${url}`, { path: "/ws/subscription" }); + + const payload = { + token: "USDC-c76f1f" + }; + + socket.emit("subscribeCustomTransfers", payload); + + socket.on("customTransferUpdate", (data) => { + // data.transfers: Transaction[] (filtered) + // data.timestampMs: number + console.log("New USDC Transfer:", data); + }); +} +``` + +**Scenario: Listen for ANY activity related to an address** +Using the `address` field is a shorthand to avoid creating 3 separate subscriptions (sender, receiver, relayer). + +```js + const payload = { + address: "erd1..." // Will capture incoming, outgoing, and relayed transfers + }; + + socket.emit("subscribeCustomTransfers", payload); +``` + +#### Update Example + +```json +{ + "transfers": [ + { + "txHash": "cb5d0644ef40943db8035ff50913c4a974a469c2479a73c3cd3ab8de9027be0f", + "receiver": "erd1qqqqqqqqqqqqqpgqxn6hj5m9x33zuq0xynjkusd8tsz3u6a94fvsn2m2ry", + "receiverShard": 1, + "sender": "erd1qqqqqqqqqqqqqpgqcc69ts8409p3h77q5chsaqz57y6hugvc4fvs64k74v", + "senderAssets": { + ... + }, + "senderShard": 1, + "status": "success", + "value": "0", + "timestamp": 1765963650, + "function": "exchange", + "action": { + "category": "esdtNft", + "name": "transfer", + "description": "Transfer", + "arguments": { + "transfers": [ + { + "type": "FungibleESDT", + "ticker": "USDC", + "svgUrl": "https://tools.multiversx.com/assets-cdn/tokens/USDC-c76f1f/icon.svg", + "token": "USDC-c76f1f", + "decimals": 6, + "value": "4087442" + } + ], + "receiver": "erd1qqqqqqqqqqqqqpgqxn6hj5m9x33zuq0xynjkusd8tsz3u6a94fvsn2m2ry", + "functionName": "exchange", + "functionArgs": [ + "01" + ] + } + }, + "type": "SmartContractResult", + "originalTxHash": "46bb841a087c5ce95ca28d0e95c860c661b4d32514bb2970137536036bf591b3" + }, + ], + "timestampMs": 1763718888000 +} +``` + +--- + ### Custom Events (Filtered) Subscribes to smart contract events matching specific criteria as they happen. @@ -517,6 +628,18 @@ You must unsubscribe with: socket.emit("unsubscribeCustomTransactions", payload); ``` +### Example: Unsubscribe from Custom Transfers +If you subscribed with: +```js +const payload = { token: "USDC-c76f1f" }; +socket.emit("subscribeCustomTransfers", payload); +``` + +You must unsubscribe with: +```js +socket.emit("unsubscribeCustomTransfers", payload); +``` + ### Example: Unsubscribe from Blocks If you subscribed with: ```js @@ -597,7 +720,7 @@ socket.on("error", (errorData) => { - WebSocket endpoint is dynamically obtained via `/websocket/config`. - **Pulse Stream Subscriptions:** periodic updates with possible duplicates (Transactions, Blocks, Pool, Events, Stats). -- **Filtered Stream Subscriptions:** real-time updates with only new data (CustomTransactions, CustomEvents). +- **Filtered Stream Subscriptions:** real-time updates with only new data (CustomTransactions, **CustomTransfers**, CustomEvents). - **Unsubscribing:** Use `un` prefix + same payload. - Payload DTOs define allowed fields and required/optional rules. - Update messages mirror REST API and include `Count` fields. From 53489b3af191167d820fd8ea1011364bb5f8b304 Mon Sep 17 00:00:00 2001 From: GuticaStefan Date: Wed, 17 Dec 2025 13:59:55 +0200 Subject: [PATCH 9/9] fix --- docs/sdk-and-tools/rest-api/ws-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/rest-api/ws-subscriptions.md b/docs/sdk-and-tools/rest-api/ws-subscriptions.md index c7b58d91..9b68c691 100644 --- a/docs/sdk-and-tools/rest-api/ws-subscriptions.md +++ b/docs/sdk-and-tools/rest-api/ws-subscriptions.md @@ -433,7 +433,7 @@ async function main() { ### Custom Transfers (Filtered) -Subscribes to value transfers (EGLD, ESDT, NFT) matching specific criteria. This stream is optimized for tracking movement of assets and supports flexible filtering like generic address matching and token identifiers. +Subscribes to the complete stream of actions associated with a specific Address or Token. This includes not only standard transactions but all blockchain operations: Transactions, Smart Contract Results (SCRs), Rewards, ... . It is the preferred mode for tracking the full real-time activity of an account or the global movement of a specific token. #### Subscribe Event `subscribeCustomTransfers`