Skip to content

Commit a5fd250

Browse files
committed
update ws docs
1 parent 6a33060 commit a5fd250

1 file changed

Lines changed: 206 additions & 39 deletions

File tree

docs/sdk-and-tools/rest-api/ws-subscriptions.md

Lines changed: 206 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,32 @@ title: MultiversX API WebSocket
77

88
Starting with the release [v1.17.0](https://github.com/multiversx/mx-api-service/releases/tag/v1.17.0) we introduced WebSocket Subscription functionality.
99

10-
It is useful for subscribing to new events, rather than performing polling (requesting latest events with a given refresh period).
10+
It is useful for subscribing to new events in real-time, rather than performing polling (requesting latest events with a given refresh period).
1111

12-
## Update Frequency and Duplicate Management
13-
Subscribers receive the most recent events at regular intervals defined by the API.
12+
## Update Frequency and Data Modes
1413

15-
This means:
16-
* You are **not** notified only when new events occur.
14+
The WebSocket API supports two primary modes of data consumption: **Latest Data** and **Fresh Data**.
1715

18-
* Instead, you receive an update every round (or according to the configured interval configured in MultiversX API).
16+
### 1. Latest Data (Snapshot & Loop)
17+
Subscribers receive the most recent events for a specific timeframe at regular intervals defined by the API.
18+
* **Behavior:** You receive an update every round (or configured interval).
19+
* **Content:** Each update contains the latest events for the requested buffer (e.g., latest 25 blocks).
20+
* **Duplicates:** Because of the repeating interval, **duplicate events may appear across batches**. It is the user’s responsibility to filter these duplicates.
1921

20-
* Each update contains the latest events for that timeframe.
21-
22-
*For example*:
23-
24-
If you subscribe to the latest 25 blocks, you will receive those 25 blocks every second.
25-
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.
22+
### 2. Fresh Data (Custom Real-time Streams)
23+
Subscribers receive events strictly as they occur on the blockchain, filtered by specific criteria.
24+
* **Behavior:** You are notified immediately when a new event matches your filter.
25+
* **Content:** Data flows in real-time from the moment of subscription.
26+
* **Duplicates:** **No duplicate events are sent.** You receive each item exactly once.
27+
* **Available Streams:** Only `CustomTransactions` and `CustomEvents` are supported in this mode.
2628

2729
## Rest API models compatibility
2830
The MultiversX WebSocket Subscription API provides real-time blockchain data identical in structure to REST API responses:
2931

30-
```
31-
https://api.multiversx.com/<resource>
32-
https://devnet-api.multiversx.com/<resource>
33-
https://testnet-api.multiversx.com/<resource>
32+
```text
33+
https://api.multiversx.com
34+
https://devnet-api.multiversx.com
35+
https://testnet-api.multiversx.com
3436
```
3537

3638
All updates mirror REST responses and include a `<resource>Count` field representing **the total number of existing items at the moment the update was delivered**.
@@ -70,27 +72,23 @@ https://<returned-url>/ws/subscription
7072

7173
## Subscription Events Overview
7274

73-
| Stream | Subscribe Event | Update Event | Mirrors REST Route |
74-
|--------------|-------------------------|--------------------|---------------------|
75-
| Transactions | `subscribeTransactions` | `transactionUpdate`| `/transactions` |
76-
| Blocks | `subscribeBlocks` | `blocksUpdate` | `/blocks` |
77-
| Pool | `subscribePool` | `poolUpdate` | `/pool` |
78-
| Events | `subscribeEvents` | `eventsUpdate` | `/events` |
79-
| Stats | `subscribeStats` | `statsUpdate` | `/stats` |
75+
| Stream Type | Stream Name | Subscribe Event | Update Event | Description |
76+
|---|---|---|---|---|
77+
| **Latest** | Transactions | `subscribeTransactions` | `transactionUpdate` | Recurring latest buffer |
78+
| **Latest** | Blocks | `subscribeBlocks` | `blocksUpdate` | Recurring latest buffer |
79+
| **Latest** | Pool | `subscribePool` | `poolUpdate` | Recurring mempool dump |
80+
| **Latest** | Events | `subscribeEvents` | `eventsUpdate` | Recurring latest events |
81+
| **Latest** | Stats | `subscribeStats` | `statsUpdate` | Recurring chain stats |
82+
| **Fresh** | Custom Txs | `subscribeCustomTransactions` | `customTransactionUpdate` | Real-time filtered Txs |
83+
| **Fresh** | Custom Events| `subscribeCustomEvents` | `customEventUpdate` | Real-time filtered Events |
8084

8185
---
8286

83-
## Subscriptions
87+
## Latest Data Subscriptions
8488

85-
Each stream includes:
89+
**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.
8690

87-
- DTO payload table
88-
- Single code block with connect + payload + subscribe + listen
89-
- Update example
90-
91-
---
92-
93-
### Transactions Subscription
91+
### Transactions (Latest)
9492

9593
#### Payload (DTO)
9694

@@ -155,7 +153,7 @@ main().catch(console.error);
155153

156154
---
157155

158-
### Blocks Subscription
156+
### Blocks (Latest)
159157

160158
#### Payload (DTO)
161159

@@ -210,7 +208,7 @@ main().catch(console.error);
210208

211209
---
212210

213-
### Pool Subscription
211+
### Pool (Latest)
214212

215213
#### Payload (DTO)
216214

@@ -263,7 +261,7 @@ main().catch(console.error);
263261

264262
---
265263

266-
### Events Subscription
264+
### Events (Latest)
267265

268266
#### Payload (DTO)
269267

@@ -319,7 +317,7 @@ main().catch(console.error);
319317

320318
---
321319

322-
### Stats Subscription
320+
### Stats (Latest)
323321

324322
#### Payload (DTO)
325323

@@ -364,6 +362,174 @@ main().catch(console.error);
364362

365363
---
366364

365+
## Fresh Data Subscriptions (Custom Streams)
366+
367+
**Note:** These streams provide real-time data with **no duplicates**. You must provide at least one filter criterion in the payload.
368+
369+
### Custom Transactions (Fresh)
370+
371+
Subscribes to transactions matching specific criteria (Sender, Receiver, or Function) as they happen.
372+
373+
#### Subscribe Event
374+
`subscribeCustomTransactions`
375+
376+
#### Payload (DTO)
377+
378+
| Field | Type | Required | Description |
379+
|---|---|---|---|
380+
| sender | string | NO* | Filter by sender address (bech32) |
381+
| receiver | string | NO* | Filter by receiver address (bech32) |
382+
| function | string | NO* | Filter by smart contract function name |
383+
384+
*\*At least one field must be provided.*
385+
386+
#### Example usage
387+
388+
```js
389+
import { io } from "socket.io-client";
390+
391+
async function main() {
392+
const { url } = await fetch("https://api.multiversx.com/websocket/config")
393+
.then((r) => r.json());
394+
395+
const socket = io(`https://${url}`, { path: "/ws/subscription" });
396+
397+
// Subscribe to all transactions sent by a specific address
398+
const payload = {
399+
sender: "erd1..."
400+
};
401+
402+
socket.emit("subscribeCustomTransactions", payload);
403+
404+
socket.on("customTransactionUpdate", (data) => {
405+
// data.transactions: Transaction[]
406+
// data.timestampMs: number
407+
console.log("New Custom Transaction:", data);
408+
});
409+
}
410+
```
411+
412+
#### Update Example
413+
414+
```json
415+
{
416+
"transactions": [
417+
{
418+
"txHash": "7f172e468e61210805815f33af8500d827aff36df6196cc96783c6d592a5fc76",
419+
"sender": "erd1srdxd75cg7nkaxxy3llz4hmwqqkmcej0jelv8ults8m86g29aj3sxjkc45",
420+
"receiver": "erd19waq9tlhj32ane9duhkv6jusm58ca5ylnthhg9h8fcumtp8srh4qrl3hjj",
421+
"nonce": 211883,
422+
"status": "pending",
423+
"timestamp": 1763718888
424+
}
425+
],
426+
"timestampMs": 1763718888000
427+
}
428+
```
429+
430+
---
431+
432+
### Custom Events (Fresh)
433+
434+
Subscribes to smart contract events matching specific criteria as they happen.
435+
436+
#### Subscribe Event
437+
`subscribeCustomEvents`
438+
439+
#### Payload (DTO)
440+
441+
| Field | Type | Required | Description |
442+
|---|---|---|---|
443+
| address | string | NO* | Filter by the address associated with the event |
444+
| identifier | string | NO* | Filter by event identifier (name) |
445+
| logAddress | string | NO* | Filter by the contract address that emitted the log |
446+
447+
*\*At least one field must be provided.*
448+
449+
#### Example usage
450+
451+
```js
452+
import { io } from "socket.io-client";
453+
454+
async function main() {
455+
const { url } = await fetch("https://api.multiversx.com/websocket/config")
456+
.then((r) => r.json());
457+
458+
const socket = io(`https://${url}`, { path: "/ws/subscription" });
459+
460+
// Subscribe to a specific event identifier
461+
const payload = {
462+
identifier: "swap"
463+
};
464+
465+
socket.emit("subscribeCustomEvents", payload);
466+
467+
socket.on("customEventUpdate", (data) => {
468+
// data.events: Events[]
469+
// data.timestampMs: number
470+
console.log("New Custom Event:", data);
471+
});
472+
}
473+
```
474+
475+
#### Update Example
476+
477+
```json
478+
{
479+
"events": [
480+
{
481+
"txHash": "b5bde891df72e26fb36e7ab3acc14b74044bd9aa82b4852692f5b9a767e0391f-1-0",
482+
"identifier": "signalError",
483+
"address": "erd1jv5m4v3yr0wy6g2jtz2v344sfx572rw6aclum9c6r7rd4ej4l6csjej2wh",
484+
"timestamp": 1763718864,
485+
"topics": [
486+
"9329bab2241bdc4d21525894c8d6b049a9e50ddaee3fcd971a1f86dae655feb1",
487+
"4865616c7468206e6f74206c6f7720656e6f75676820666f72206c69717569646174696f6e2e"
488+
],
489+
"shardID": 1
490+
}
491+
],
492+
"timestampMs": 1763718864000
493+
}
494+
```
495+
496+
---
497+
498+
## Unsubscribing
499+
500+
To stop receiving updates for any stream, you must emit the corresponding unsubscribe event.
501+
502+
**The Rule:**
503+
1. Add the prefix `un` to the subscription event name (e.g., `subscribeTransactions``unsubscribeTransactions`, `subscribeCustomTransactions``unsubscribeCustomTransactions`).
504+
2. Send the **exact same payload** used for the subscription.
505+
506+
### Example: Unsubscribe from Custom Transactions
507+
508+
If you subscribed with:
509+
```js
510+
const payload = { sender: "erd1..." };
511+
socket.emit("subscribeCustomTransactions", payload);
512+
```
513+
514+
You must unsubscribe with:
515+
```js
516+
socket.emit("unsubscribeCustomTransactions", payload);
517+
```
518+
519+
### Example: Unsubscribe from Blocks
520+
If you subscribed with:
521+
```js
522+
const payload = { from: 0, size: 25 };
523+
socket.emit("subscribeBlocks", payload);
524+
```
525+
526+
You must unsubscribe with:
527+
```js
528+
socket.emit("unsubscribeBlocks", payload);
529+
```
530+
531+
---
532+
367533
## Error Handling
368534

369535
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) => {
429595
## Summary
430596

431597
- WebSocket endpoint is dynamically obtained via `/websocket/config`.
432-
- Each stream has its own subscribe and update events.
598+
- **Latest Data Subscriptions:** periodic updates with possible duplicates (Transactions, Blocks, Pool, Events, Stats).
599+
- **Fresh Data Subscriptions:** real-time updates with only new data (CustomTransactions, CustomEvents).
600+
- **Unsubscribing:** Use `un` prefix + same payload.
433601
- Payload DTOs define allowed fields and required/optional rules.
434602
- Update messages mirror REST API and include `<resource>Count` fields.
435-
- `<resource>Count` reflects **total items at the moment of update**.
436-
- Errors are emitted via the standard `error` event, containing the pattern, original data, and error message.
603+
- Errors are emitted via the standard `error` event.
437604
- Uses `socket.io-client`.
438605

439606
This document contains everything required to use MultiversX WebSocket Subscriptions effectively.

0 commit comments

Comments
 (0)