WebSocket-події

Посібники

WebSocket-події

WebSocket stream BroSettlement повідомляє організацію про durable зміни рахунків, гаманців, депозитів і транзакцій, щоб клієнт міг оновлювати пов’язані REST-ресурси без постійного polling.

Підключення

Staging endpoint:

text
wss://brosettlement-staging-api.brolabel.io/v1/ws

API key потребує scope websockets:read. Підпишіть handshake приватним Ed25519-ключем API key:

text
WS_CONNECT
/v1/ws
TIMESTAMP
NONCE

Використовуйте API key ID, Unix timestamp у секундах, nonce і стандартний padded Base64 signature відповідно до поточного integration environment. Шестирядковий REST canonical value не підходить для WebSocket connection.

Модель доставки

  • Events належать лише організації API key.
  • Durable business event має globally unique ID, який не змінюється під час redelivery.
  • Delivery працює за моделлю at least once: клієнт має очікувати дублікати.
  • occurredAt — час durable state transition, а не доставки через WebSocket.
  • correlationId зв’язує REST requests, транзакції, blockchain activity і support logs.
  • Event schemas починаються з version 1 і розвиваються additive-only.
  • Listener має ігнорувати невідомі поля.
  • Приватні ключі, raw signatures, seed material, user secrets та internal MPC payloads не повинні потрапляти до event.

Event envelopes

MVP-compatible envelope використовує type, event і eventId:

json
{
  "type": "EVENT",
  "event": "transaction.updated",
  "eventId": "evt_...",
  "version": 1,
  "orgId": "org-id",
  "occurredAt": "2026-07-07T13:38:33.000Z",
  "correlationId": "request-or-flow-id",
  "data": {}
}

Durable topic-stream envelope використовує op, subject, id і sequence:

json
{
  "op": "event",
  "subId": "s1",
  "id": "evt_...",
  "sequence": 987654,
  "subject": "transaction.updated",
  "occurredAt": "2026-07-07T13:38:33.000Z",
  "orgId": "org-id",
  "correlationId": "request-or-flow-id",
  "version": 1,
  "data": {}
}

Під час міграції підтримуйте обидва envelopes, якщо ваше середовище може надсилати обидві форми. Нормалізуйте eventId/id та event/subject перед обробкою.

Обов’язкові event families

EventЗначення
system.connectedАвтентифікація успішна, stream готовий
system.errorПомилка автентифікації, policy, topic або payload
account.createdРахунок операційного журналу durable created
wallet.createdГаманець і адресу створено та можна прочитати через REST
wallet.creation_failedАсинхронно прийнятий wallet provisioning завершився помилкою
wallet.status_changedГаманець перейшов між lifecycle states
transaction.createdСтворено durable transaction record або intent
transaction.updatedЗмінився client-visible стан або on-chain detail
transaction.confirmedТранзакція досягла успішної finality
transaction.failedОбробка завершилася помилкою до успішного on-chain completion
transaction.failed_on_chainBlockchain execution failed або reverted
deposit.observedВхідний переказ виявлено
deposit.confirmedДепозит виконав confirmation policy
deposit.creditedПідтверджений депозит зараховано до операційного журналу

Read-only REST calls не створюють business event лише через факт виклику.

Account events

account.created надходить після успішного POST /api/v1/ledger/accounts.

json
{
  "accountId": "account-id",
  "orgId": "org-id",
  "name": "Customer 123",
  "externalId": "customer-123",
  "createdAt": "2026-07-07T13:00:00.000Z",
  "requestId": "http-request-id",
  "idempotencyKey": null
}

Synchronous validation або authentication rejection, який не створив рахунок, не призводить до account.created.

Wallet events

wallet.created ідентифікує durable wallet та адресу:

json
{
  "walletId": "wallet-id",
  "accountId": "account-id",
  "orgId": "org-id",
  "chain": "tron:nile",
  "address": "T...",
  "status": "ACTIVE",
  "createdAt": "2026-07-07T13:00:00.000Z",
  "requestId": "http-request-id",
  "idempotencyKey": "client-operation-id"
}

Якщо асинхронно прийняте створення гаманця згодом завершується помилкою, надсилається wallet.creation_failed з accountId, chain, status і machine-readable errorCode. wallet.status_changed використовується для переходів на кшталт PENDINGACTIVE, DISABLED, ARCHIVED або FAILED.

Synchronous 4xx, який не створив durable wallet, не вимагає business event. Джерелом істини є REST error.

Transaction events

Використовуйте єдину family transaction.* для withdrawals та internal transfers. Не розділяйте lifecycle на withdrawal.*, transfer.* або internal_transfer.*.

Тип переміщення визначається в payload:

  • isInternalTransfer: false і transferKind: "EXTERNAL_WITHDRAWAL" для зовнішнього виведення;
  • isInternalTransfer: true і transferKind: "INTERNAL_TRANSFER" для переміщення всередині організації.

Minimum lifecycle payload:

