# Overview The `five-bells-ledger` WebSocket API provides a way for real-time access to the ledger. ## Endpoint The URL for the websocket endpoint is available by `GET`ting the ledger's main URI (metadata endpoint) and inspecting the `urls.websocket` property. Example: ###### Request ``` http GET / HTTP/1.1 Host: ledger.example ``` ###### Response (irrelevant fields omitted) ``` http HTTP/1.1 200 OK Content-Type: application/json { "urls": { "websocket": "wss://ledger.example/websocket" } } ``` ## Authentication Unfortunately, browsers do not support setting a custom auth header (for `Basic` auth from a browser client) for websocket connections. So in order to support any type of client, we need to use [token-based authentication](https://auth0.com/blog/auth-with-socket-io/). The client first makes a regular HTTP request to obtain a token: ``` http POST /authenticate HTTP/1.1 Accept: application/json Authorization: Basic c3RlZmFuOnN0ZWZhbg== ``` ``` http HTTP 200 OK Content-Type: application/json { "token": "...xyz..." } ``` Note that the authenticate request fails if the user does not successfully authenticate. Anonymous requests are not allowed. The client then uses this token as a query parameter when initiating the websocket connection: ``` http GET /websocket?token=...xyz... HTTP/1.1 Connection: Upgrade Upgrade: websocket ``` ``` http HTTP 101 Switching Protocols Connection: Upgrade Upgrade: websocket ``` The websocket client is now treated as the user who initiated the `/authenticate` request. Anonymous connections (without token) are disallowed. The token is simply an opaque string from the client's perspective. The server may use a technology like [JWT](https://jwt.io/) to generate the token. ## Bidirectional JSON-RPC 2.0 In JSON-RPC 2.0 requests always go from "client" to "server" and responses always go from "server" to "client". Each response must be solicited by a prior request. In order to implement a publish/subscribe flow, the ledger must be able to send unsolicited messages. Therefore, the ledger and the TCP client must be acting as both JSON-RPC client and JSON-RPC server. ## Legend The examples in this document use the following convention: ``` json --> data sent from Client to Ledger <-- data sent from Ledger to Client ``` # Commands #### subscribe_account <code>subscribe_account({ eventType: String, accounts: String[] }) ⇒ null</code> Subscribe to events related to zero or more accounts. Parameters * `eventType` - Scope of the subscription. Valid scopes are: * `"transfer.create"` - Triggered for newly created transfer * `"transfer.update"` - Triggered for transfer changes (e.g. state changes) Note that a wildcard may be used to refer to multiple events, e.g. `transfer.*` would subscribe to all transfer events. * `accounts` - List of accounts to subscribe to. Admins are allowed to subscribe to any account whereas regular users are allowed only to subscribe to their own account. ###### Example ```json --> { "jsonrpc": "2.0", "method": "subscribe_account", "params": [{ "eventType": "transfer.*", "accounts": ["https://ledger.example/accounts/alice"] }], "id": 1 } <-- { "jsonrpc": "2.0", "result": 19, "id": 1 } <-- { "jsonrpc": "2.0", "method": "notify", "params": [{ "id": "https://ledger.example/notifications/c92f2a2c-b21d-4e6c-96e7-4f6d6df4bee9", "event": "transfer.update", "resource": { "id": "https://ledger.example/transfers/155dff3f-4915-44df-a707-acc4b527bcbd", "ledger": "http://localhost", "debits": [{ "account": "https://ledger.example/accounts/alice", "amount": "10", "authorized": true }], "credits": [{ "account": "https://ledger.example/accounts/bob", "amount": "10" }], "state": "executed" } }], "id": null } <-- { "jsonrpc": "2.0", "method": "notify", "params": [{ "id": "https://ledger.example/notifications/52a42d6f-8d9c-4c05-b31c-cccc8bbdb30d", "event": "transfer.update", "resource": { /* transfer resource */ }, "related_resources": { "execution_condition_fulfillment": "[ fulfillment ]", "cancellation_condition_fulfillment": "[ fulfillment ]" } }], "id": null } ``` #### subscribe_all_accounts <code>subscribe_all_accounts({ eventType: String }) ⇒ null</code> Subscribe to account-related events from all accounts. Requires admin permission.