Skip to content

NUT-17: WebSockets

optional

depends on: NUT-07


This NUT defines a websocket protocol that enables bidirectional communication between apps and mints using the JSON-RPC format.

Subscriptions

The websocket enables real-time subscriptions that wallets can use to receive notifications for a state change of a MintQuoteResponse (NUT-04), MeltQuoteResponse (NUT-05), CheckStateResponse (NUT-07).

A summary of the subscription flow is the following:

  1. A wallet connects to the websocket endpoint and sends a WsRequest with the subscribe command.
  2. The mint responds with a WsResponse containing an ok or an error.
  3. If the subscription was accepted, the mint sends a WsNotification of the current state of the subscribed objects and whenever there is an update for the wallet's subscriptions.
  4. To close a subscription, the wallet sends WsRequest with the unsubscribe command.

Specifications

The websocket is reachable via the mint's URL path /v1/ws:

https://mint.com/v1/ws

NUT-17 uses the JSON-RPC format for all messages. There are three types of messages defined in this NUT.

Requests

All requests from the wallet to the mint are of the form of a WsRequest:

{
  "jsonrpc": "2.0",
  "method": <str_enum[WsRequestMethod]>,
  "params": <str_WsRequestParams>,
  "id": <int>
}

WsRequestMethod is a enum of strings with the supported commands "subscribe" and "unsubscribe":

enum WsRequestMethod {
  sub = "subscribe",
  unsub = "unsubscribe",
}

WsRequestParams is a serialized JSON with the parameters of the corresponding command.

Command: Subscribe

To subscribe to updates, the wallet sends a "subscribe" command with the following params parameters:

{
  "kind": <str_enum[SubscriptionKind]>,
  "subId": <string>,
  "filters": <string[]>
}

Here, subId is a unique uuid generated by the wallet and allows the client to map its requests to the mint's responses.

SubscriptionKind is an enum with the following possible values:

enum SubscriptionKind {
  bolt11_melt_quote = "bolt11_melt_quote",
  bolt11_mint_quote = "bolt11_mint_quote",
  proof_state = "proof_state",
}

The filters are an array of mint quote IDs (NUT-04), or melt quote IDs (NUT-05), or Y's (NUT-07) of the corresponding object to receive updates from.

As an example, filters would be of the following form to subscribe for updates of three different mint quote IDs:

["20385fc7245...", "d06667cda9b...", "e14d8ca96f..."]

Note that id and subId are unrelated. The subId is the ID for each subscription, whereas id is part of the JSON-RPC spec and is an integer counter that must be incremented for every request sent over the websocket.

Important: If the subscription is accepted by the mint, the mint MUST first respond with the current state of the subscribed object and continue sending any further updates to it.

For example, if the wallet subscribes to a Proof.Y of a Proof that has not been spent yet, the mint will first respond with a ProofState with state == "UNSPENT". If the wallet then spends this Proof, the mint would send a ProofState with state == "PENDING" and then one with state == "SPENT". In total, the mint would send three notifications to the wallet.

Command: Unsubscribe

The wallet should always unsubscribe any subscriptions that is isn't interested in anymore. The parameters for the "unsubscribe" command is only the subscription ID:

{
  "subId": <string>
}

Responses

A WsResponse is returned by the mint to both the "subscribe" and "unsubscribe" commands and indicates whether the request was successful:

{
  "jsonrpc": "2.0",
  "result": {
    "status": "OK",
    "subId": <str>
  },
  "id": <int>
}

Here, the id corresponds to the id in the request (as part of the JSON-RPC spec) and subId corresponds to the subscription ID.

Notifications

WsNotification's are sent from the mint to the wallet and contain subscription data in the following format

{
  "jsonrpc": "2.0",
  "method": "subscribe",
  "params": {
    "subId": <str>,
    "payload": NotificationPayload
  }
}

subId is the subscription ID (previously generated by the wallet) this notification corresponds to. NotificationPayload carries the subscription data which is a MintQuoteResponse (NUT-04), a MeltQuoteResponse (NUT-05), or a CheckStateResponse (NUT-07), depending on what the corresponding SubscriptionKind was.

Errors

WsErrors for a given WsRequest are returned in the following format

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32601,
    "message": "Human readable error message"
  },
  "id": "1"
}

Example: ProofState subscription

To subscribe to the ProofState of a Proof, the wallet establishes a websocket connection to https://mint.com/v1/ws and sends a WsRequest with a filters chosen to be the a Proof.Y value of the Proof (see NUT-00). Note that filters is an array meaning multiple subscriptions of the same kind can be made in the same request.

Wallet:

{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "subscribe",
  "params": {
    "kind": "proof_state",
    "filters": [
      "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d"
    ],
    "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T"
  }
}

The mint first responds with a WsResponse confirming that the subscription has been added.

Mint:

{
  "jsonrpc": "2.0",
  "result": {
    "status": "OK",
    "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T"
  },
  "id": 0
}

The mint immediately sends the current ProofState of the subscription as a WsNotification.

Mint:

{
  "jsonrpc": "2.0",
  "method": "subscribe",
  "params": {
    "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T",
    "payload": {
      "Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d",
      "state": "UNSPENT",
      "witness": null
    }
  }
}

While leaving the websocket connection open, the wallet then spends the ecash. The mint sends WsNotification updating the wallet about state changes of the ProofState accordingly:

Mint:

{"jsonrpc": "2.0", "method": "subscribe", "params": {"subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "PENDING"}}}

{"jsonrpc": "2.0", "method": "subscribe", "params": {"subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "SPENT"}}}

The wallet then unsubscribes.

Wallet:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "unsubscribe",
  "params": { "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T" }
}

Mint info setting

Mints signal websocket support via NUT-06 using the following setting:

"nuts": {
    "17": {
      "supported": [
        {
          "method": <str>,
          "unit": <str>,
          "commands": <str[]>
        },
        ...
      ]
    }
}

Here, commands is an array of the commands that the mint supports. A mint that supports all commands would return ["bolt11_mint_quote", "bolt11_melt_quote", "proof_state"]. Supported commands are given for each method-unit pair.

Example:

"nuts": {
    "17": {
      "supported": [
        {
          "method": "bolt11",
          "unit": "sat",
          "commands": [
            "bolt11_mint_quote",
            "bolt11_melt_quote",
            "proof_state"
            ]
        },
      ]
    }
}