json
{
  "transactionId": "transaction-id",
  "transactionGroupId": null,
  "sourceTransactionId": null,
  "destinationTransactionId": null,
  "orgId": "org-id",
  "accountId": "account-id",
  "walletId": "wallet-id",
  "destinationAccountId": null,
  "destinationWalletId": null,
  "chain": "tron:nile",
  "asset": "TRX",
  "amountAtomic": "7000000",
  "amountDirection": "DEBIT",
  "isInternalTransfer": false,
  "transferKind": "EXTERNAL_WITHDRAWAL",
  "fromAddress": "T...",
  "toAddress": "T...",
  "txHash": null,
  "status": "PENDING",
  "sourceStatus": "PENDING",
  "destinationStatus": null,
  "failureCode": null,
  "failureMessage": null
}

transaction.updated покриває reservations, MPC signing progress, відправлення у блокчейн, confirmation progress, on-chain details і externally relevant failure information. Terminal events містять фінальний REST-visible status: CONFIRMED, FAILED або FAILED_ON_CHAIN.

Зберігайте amountAtomic як base-10 string. Не обробляйте його через floating-point types.

Депозити

Депозити виникають унаслідок blockchain observation, а не client REST POST:

  1. deposit.observed — вхідний переказ виявлено.
  2. transaction.created — існує client-visible deposit transaction.
  3. deposit.confirmed — досягнуто необхідної кількості confirmations.
  4. transaction.confirmed — транзакція досягла terminal success.
  5. deposit.credited — кошти зараховано до операційного журналу.

Implementation може об’єднувати сумісні lifecycle signals, але підтверджений депозит повинен створити щонайменше одну deposit або transaction event. Клієнт не повинен виявляти durable confirmed deposit лише через polling.

Deposit data має містити transactionId, якщо він уже доступний, walletId, accountId, chain, asset, amountAtomic, txHash, block information і confirmation progress.

Synchronous rejection і durable failure

Ці випадки мають різну event behavior:

Результат POST /api/v1/transactionsОчікування WebSocket
Synchronous 4xx, немає transaction ID, durable record або reservationНемає transaction.* business event; обробіть REST error
Transaction ID або durable intent створено, а потім обробка завершилася помилкоюНадішліть transaction.created, потім terminal transaction.failed
Blockchain execution failed або revertedНадішліть transaction.failed_on_chain

Щойно транзакція стала видимою через list/detail REST endpoints, кожен наступний terminal failure має бути видимим і через WebSocket. Failed reservation має бути voided рівно один раз.

Для internal transfer із paired debit/credit rows додайте sourceTransactionId, destinationTransactionId і leg statuses. Failed source leg не повинен залишати destination leg у PENDING.

Звірка з REST

Сприймайте event як сигнал оновити authoritative state:

Event familyREST resources для оновлення
account.*/api/v1/ledger/accounts/{accountId}
wallet.*/api/v1/wallets/{walletId} і wallets рахунку
transaction.*/api/v1/transactions/{id}, пов’язані balances і journal entries
deposit.*Transaction detail, wallet/account balances і ledger entries

Transactions list і transaction detail мають збігатися за status. Не показуйте фінальний клієнтський баланс лише на основі неперевіреного event payload.

Вимоги до listener

  1. Записуйте отримані frames, але не API private keys або signing secrets.
  2. Парсьте JSON зі збереженням original payload для розслідування.
  3. Сприймайте system.connected як transport state, а не business event.
  4. Дедуплікуйте business events за стабільним eventId або id.
  5. Зберігайте sequence або last event ID, коли доступний resume.
  6. Виконуйте acknowledgement durable events, якщо server вимагає explicit ack.
  7. Повторно підключайтеся з bounded exponential backoff і jitter.
  8. Відновлюйте stream з останньої committed position, коли це підтримується.
  9. Оновлюйте пов’язані REST resources після transaction.*, wallet.* і deposit events.
  10. Commit resume position лише після успішної локальної обробки.

Сумісність поточного staging

Requirements document фіксує такі staging compatibility gaps:

  • частина deployments може ще надсилати legacy SYSTEM_CONNECTED замість system.connected;
  • поточний /v1/ws transport може ще не підтримувати topics, ack або resume;
  • event coverage для deposits і paired internal-transfer legs потрібно перевіряти в цільовому середовищі.

Під час міграції нормалізуйте legacy connection event, але не вигадуйте відсутні business events на стороні клієнта. Використовуйте REST-звірку для відновлення стану й підтвердьте ввімкнений WebSocket contract перед запуском у робочому режимі.

Безпека

  • Відхиляйте або ігноруйте event, якщо orgId не відповідає автентифікованій організації.
  • Не записуйте credentials або MPC signing material у журнали.
  • Сприймайте event data як untrusted input і валідуйте required fields.
  • Зберігайте correlationId і REST requestId для підтримки.
  • Використовуйте окремий least-privilege API key зі scope websockets:read.