From 2a63df5f944743c41a4471537c468882696c0f77 Mon Sep 17 00:00:00 2001 From: Brad Bayliss Date: Tue, 20 Oct 2026 09:16:34 +0100 Subject: [PATCH] Document New Event Inbox Feature --- .../04-using-the-enjin-platform.md | 2 + .../01-queries/09-event-inbox-queries.md | 128 ++++++++++++++++++ .../02-mutations/09-event-inbox-mutations.md | 88 ++++++++++++ docs/03-api-reference/03-websocket-events.md | 65 ++++++++- .../03-working-with-events.md | 61 ++++++++- docs/05-enjin-platform/05-enjin-platform.md | 2 +- 6 files changed, 334 insertions(+), 12 deletions(-) create mode 100644 docs/03-api-reference/01-queries/09-event-inbox-queries.md create mode 100644 docs/03-api-reference/02-mutations/09-event-inbox-mutations.md diff --git a/docs/01-getting-started/04-using-the-enjin-platform.md b/docs/01-getting-started/04-using-the-enjin-platform.md index 513a109..498548d 100644 --- a/docs/01-getting-started/04-using-the-enjin-platform.md +++ b/docs/01-getting-started/04-using-the-enjin-platform.md @@ -323,6 +323,8 @@ There are two ways to receive the transaction status and information: You can also receive real-time, push-based notifications over WebSocket instead of polling — see [WebSocket Events](/03-api-reference/03-websocket-events.md). +For on-chain notifications such as transfers, [Event Inbox](/05-enjin-platform/03-working-with-events.md#event-inbox) lets you poll when convenient or recover notifications missed during a WebSocket outage. Process and acknowledge pending deliveries within the 14-day retention window. + ### Receive Transaction Information Using the Platform User Interface To check the transaction status, head over to the [Transactions](https://platform.enjin.io/transactions) page. Each row shows the transaction's `UUID`, `Action` (e.g. `MultiTokens.create_collection`), `State`, and `Extrinsic Hash` once it has been broadcast. diff --git a/docs/03-api-reference/01-queries/09-event-inbox-queries.md b/docs/03-api-reference/01-queries/09-event-inbox-queries.md new file mode 100644 index 0000000..e5ae961 --- /dev/null +++ b/docs/03-api-reference/01-queries/09-event-inbox-queries.md @@ -0,0 +1,128 @@ +--- +title: "Event Inbox" +slug: "event-inbox" +description: "Retrieve pending Enjin Platform notifications with GetPendingEvents for polling or recovery after a WebSocket disconnect." +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::tip GraphQL Endpoint +`https://platform.enjin.io/graphql` +::: + +Event Inbox stores eligible on-chain notifications for your account so you can poll at a convenient time or recover notifications missed over WebSocket. Authenticate with your Enjin Platform API token. Notifications are captured automatically; no Pusher connection or opt-in is required. + +See [Working with Events](/05-enjin-platform/03-working-with-events.md#event-inbox) for the processing workflow and [On-chain notifications](/03-api-reference/03-websocket-events.md#on-chain-notifications) for the supported events. + +## GetPendingEvents + +Returns the oldest unacknowledged notifications for the authenticated user on the requested network and chain, up to `limit`. + +```graphql +GetPendingEvents( + network: Network! = ENJIN + chain: Chain! = MATRIX + limit: Int! = 100 +): [UnprocessedEvent!]! +``` + +| Argument | Type | Default | Description | +| --- | --- | --- | --- | +| `network` | `Network!` | `ENJIN` | Network to retrieve notifications from: `ENJIN` or `CANARY`. | +| `chain` | `Chain!` | `MATRIX` | Chain to retrieve notifications from: `MATRIX` or `RELAY`. | +| `limit` | `Int!` | `100` | Maximum number of notifications to return, from `1` to `100`. | + +All three arguments can be omitted to use their defaults. Poll each network and chain you need separately. + +### Response fields + +The result is a list of `UnprocessedEvent` objects, or `[]` if there are no pending notifications. + +| Field | Type | Description | +| --- | --- | --- | +| `uuid` | `ID!` | Delivery UUID used for deduplication and acknowledgment. The same UUID is included in the corresponding Pusher notification. | +| `event` | `String!` | Exact Pusher event name, for example `BalancesTransfer`. | +| `payload` | `JSON!` | Notification payload as a JSON object. The delivery UUID is returned separately in `uuid`; Pusher adds it to the payload as `uuid`. | + +### Example + +This example retrieves notifications on Enjin Relaychain. + + + + +```graphql +query GetPendingEvents( + $network: Network! = ENJIN + $chain: Chain! = MATRIX + $limit: Int! = 100 +) { + GetPendingEvents(network: $network, chain: $chain, limit: $limit) { + uuid + event + payload + } +} +``` + +**Variables:** + +```json +{ + "network": "ENJIN", + "chain": "RELAY", + "limit": 100 +} +``` + + + + +```bash +curl --location 'https://platform.enjin.io/graphql' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer ' \ +-d '{"query":"query GetPendingEvents($network: Network! = ENJIN, $chain: Chain! = MATRIX, $limit: Int! = 100) { GetPendingEvents(network: $network, chain: $chain, limit: $limit) { uuid event payload } }","variables":{"network":"ENJIN","chain":"RELAY","limit":100}}' +``` + + + + +```json +{ + "data": { + "GetPendingEvents": [ + { + "uuid": "af078b53-701a-40a9-b0e6-2dc39f3fc47d", + "event": "BalancesTransfer", + "payload": { + "chain": "RELAY", + "network": "ENJIN", + "publicKey": "0xa7208d10c6622f3f7eca1551de8355fde9de577dbb308d38994ace561738a51f" + } + }, + { + "uuid": "7a862834-6f50-45bc-8bf0-c8231d00ec78", + "event": "BalancesTransfer", + "payload": { + "chain": "RELAY", + "network": "ENJIN", + "publicKey": "0xa7208d10c6622f3f7eca1551de8355fde9de577dbb308d38994ace561738a51f" + } + } + ] + } +} +``` + + + + +These two notifications have identical payloads but different UUIDs. Treat them as separate deliveries; deduplicate by `uuid`, not by payload or event name. + +### Retrieving the next batch + +Reading notifications does not acknowledge them. There are no page, offset, or cursor arguments: repeated queries return the oldest pending notifications until they are acknowledged or expire. After successfully processing a batch, call [`AcknowledgeEvents`](/03-api-reference/02-mutations/09-event-inbox-mutations.md#acknowledgeevents) with its UUIDs, then query again for the next batch. + +Notifications are retained for **14 days** from dispatch. Unacknowledged notifications expire and are automatically purged after that period. Event Inbox does not backfill historical events. diff --git a/docs/03-api-reference/02-mutations/09-event-inbox-mutations.md b/docs/03-api-reference/02-mutations/09-event-inbox-mutations.md new file mode 100644 index 0000000..cf6f426 --- /dev/null +++ b/docs/03-api-reference/02-mutations/09-event-inbox-mutations.md @@ -0,0 +1,88 @@ +--- +title: "Event Inbox" +slug: "event-inbox" +description: "Acknowledge processed Enjin Platform notifications with AcknowledgeEvents." +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::tip GraphQL Endpoint +`https://platform.enjin.io/graphql` +::: + +Use Event Inbox acknowledgments after successfully processing notifications received through [`GetPendingEvents`](/03-api-reference/01-queries/09-event-inbox-queries.md#getpendingevents) or the corresponding Pusher subscription. Authenticate with your Enjin Platform API token. + +## AcknowledgeEvents + +Marks the authenticated user's pending notifications as acknowledged, removing them from subsequent `GetPendingEvents` results. + +```graphql +AcknowledgeEvents(uuids: [ID!]!): Int! +``` + +| Argument | Type | Description | +| --- | --- | --- | +| `uuids` | `[ID!]!` | Between `1` and `100` distinct, valid delivery UUIDs. Use the top-level `uuid` from an inbox entry or the `uuid` in the corresponding Pusher notification's payload. | + +This mutation is **network- and chain-agnostic**. It accepts UUIDs from multiple networks and chains in one request and takes no `network` or `chain` argument. + +### Example + + + + +```graphql +mutation AcknowledgeEvents($uuids: [ID!]!) { + AcknowledgeEvents(uuids: $uuids) +} +``` + +**Variables:** + +```json +{ + "uuids": [ + "af078b53-701a-40a9-b0e6-2dc39f3fc47d", + "7a862834-6f50-45bc-8bf0-c8231d00ec78" + ] +} +``` + + + + +```bash +curl --location 'https://platform.enjin.io/graphql' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer ' \ +-d '{"query":"mutation AcknowledgeEvents($uuids: [ID!]!) { AcknowledgeEvents(uuids: $uuids) }","variables":{"uuids":["af078b53-701a-40a9-b0e6-2dc39f3fc47d","7a862834-6f50-45bc-8bf0-c8231d00ec78"]}}' +``` + + + + +```json +{ + "data": { + "AcknowledgeEvents": 2 + } +} +``` + + + + +### Return value and retries + +The integer result counts only notifications that changed from unacknowledged to acknowledged in this request. Repeating the example after both notifications have been acknowledged returns `0`. + +Unknown, expired, already acknowledged UUIDs, and UUIDs belonging to another user do not contribute to the count. A count smaller than the number submitted is therefore possible. Invalid UUIDs, duplicate UUIDs within one request, an empty list, or more than 100 UUIDs fail validation. + +Acknowledgment is safe to retry if a request fails or its response is lost. Deduplicate UUIDs within each batch, and send only those whose processing has completed successfully. If only part of a batch succeeds, acknowledge that subset and leave failed notifications pending for retry. + +:::tip Acknowledge to advance +`GetPendingEvents` has no pagination. Acknowledge processed notifications so the next query can return later pending notifications. Receiving a WebSocket message or reading an inbox entry does not acknowledge it automatically. +::: + +See [Working with Events](/05-enjin-platform/03-working-with-events.md#event-inbox) for polling, reconnect recovery, and deduplication guidance. diff --git a/docs/03-api-reference/03-websocket-events.md b/docs/03-api-reference/03-websocket-events.md index 874acee..9b2855e 100644 --- a/docs/03-api-reference/03-websocket-events.md +++ b/docs/03-api-reference/03-websocket-events.md @@ -1,16 +1,18 @@ --- title: "WebSocket Events" slug: "websocket-events" -description: "Subscribe to real-time Enjin Platform events over WebSocket using the Pusher protocol." +description: "Subscribe to Enjin Platform notifications over Pusher, and use Event Inbox to recover missed on-chain notifications." --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -After you submit a transaction, there are two ways to find out what happened to it. You can keep asking the API ("is it finalized yet?") — that's **polling**, and it works, but it wastes requests and adds delay. Or the platform can tell *you* the moment something happens — a transaction changing state, a managed wallet being created, a user linking their wallet. That's what this page covers. +The Enjin Platform can notify your application when something changes — an on-chain transfer, a transaction changing state, a managed wallet being created, or a user linking their wallet. This page covers receiving those notifications in real time. These notifications are delivered over a **WebSocket** — a connection your application opens once and keeps open, so the server can send it messages at any time. The Enjin Platform sends them through [Pusher](https://pusher.com/), a widely-used message-delivery service with a [documented protocol](https://pusher.com/docs/channels/library_auth_reference/pusher-websockets-protocol/) and official client libraries for most languages. +For eligible [on-chain notifications](#on-chain-notifications), **Event Inbox** provides a second delivery option: poll [`GetPendingEvents`](/03-api-reference/01-queries/09-event-inbox-queries.md#getpendingevents) at a time that suits your application, or use it alongside WebSocket to recover missed notifications. The event names and notification data match across both transports, and a shared delivery `uuid` lets you deduplicate them. See [Working with Events](/05-enjin-platform/03-working-with-events.md#event-inbox) for the complete workflow. + :::info You don't need a Pusher account Pusher is just the delivery mechanism. You connect using Enjin's public app key below and authenticate with your regular Enjin Platform API token — there's nothing to sign up for or configure on Pusher's side. ::: @@ -25,6 +27,8 @@ Your account's events are broadcast on a **channel** — a named stream you subs The `auth` signature is tied to the `socket_id` it was issued for, and every new connection gets a new `socket_id` — so if the connection drops and you reconnect, you repeat steps 2 and 3. +Reconnecting restores live delivery. To recover on-chain notifications missed during the outage, drain Event Inbox after the subscription succeeds and acknowledge each successfully processed delivery. Notifications remain available for 14 days from dispatch; receiving them over WebSocket does not automatically acknowledge them. + If that sounds like a lot of bookkeeping: it mostly isn't yours to do. A first-party Pusher library (like [pusher-js](https://github.com/pusher/pusher-js)) handles the connection, the re-authentication on reconnect, and the keepalive for you — you just tell it how to call the mutation. That's the recommended setup, and the quick start below is exactly that. Only if no Pusher library exists for your stack do you need to speak the protocol yourself — see [Subscribing over a raw WebSocket](#subscribing-over-a-raw-websocket). ## Quick start @@ -102,7 +106,7 @@ ManagedWalletCreated { } ``` -The full list of events you can receive is in the [Platform events](#platform-events) reference below. +See [On-chain notifications](#on-chain-notifications) for events also available through Event Inbox, and [Platform events](#platform-events) for lifecycle notifications. :::warning Keep your API token server-side `AuthenticatePusherSocket` requires your platform API token, so the socket should be opened from your backend (or the mutation proxied through it) — never ship the token to a browser or game client. @@ -244,7 +248,7 @@ Response: ## Subscribing over a raw WebSocket {#subscribing-over-a-raw-websocket} -If a first-party Pusher library isn't available for your stack, you can speak the protocol directly — it's the same three steps from [How it works](#how-it-works), done by hand. Note that the `data` field of every Pusher protocol frame is a JSON-*encoded string*, so it needs a second parse. +If a first-party Pusher library isn't available for your stack, you can speak the protocol directly — it's the same three steps from [How it works](#how-it-works), done by hand. In the incoming messages shown below, `data` is a JSON-*encoded string*, so it needs a second parse. 1. Connect to `wss://ws-us2.pusher.com/app/8ab7ab8c519e8f59b635?protocol=7`. The server sends: @@ -271,16 +275,63 @@ If a first-party Pusher library isn't available for your stack, you can speak th ```json { - "event": "TransactionStateChanged", - "channel": "private-user.b1acd213-2b27-406e-a21b-02307b52eca0", - "data": "{\"uuid\":\"0af09287-30ff-43c9-a13d-75ad1ea714c3\",\"state\":\"FINALIZED\"}" + "event": "BalancesTransfer", + "data": "{\"network\":\"ENJIN\",\"chain\":\"RELAY\",\"uuid\":\"f2342ed4-3504-4f81-b4b5-315635283652\",\"publicKey\":\"0xa7208d10c6622f3f7eca1551de8355fde9de577dbb308d38994ace561738a51f\"}", + "channel": "private-user.b1acd213-2b27-406e-a21b-02307b52eca0" } ``` Your client is also responsible for keepalive: if the connection is quiet for longer than the `activity_timeout` reported in `pusher:connection_established`, send `{"event":"pusher:ping","data":{}}` and expect a `pusher:pong` back (the server may likewise ping you). And remember that a reconnect is a new socket — repeat the authenticate-and-subscribe handshake every time. +## On-chain notifications {#on-chain-notifications} + +These notifications are delivered through both Pusher and Event Inbox for eligible wallets and collections associated with your account. Each delivery includes a UUID that is shared across the two transports. Use it to deduplicate processing and pass it to [`AcknowledgeEvents`](/03-api-reference/02-mutations/09-event-inbox-mutations.md#acknowledgeevents) after processing succeeds. + +All Pusher payloads in this group contain `network`, `chain`, `uuid`, and `publicKey`. Addresses are public keys (`0x...`). Additional fields depend on the event: + +| Event | Change | Additional payload fields | +| --- | --- | --- | +| `BalancesTransfer` | ENJ is transferred | None | +| `MultiTokensTransferred` | Tokens are transferred | `id`: full token ID (`collectionId-tokenId`) | +| `MultiTokensMinted` | Tokens are minted | `id`: full token ID | +| `MultiTokensBurned` | Tokens are burned | `id`: full token ID | +| `MarketplaceListingCreated` | A marketplace listing is created | `tokenId`: full token ID; `listingId`: listing ID | +| `MarketplaceListingFilled` | A marketplace listing is filled | `tokenId`: full token ID; `listingId`: listing ID | +| `MarketplaceListingCancelled` | A marketplace listing is cancelled or removed under its minimum | `tokenId`: full token ID; `listingId`: listing ID | +| `NominationPoolsBond` | An account bonds into a nomination pool | `id`: pool ID | +| `NominationPoolsUnbond` | An account initiates unbonding from a nomination pool | `id`: pool ID | + +For example, a Pusher client library delivers this decoded `BalancesTransfer` payload: + +```json +{ + "network": "ENJIN", + "chain": "RELAY", + "uuid": "f2342ed4-3504-4f81-b4b5-315635283652", + "publicKey": "0xa7208d10c6622f3f7eca1551de8355fde9de577dbb308d38994ace561738a51f" +} +``` + +The same notification returned by `GetPendingEvents(network: ENJIN, chain: RELAY)` has this shape: + +```json +{ + "uuid": "f2342ed4-3504-4f81-b4b5-315635283652", + "event": "BalancesTransfer", + "payload": { + "network": "ENJIN", + "chain": "RELAY", + "publicKey": "0xa7208d10c6622f3f7eca1551de8355fde9de577dbb308d38994ace561738a51f" + } +} +``` + +The inbox returns the delivery UUID separately from `payload`. Its `event` is the exact Pusher name, without the dots or spaces used for on-chain event logs. These notifications identify what changed; query the relevant account, token, listing, or pool for its current state. + ## Platform events +The following lifecycle notifications are delivered over WebSocket and are separate from the on-chain notifications retained in Event Inbox. In `TransactionCreated` and `TransactionStateChanged`, `uuid` identifies the **transaction** for `GetTransaction`, rather than an inbox delivery to acknowledge. + Event payloads are deliberately lightweight: they carry just enough to tell you *what* changed, and you fetch whatever else you need with a follow-up GraphQL query. For example, when `TransactionStateChanged` reports `FINALIZED`, query [`GetTransaction(uuid:)`](/03-api-reference/01-queries/01-transactions-queries.md#gettransaction) to read the on-chain outcome and emitted events — see [Working with Events](/05-enjin-platform/03-working-with-events.md) for that flow. | Event | Fires when | diff --git a/docs/05-enjin-platform/03-working-with-events.md b/docs/05-enjin-platform/03-working-with-events.md index 9b1ef8a..f8fc248 100644 --- a/docs/05-enjin-platform/03-working-with-events.md +++ b/docs/05-enjin-platform/03-working-with-events.md @@ -1,7 +1,7 @@ --- title: "Working with Events" slug: "working-with-events" -description: "How to read the on-chain events emitted by transactions through the Enjin Platform API." +description: "Receive Enjin Platform notifications through WebSocket or Event Inbox, deduplicate and acknowledge deliveries, and read on-chain transaction events." --- import Tabs from '@theme/Tabs'; @@ -9,7 +9,60 @@ import TabItem from '@theme/TabItem'; In the blockchain ecosystem, "events" are the on-chain log entries emitted by extrinsics — they're how the chain reports what actually happened: token transfers, collection creation, marketplace listing IDs, balance changes, and so on. Monitoring them is essential for any application that needs to react to state changes — for example, unlocking an in-game item the moment the user receives the corresponding NFT. -## How events are exposed +## Choosing how to receive events + +The platform provides notifications for changes relevant to your account, alongside the on-chain event logs you can read from a transaction's extrinsic. + +| Approach | Use it when | +| --- | --- | +| [WebSocket through Pusher](/03-api-reference/03-websocket-events.md) | Your application needs notifications as changes happen. | +| [Event Inbox](#event-inbox) | You want to poll for on-chain notifications at a convenient time, or recover notifications missed during a connection outage. | +| [Transaction and extrinsic queries](#reading-a-transactions-events) | You need a transaction's on-chain outcome and detailed event logs. | + +WebSocket and Event Inbox can share the same handler for eligible on-chain notifications: both expose the same event names and notification data. Event Inbox captures notifications automatically, without requiring a WebSocket subscription. It covers notifications for your eligible wallets and collections, rather than every event on the blockchain. + +Platform lifecycle notifications such as `TransactionStateChanged` and `WalletLinked` are delivered over WebSocket separately. See the [notification reference](/03-api-reference/03-websocket-events.md#on-chain-notifications) for the distinction and supported inbox events. + +## Event Inbox {#event-inbox} + +Event Inbox retains notifications for **14 days** from dispatch. It supports both periodic polling and recovery alongside real-time delivery. Unacknowledged notifications expire and are automatically purged after 14 days, so choose a polling and retry schedule that processes them within that window. Historical events are not backfilled. + +### Polling and acknowledging + +1. Call [`GetPendingEvents`](/03-api-reference/01-queries/09-event-inbox-queries.md#getpendingevents) for a network and chain. It returns the oldest pending notifications first, up to `limit` (`1`–`100`, default `100`). The default network is `ENJIN` and the default chain is `MATRIX`. +2. For each entry, use its `uuid` to check whether your application has already processed the delivery. Dispatch new notifications to your handler using `event` and `payload`. +3. Persist the successful processing result and the delivery UUID. Then call [`AcknowledgeEvents`](/03-api-reference/02-mutations/09-event-inbox-mutations.md#acknowledgeevents) with the successfully processed UUIDs, including any you had already processed on a previous attempt. +4. Query again to retrieve the next batch. Continue until the result is `[]`, then wait until your next scheduled poll. Repeat for each network and chain your application uses. + +`GetPendingEvents` returns a list of objects with `uuid`, `event`, and `payload`. Reading them does not remove them. **There is no pagination**: acknowledgment is how you advance beyond the oldest pending notifications. + +`AcknowledgeEvents` accepts `1`–`100` distinct, valid UUIDs across any network or chain. It returns the number newly acknowledged; retrying UUIDs that are already acknowledged does not count them again. If processing fails, leave those notifications unacknowledged for retry. If acknowledgment fails after processing succeeds, retry acknowledgment without repeating the business action. + +### Deduplicating deliveries + +Use the delivery **`uuid`** as the deduplication key. The same delivery has the same UUID in both transports: + +| Transport | Event name | Delivery UUID | Notification data | +| --- | --- | --- | --- | +| Event Inbox | Entry's `event` | Entry's top-level `uuid` | Entry's `payload` JSON object | +| Pusher client library | Callback's event name | Callback payload's `uuid` | Callback payload, including `uuid` | +| Raw WebSocket | Frame's `event` | `uuid` inside the decoded `data` | Parse the frame, then its JSON-encoded `data` string | + +The inbox `payload` contains the same notification data as Pusher, with the delivery UUID exposed separately. Event names match exactly, for example `BalancesTransfer`; they differ from the spaced `name` values on extrinsic event logs described below. + +Store processed UUIDs durably so deduplication survives restarts. Coordinate the UUID record with the business action, for example in one database transaction, and use the UUID as an idempotency key for external actions where supported. When WebSocket and polling handlers run concurrently, enforce uniqueness atomically so they cannot both process the same delivery. Keep deduplication records for at least the inbox retention window. + +The delivery UUID identifies a notification, not a transaction. In the separate `TransactionCreated` and `TransactionStateChanged` lifecycle payloads, `uuid` remains the **transaction UUID** used with `GetTransaction`; do not acknowledge those UUIDs through Event Inbox. + +### Recovering after a WebSocket disconnect + +After the Pusher subscription succeeds on initial connection or reconnection, drain Event Inbox for each relevant network and chain while continuing to receive live notifications. Route both through the same deduplication and processing logic. This handles overlap between live delivery and recovery without applying the same action twice. + +Acknowledge successfully processed on-chain notifications received over WebSocket too, using the delivery UUID in their payload. This keeps the inbox focused on work still outstanding. A periodic inbox poll can also recover missed notifications while the application remains connected. + +For lifecycle notifications outside Event Inbox, query the relevant resource after reconnecting — for example, use `GetTransaction` to refresh outstanding transactions. + +## How on-chain event logs are exposed {#how-events-are-exposed} Once a transaction has been included in a block, the platform exposes the extrinsic that carried it — and every event that extrinsic emitted — through the `extrinsic` field on the `Transaction` type: @@ -260,6 +313,6 @@ Events hang off the extrinsic, so any query that returns an `Extrinsic` returns You can also follow your transactions in the [Platform UI](https://platform.enjin.io/transactions): the Transactions page shows each transaction's state and extrinsic hash as it moves on-chain. -:::tip Skip the polling -The platform pushes `TransactionStateChanged` and other events to your application over WebSocket in real time, removing the need to poll — see [WebSocket Events](/03-api-reference/03-websocket-events.md) for the full reference. +:::tip Notifications and recovery +Use [WebSocket Events](/03-api-reference/03-websocket-events.md) for real-time notifications and [Event Inbox](#event-inbox) to poll for eligible on-chain notifications or recover missed deliveries. Query the transaction or extrinsic when you need its detailed on-chain event logs. ::: diff --git a/docs/05-enjin-platform/05-enjin-platform.md b/docs/05-enjin-platform/05-enjin-platform.md index 307db08..9377aef 100644 --- a/docs/05-enjin-platform/05-enjin-platform.md +++ b/docs/05-enjin-platform/05-enjin-platform.md @@ -28,7 +28,7 @@ Our [GraphQL API](/01-getting-started/05-using-enjin-api/05-using-enjin-api.md) - **Queries** for reading blockchain state — accounts, tokens, collections, marketplace listings, nomination pools, and transactions. - **Mutations** for building and broadcasting transactions. All on-chain actions go through a single `CreateTransaction` mutation, with the specific action selected via the `transaction` input (e.g. `createCollection`, `mintToken`, `transferToken`). -- **Events** for real-time updates — subscribe to receive on-chain events as they fire, instead of polling. See [Working with Events](/05-enjin-platform/03-working-with-events.md) for the current event-reading flow. +- **Events** through WebSocket subscriptions and **Event Inbox** — receive notifications in real time, poll for on-chain notifications when convenient, or recover missed deliveries after a disconnect. Use delivery UUIDs to deduplicate and acknowledge processed inbox notifications. See [Working with Events](/05-enjin-platform/03-working-with-events.md) for the full workflow. ### Wallet Daemon