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:
- A wallet connects to the websocket endpoint and sends a
WsRequestwith thesubscribecommand. - The mint responds with a
WsResponsecontaining an ok or an error. - If the subscription was accepted, the mint sends a
WsNotificationof the current state of the subscribed objects and whenever there is an update for the wallet’s subscriptions. - To close a subscription, the wallet sends
WsRequestwith theunsubscribecommand.
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 an 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 v7 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",
bolt12_melt_quote = "bolt12_melt_quote",
bolt12_mint_quote = "bolt12_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 it
isn’t interested in anymore. The parameters for the
"unsubscribe" command are 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
WsNotifications 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 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": "019e6d5a-2347-7000-8afa-051ae571f334"
}
}
The mint first responds with a WsResponse
confirming that the subscription has been added.
Mint:
{
"jsonrpc": "2.0",
"result": {
"status": "OK",
"subId": "019e6d5a-2347-7000-8afa-051ae571f334"
},
"id": 0
}
The mint immediately sends the current ProofState
of the subscription as a WsNotification.
Mint:
{
"jsonrpc": "2.0",
"method": "subscribe",
"params": {
"subId": "019e6d5a-2347-7000-8afa-051ae571f334",
"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": "019e6d5a-2347-7000-8afa-051ae571f334", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "PENDING"}}}
{"jsonrpc": "2.0", "method": "subscribe", "params": {"subId": "019e6d5a-2347-7000-8afa-051ae571f334", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "SPENT"}}}
The wallet then unsubscribes.
Wallet:
{
"jsonrpc": "2.0",
"id": 1,
"method": "unsubscribe",
"params": { "subId": "019e6d5a-2347-7000-8afa-051ae571f334" }
}
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", "bolt12_mint_quote", "bolt12_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"
]
}
]
}
}