12<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-47 - Nostr Wallet Connect (NWC) — Nostr Instruction Manual</title>7<meta name="description" content="NIP-47: Nostr Wallet Connect. Remote lightning wallet control over encrypted Nostr events. Connection URI, commands, error codes, and encryption negotiation.">8<link rel="preload" href="../assets/fonts/michroma-400-latin.woff2" as="font" type="font/woff2" crossorigin>9<link rel="preload" href="../assets/fonts/exo2-var-latin.woff2" as="font" type="font/woff2" crossorigin>10<link rel="preload" href="../assets/fonts/sairacondensed-600-latin.woff2" as="font" type="font/woff2" crossorigin>11<link rel="preload" href="../assets/fonts/sairacondensed-700-latin.woff2" as="font" type="font/woff2" crossorigin>12<link rel="preload" href="../assets/fonts/sairacondensed-500-latin.woff2" as="font" type="font/woff2" crossorigin>13<link rel="preload" href="../assets/fonts/sharetechmono-400-latin.woff2" as="font" type="font/woff2" crossorigin>14<link rel="preload" href="../assets/fonts/exo2-400i-latin.woff2" as="font" type="font/woff2" crossorigin>15<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">16<link rel="stylesheet" href="../assets/manual.css">17</head>18<body>1920<svg width="0" height="0" style="position:absolute">21 <defs>22 <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">23 <path d="M1,1 L8,4.5 L1,8 Z" fill="#f26a00"/>24 </marker>25 <marker id="arrb" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">26 <path d="M1,1 L8,4.5 L1,8 Z" fill="#2e5f9e"/>27 </marker>28 </defs>29</svg>3031<div class="page">32 <span class="staple s1"></span><span class="staple s2"></span>3334 <header class="band">35 <a class="logo" href="../index.html">NOSTR<b>▸</b>PROTOCOL MANUAL</a>36 <span class="booklet-note">Chapter 47 - Payments</span>37 </header>38 <nav class="tabs">39 <a href="../index.html">Contents</a>40 <a href="#overview" class="on">Overview</a>41 <a href="#spec">Spec</a>42 <a href="#examples">Examples</a>43 <a href="#history">History</a>44 </nav>4546 <div class="chapter">47 <div class="nipno" data-n="47">47</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 47</div>50 <h1>Nostr Wallet Connect (NWC)</h1>51 <p class="sub">Control a remote lightning wallet from any Nostr client through end-to-end encrypted messages over a relay.</p>52 </div>53 </div>5455 <main class="spread">56 <div class="main">5758 <h2 class="bar" id="overview"><span class="num">1</span> Overview</h2>59 <p>Many Nostr clients want to pay lightning invoices. Few devices can run a lightning wallet all day. This NIP connects the two. A <strong>client</strong> sends payment commands to a remote <strong>wallet service</strong>. The wallet service executes them against a real wallet and reports back. A custodian may run the wallet service. A user may also run a bridge that connects their own wallet or node to the NWC protocol.</p>60 <p>All traffic travels as end-to-end encrypted direct messages over a normal Nostr relay. The relay sees only event kinds and tags. It never sees the payloads. The user's Nostr identity key is never used. This avoids linking payment activity to the user. Ideally each connection uses its own unique key.</p>61 <p>This NIP defines the core protocol: three event kinds, a connection URI, five commands, and an error model. More granular authorization models and advanced wallet features live in optional extension specifications in the NWC repository at <code>https://github.com/nostr-wallet-connect/nwc</code>.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>NWC powers one-tap zaps (<a href="57.html">NIP-57</a>). The client fetches an invoice, then asks the wallet service to pay it. The user never leaves the app.</p>66 </div>6768 <h2 class="bar" id="spec"><span class="num">2</span> Actors and Theory of Operation</h2>69 <p>The protocol names three actors. Use these terms exactly:</p>70 <div class="tscroll"><table class="ref">71 <caption>Actors</caption>72 <tr><th>Term</th><th>Meaning</th></tr>73 <tr><td class="btn">client</td><td>A Nostr app, on any platform, that wants to interact with a lightning wallet.</td></tr>74 <tr><td class="btn">user</td><td>The person who uses the client and wants to connect their wallet to it.</td></tr>75 <tr><td class="btn">wallet service</td><td>A Nostr app that typically runs on an always-on computer, for example in the cloud or on a Raspberry Pi. It has access to the APIs of the wallets it serves.</td></tr>76 </table></div>7778 <p>A session follows five steps:</p>79 <ol>80 <li>The user gets a special <strong>connection URI</strong> from their NIP-47 compliant wallet application. The wallet may show it as a QR code, a pasteable string, or another form.</li>81 <li>The user copies the URI into their client by pasting or scanning. The client saves the URI for all later wallet interaction. The client then requests the <code>info</code> event (kind 13194) from the relays named in the URI. The wallet service has published that event there earlier, and the relays hold it as a replaceable event.</li>82 <li>When the user starts a payment, the client creates a <code>pay_invoice</code> request. It encrypts the request with the secret from the URI and sends it as a kind 23194 event to the relays from the URI. The wallet service listens on those relays, decrypts the request, and tells the user's wallet application to send the payment.</li>83 <li>When the payment completes, the wallet service sends an encrypted response (kind 23195) back to the client over the same relays.</li>84 <li>Optional extension specifications may define additional wallet-to-client events.</li>85 </ol>8687 <div class="box tech">88 <span class="tag">Tech Note</span>89 <p>The source text of step 3 says the wallet service knows how to talk to the wallet "because the connection URI specified relay(s) that have access to the wallet app API". This contradicts the architecture: relays only carry encrypted events and have no wallet API access. The wallet service itself holds the API access, as the Terms section states. Read the sentence as: the URI names relays where the wallet service listens.</p>90 </div>9192 <figure class="diagram">93 <div class="frame">94 <svg viewBox="0 0 560 200" role="img" aria-label="Client sends encrypted requests through a relay to the wallet service, which talks to the lightning wallet and answers through the same relay">95 <rect class="d-actor" x="10" y="75" width="105" height="50" rx="8"/>96 <text class="d-actor-label" x="62" y="97" text-anchor="middle">Client</text>97 <text class="d-note" x="62" y="114" text-anchor="middle">holds secret</text>98 <rect class="d-actor" x="210" y="75" width="105" height="50" rx="8"/>99 <text class="d-actor-label" x="262" y="97" text-anchor="middle">Relay</text>100 <text class="d-note" x="262" y="114" text-anchor="middle">sees kinds only</text>101 <rect class="d-actor" x="410" y="30" width="140" height="50" rx="8"/>102 <text class="d-actor-label" x="480" y="52" text-anchor="middle">Wallet service</text>103 <text class="d-note" x="480" y="69" text-anchor="middle">always on</text>104 <rect class="d-actor" x="410" y="130" width="140" height="50" rx="8"/>105 <text class="d-actor-label" x="480" y="152" text-anchor="middle">Lightning wallet</text>106 <text class="d-note" x="480" y="169" text-anchor="middle">node / custodian</text>107 <path class="d-msg d-flow" d="M117,90 L207,90"/>108 <path class="d-msg d-flow" d="M317,88 C355,80 370,68 407,58"/>109 <path class="d-msg blue d-flow" d="M407,68 C370,80 355,95 317,102"/>110 <path class="d-msg blue d-flow" d="M207,110 L117,110"/>111 <path class="d-msg" d="M480,82 L480,127"/>112 <text class="d-label" x="160" y="80" text-anchor="middle">23194</text>113 <text class="d-label" x="160" y="130" text-anchor="middle">23195</text>114 <text class="d-label" x="510" y="110" text-anchor="middle">API</text>115 <circle r="4" fill="#f26a00">116 <animateMotion dur="2.6s" repeatCount="indefinite" path="M117,90 L207,90 M317,88 C355,80 370,68 407,58"/>117 </circle>118 <circle r="4" fill="#2e5f9e">119 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M407,68 C370,80 355,95 317,102 M207,110 L117,110"/>120 </circle>121 </svg>122 </div>123 <figcaption>Fig. 47-1 — <b>NWC architecture.</b> Encrypted requests and responses pass through the relay. Only the wallet service touches the wallet API.</figcaption>124 </figure>125126 <h2 class="bar" id="uri"><span class="num">3</span> The Connection URI</h2>127 <p>Encryption needs two keys. The connection URI carries the <strong>secret key of the client</strong> and only the <strong>public key of the wallet service</strong>. The client discovers the wallet service by scanning a QR code, handling a deeplink, or pasting the URI.</p>128 <p>The wallet service generates the URI. The protocol is <code>nostr+walletconnect://</code>. The base path is the 32-byte hex-encoded <code>pubkey</code> of the wallet service. This pubkey SHOULD be unique per client connection.</p>129130 <div class="crt">131 <div class="titlebar">Connection URI format</div>132<pre>nostr+walletconnect://<span class="c"><wallet-service-pubkey, 32-byte hex></span>133 ?relay=<span class="c"><url-encoded relay URL></span> <span class="c">&relay=… may repeat</span>134 &secret=<span class="c"><32-byte random hex string></span>135 &lud16=<span class="c"><lightning address></span></pre>136 </div>137138 <div class="tscroll"><table class="ref">139 <caption>Query string parameters</caption>140 <tr><th>Parameter</th><th>Presence</th><th>Rule</th></tr>141 <tr><td class="btn">relay</td><td>Required</td><td>URL of the relay where the wallet service is connected and listens for events. May appear more than once.</td></tr>142 <tr><td class="btn">secret</td><td>Required</td><td>A 32-byte randomly generated hex-encoded string. The client MUST use it to sign events and encrypt payloads when it talks to the wallet service. The wallet service MUST use the corresponding public key of this secret to talk to the client.</td></tr>143 <tr><td class="btn">lud16</td><td>Recommended</td><td>A lightning address. Clients can use it to set the <code>lud16</code> field on the user's profile automatically if none is configured.</td></tr>144 </table></div>145146 <p>The per-connection <code>secret</code> exists for four reasons:</p>147 <ul>148 <li>Authorization does not require passing keys back and forth.</li>149 <li>The user can hold different keys for different applications. Keys can be revoked and created at will, with arbitrary constraints such as budgets.</li>150 <li>The key is harder to leak. It is not shown to the user and not backed up.</li>151 <li>It improves privacy. The user's main key is not linked to their payments.</li>152 </ul>153154 <p>Key usage is symmetric but strict:</p>155 <ul>156 <li>When the client sends or receives a message, it uses the <code>secret</code> from the URI plus the wallet service's <code>pubkey</code> to encrypt or decrypt.</li>157 <li>When the wallet service sends or receives a message, it uses its own secret plus the public key that corresponds to the client's <code>secret</code>. The wallet service SHOULD NOT store the secret it generated for the client. It MUST NOT rely on knowing the client secret for general operation.</li>158 </ul>159 <p>The client stores the connection and reuses it for every wallet action. This NIP uses ephemeral events. Pick relays that do not close idle connections, so events do not drop. Ideally the relays retain the events until they are consumed or become stale.</p>160161 <div class="box tech">162 <span class="tag">Tech Note</span>163 <p>Kinds 23194 and 23195 sit in the ephemeral range of <a href="01.html">NIP-01</a>, so relays are not expected to store them at all. The spec still asks for relays that "retain the events until they are either consumed or become stale". These two goals conflict on a strict NIP-01 relay. In practice, NWC relays keep the events long enough for a listener that is briefly offline.</p>164 </div>165166 <div class="crt">167 <div class="titlebar">Example connection string</div>168<pre><span class="s">nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4169 ?relay=wss%3A%2F%2Frelay.damus.io170 &secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c</span></pre>171 </div>172173 <div class="box danger">174 <span class="tag">Warning!</span>175 <p>The URI contains a live spending secret. Whoever holds it can command the wallet within its budget. Treat the URI like a password. Never publish it in an event.</p>176 </div>177178 <h2 class="bar" id="events"><span class="num">4</span> Events</h2>179 <p>The core protocol uses three event kinds:</p>180 <div class="tscroll"><table class="ref">181 <caption>Core event kinds</caption>182 <tr><th>Kind</th><th>Name</th><th>Direction</th><th>Storage class</th></tr>183 <tr><td class="btn">13194</td><td>NIP-47 info event</td><td>wallet service → relays</td><td>replaceable</td></tr>184 <tr><td class="btn">23194</td><td>NIP-47 request</td><td>client → wallet service</td><td>ephemeral</td></tr>185 <tr><td class="btn">23195</td><td>NIP-47 response</td><td>wallet service → client</td><td>ephemeral</td></tr>186 </table></div>187188 <h3>The info event (kind 13194)</h3>189 <p>The wallet service publishes the info event on the relay as a replaceable event. It advertises the capabilities the service supports. The <code>content</code> is a plaintext string of supported capabilities, space-separated, for example <code>pay_invoice get_balance</code>.</p>190 <ul>191 <li>If the wallet service supports optional NWC extensions, the info event SHOULD carry an <code>extensions</code> tag with the supported extension identifiers, space-separated, for example <code>02 03 04</code>.</li>192 <li>Any additional methods that those extensions provide SHOULD also appear in the <code>content</code> capability list.</li>193 <li>The info event should also carry an <code>encryption</code> tag with the supported encryption modes. See §7.</li>194 </ul>195 <div class="crt">196 <div class="titlebar">Info event - shape</div>197<pre>{198 <span class="k">"kind"</span>: 13194,199 <span class="k">"tags"</span>: [200 [<span class="s">"encryption"</span>, <span class="s">"nip44_v2 nip04"</span>], <span class="c">// supported encryption schemes, see §7</span>201 [<span class="s">"extensions"</span>, <span class="s">"02 03 04"</span>] <span class="c">// supported NWC extension ids, optional</span>202 ],203 <span class="k">"content"</span>: <span class="s">"pay_invoice get_balance make_invoice lookup_invoice get_info"</span>204}</pre>205 </div>206207 <h3>Request and response events (kinds 23194 / 23195)</h3>208 <p>Both request and response events SHOULD carry one <code>p</code> tag. On a request it holds the public key of the wallet service. On a response it holds the public key of the client. A response SHOULD also carry an <code>e</code> tag with the id of the request event it answers.</p>209 <p>A request may carry an optional <code>expiration</code> tag with a unix timestamp in seconds. If the wallet service receives the request after that timestamp, it should ignore the request.</p>210 <p>The <code>content</code> of requests and responses is encrypted with <a href="44.html">NIP-44</a>. Inside is a JSON-RPC-like object with a semi-fixed structure. For backwards compatibility: the initial protocol version used <a href="04.html">NIP-04</a>. If a wallet service or client does not include the <code>encryption</code> tag in the info or request events, assume the connection uses NIP-04. See §7.</p>211212 <div class="crt">213 <div class="titlebar">Request - kind 23194</div>214<pre>{215 <span class="k">"kind"</span>: 23194,216 <span class="k">"tags"</span>: [217 [<span class="s">"encryption"</span>, <span class="s">"nip44_v2"</span>],218 [<span class="s">"p"</span>, <span class="c"><wallet service pubkey></span>]219 ],220 <span class="k">"content"</span>: nip44_encrypt({ <span class="c">// scheme matches the encryption tag</span>221 <span class="k">"method"</span>: <span class="s">"pay_invoice"</span>, <span class="c">// method, string</span>222 <span class="k">"params"</span>: { <span class="c">// params, object</span>223 <span class="k">"invoice"</span>: <span class="s">"lnbc50n1..."</span> <span class="c">// command-related data</span>224 }225 })226}</pre>227 </div>228229 <div class="crt">230 <div class="titlebar">Response - kind 23195</div>231<pre>{232 <span class="k">"kind"</span>: 23195,233 <span class="k">"tags"</span>: [234 [<span class="s">"p"</span>, <span class="c"><requesting client pubkey></span>],235 [<span class="s">"e"</span>, <span class="c"><id of the request event></span>]236 ],237 <span class="k">"content"</span>: nip44_encrypt({ <span class="c">// scheme requested by the client</span>238 <span class="k">"result_type"</span>: <span class="s">"pay_invoice"</span>, <span class="c">// names the structure of result</span>239 <span class="k">"error"</span>: { <span class="c">// object, non-null on error</span>240 <span class="k">"code"</span>: <span class="s">"UNAUTHORIZED"</span>, <span class="c">// string error code, see §5</span>241 <span class="k">"message"</span>: <span class="s">"human readable"</span>242 },243 <span class="k">"result"</span>: { <span class="c">// object, null on error</span>244 <span class="k">"preimage"</span>: <span class="s">"0123456789abcdef..."</span>245 }246 })247}</pre>248 </div>249250 <p>Three rules bind the response body:</p>251 <ul>252 <li><code>result_type</code> MUST contain the name of the method that the event responds to.</li>253 <li>On failure, <code>error</code> MUST contain a <code>message</code> field with human-readable text and a <code>code</code> field with the error code. <code>result</code> is null.</li>254 <li>On success, <code>error</code> must be null.</li>255 </ul>256257 <h2 class="bar" id="errors"><span class="num">5</span> Error Codes</h2>258 <p>The <code>error.code</code> field takes one of these values for any command:</p>259 <div class="tscroll"><table class="ref">260 <caption>General error codes</caption>261 <tr><th>Code</th><th>Meaning</th></tr>262 <tr><td class="btn">RATE_LIMITED</td><td>The client sends commands too fast. It should retry in a few seconds.</td></tr>263 <tr><td class="btn">NOT_IMPLEMENTED</td><td>The command is not known, or is intentionally not implemented.</td></tr>264 <tr><td class="btn">INSUFFICIENT_BALANCE</td><td>The wallet does not have enough funds to cover a fee reserve or the payment amount.</td></tr>265 <tr><td class="btn">QUOTA_EXCEEDED</td><td>The wallet has exceeded its spending quota.</td></tr>266 <tr><td class="btn">RESTRICTED</td><td>This public key is not allowed to do this operation.</td></tr>267 <tr><td class="btn">UNAUTHORIZED</td><td>This public key has no wallet connected.</td></tr>268 <tr><td class="btn">INTERNAL</td><td>An internal error.</td></tr>269 <tr><td class="btn">UNSUPPORTED_ENCRYPTION</td><td>The encryption type of the request is not supported by the wallet service.</td></tr>270 <tr><td class="btn">OTHER</td><td>Other error.</td></tr>271 </table></div>272 <p>Two more codes belong to specific commands:</p>273 <div class="tscroll"><table class="ref">274 <caption>Command-specific error codes</caption>275 <tr><th>Code</th><th>Command</th><th>Meaning</th></tr>276 <tr><td class="btn">PAYMENT_FAILED</td><td><code>pay_invoice</code></td><td>The payment failed. Possible causes: a timeout, all routes exhausted, insufficient capacity, or similar.</td></tr>277 <tr><td class="btn">NOT_FOUND</td><td><code>lookup_invoice</code></td><td>The invoice could not be found by the given parameters.</td></tr>278 </table></div>279280 <h2 class="bar" id="commands"><span class="num">6</span> Commands</h2>281 <p>The core command set has five methods. All amounts are millisatoshis (msats). Each request names its <code>method</code> and passes a <code>params</code> object. Each response repeats the method name in <code>result_type</code>.</p>282283 <h3>pay_invoice</h3>284 <p>Requests payment of an invoice.</p>285 <div class="crt">286 <div class="titlebar">pay_invoice - request / response</div>287<pre>{288 <span class="k">"method"</span>: <span class="s">"pay_invoice"</span>,289 <span class="k">"params"</span>: {290 <span class="k">"invoice"</span>: <span class="s">"lnbc50n1..."</span>, <span class="c">// bolt11 invoice</span>291 <span class="k">"amount"</span>: 123, <span class="c">// invoice amount in msats, optional</span>292 <span class="k">"metadata"</span>: {} <span class="c">// generic metadata: zap/boostagram details,</span>293 } <span class="c">// payer name/comment/etc, optional</span>294}295<span class="c">---</span>296{297 <span class="k">"result_type"</span>: <span class="s">"pay_invoice"</span>,298 <span class="k">"result"</span>: {299 <span class="k">"preimage"</span>: <span class="s">"0123456789abcdef..."</span>, <span class="c">// preimage of the payment</span>300 <span class="k">"fees_paid"</span>: 123 <span class="c">// value in msats, optional</span>301 }302}</pre>303 </div>304 <p>Errors: <code>PAYMENT_FAILED</code>.</p>305306 <h3>make_invoice</h3>307 <p>Creates a new invoice on the wallet.</p>308 <div class="crt">309 <div class="titlebar">make_invoice - request / response</div>310<pre>{311 <span class="k">"method"</span>: <span class="s">"make_invoice"</span>,312 <span class="k">"params"</span>: {313 <span class="k">"amount"</span>: 123, <span class="c">// value in msats</span>314 <span class="k">"description"</span>: <span class="s">"string"</span>, <span class="c">// invoice description, optional</span>315 <span class="k">"description_hash"</span>: <span class="s">"string"</span>, <span class="c">// invoice description hash, optional</span>316 <span class="k">"expiry"</span>: 213, <span class="c">// seconds from creation, optional</span>317 <span class="k">"metadata"</span>: {} <span class="c">// generic metadata, optional</span>318 }319}320<span class="c">---</span>321{322 <span class="k">"result_type"</span>: <span class="s">"make_invoice"</span>,323 <span class="k">"result"</span>: {324 <span class="k">"type"</span>: <span class="s">"incoming"</span>, <span class="c">// "incoming" invoices, "outgoing" payments</span>325 <span class="k">"state"</span>: <span class="s">"pending"</span>, <span class="c">// optional</span>326 <span class="k">"invoice"</span>: <span class="s">"string"</span>, <span class="c">// encoded invoice, optional</span>327 <span class="k">"description"</span>: <span class="s">"string"</span>, <span class="c">// invoice description, optional</span>328 <span class="k">"description_hash"</span>: <span class="s">"string"</span>, <span class="c">// invoice description hash, optional</span>329 <span class="k">"preimage"</span>: <span class="s">"string"</span>, <span class="c">// preimage, optional if unpaid</span>330 <span class="k">"payment_hash"</span>: <span class="s">"string"</span>, <span class="c">// payment hash for the payment</span>331 <span class="k">"amount"</span>: 123, <span class="c">// value in msats</span>332 <span class="k">"fees_paid"</span>: 123, <span class="c">// value in msats</span>333 <span class="k">"created_at"</span>: <span class="c">unixtimestamp</span>, <span class="c">// creation time</span>334 <span class="k">"expires_at"</span>: <span class="c">unixtimestamp</span>, <span class="c">// expiration, optional if not applicable</span>335 <span class="k">"metadata"</span>: {} <span class="c">// generic metadata</span>336 }337}</pre>338 </div>339 <div class="box tech">340 <span class="tag">Tech Note</span>341 <p>The <code>state</code> field of <code>make_invoice</code> was marked optional in 2025 for backward compatibility with older wallet services. Do not treat its absence as an error.</p>342 </div>343344 <h3>lookup_invoice</h3>345 <p>Looks up the state of an invoice or payment. One of <code>payment_hash</code> or <code>invoice</code> is required.</p>346 <div class="crt">347 <div class="titlebar">lookup_invoice - request / response</div>348<pre>{349 <span class="k">"method"</span>: <span class="s">"lookup_invoice"</span>,350 <span class="k">"params"</span>: {351 <span class="k">"payment_hash"</span>: <span class="s">"31afdf1.."</span>, <span class="c">// one of payment_hash or invoice required</span>352 <span class="k">"invoice"</span>: <span class="s">"lnbc50n1..."</span> <span class="c">// invoice to look up</span>353 }354}355<span class="c">---</span>356{357 <span class="k">"result_type"</span>: <span class="s">"lookup_invoice"</span>,358 <span class="k">"result"</span>: {359 <span class="k">"type"</span>: <span class="s">"incoming"</span>, <span class="c">// "incoming" invoices, "outgoing" payments</span>360 <span class="k">"state"</span>: <span class="s">"pending"</span>, <span class="c">// "pending", "settled", "accepted" (hold</span>361 <span class="c">// invoices), "expired" (invoices) or</span>362 <span class="c">// "failed" (payments), optional</span>363 <span class="k">"invoice"</span>: <span class="s">"string"</span>, <span class="c">// encoded invoice, optional</span>364 <span class="k">"description"</span>: <span class="s">"string"</span>, <span class="c">// invoice description, optional</span>365 <span class="k">"description_hash"</span>: <span class="s">"string"</span>, <span class="c">// invoice description hash, optional</span>366 <span class="k">"preimage"</span>: <span class="s">"string"</span>, <span class="c">// preimage, optional if unpaid</span>367 <span class="k">"payment_hash"</span>: <span class="s">"string"</span>, <span class="c">// payment hash for the payment</span>368 <span class="k">"amount"</span>: 123, <span class="c">// value in msats</span>369 <span class="k">"fees_paid"</span>: 123, <span class="c">// value in msats</span>370 <span class="k">"created_at"</span>: <span class="c">unixtimestamp</span>, <span class="c">// creation time</span>371 <span class="k">"expires_at"</span>: <span class="c">unixtimestamp</span>, <span class="c">// expiration, optional if not applicable</span>372 <span class="k">"settled_at"</span>: <span class="c">unixtimestamp</span>, <span class="c">// settlement time, optional if unpaid</span>373 <span class="k">"metadata"</span>: {} <span class="c">// generic metadata</span>374 }375}</pre>376 </div>377 <p>Errors: <code>NOT_FOUND</code>.</p>378379 <h3>get_balance</h3>380 <p>Returns the wallet balance. The request takes an empty <code>params</code> object.</p>381 <div class="crt">382 <div class="titlebar">get_balance - request / response</div>383<pre>{ <span class="k">"method"</span>: <span class="s">"get_balance"</span>, <span class="k">"params"</span>: {} }384<span class="c">---</span>385{386 <span class="k">"result_type"</span>: <span class="s">"get_balance"</span>,387 <span class="k">"result"</span>: {388 <span class="k">"balance"</span>: 10000 <span class="c">// user's balance in msats</span>389 }390}</pre>391 </div>392393 <h3>get_info</h3>394 <p>Returns node and connection information. The request takes an empty <code>params</code> object.</p>395 <div class="crt">396 <div class="titlebar">get_info - request / response</div>397<pre>{ <span class="k">"method"</span>: <span class="s">"get_info"</span>, <span class="k">"params"</span>: {} }398<span class="c">---</span>399{400 <span class="k">"result_type"</span>: <span class="s">"get_info"</span>,401 <span class="k">"result"</span>: {402 <span class="k">"alias"</span>: <span class="s">"string"</span>,403 <span class="k">"color"</span>: <span class="s">"hex string"</span>,404 <span class="k">"pubkey"</span>: <span class="s">"hex string"</span>,405 <span class="k">"network"</span>: <span class="s">"string"</span>, <span class="c">// mainnet, testnet, signet, or regtest</span>406 <span class="k">"block_height"</span>: 1,407 <span class="k">"block_hash"</span>: <span class="s">"hex string"</span>,408 <span class="k">"methods"</span>: [<span class="s">"pay_invoice"</span>, <span class="s">"get_balance"</span>, <span class="s">"make_invoice"</span>,409 <span class="s">"lookup_invoice"</span>, <span class="s">"get_info"</span>], <span class="c">// methods for this connection</span>410 <span class="k">"extensions"</span>: [<span class="s">"02"</span>, <span class="s">"03"</span>, <span class="s">"04"</span>, <span class="s">"06"</span>, <span class="s">"07"</span>, <span class="s">"08"</span>]411 <span class="c">// supported NWC extension specs for this connection, optional</span>412 }413}</pre>414 </div>415416 <h2 class="bar" id="encryption"><span class="num">7</span> Encryption</h2>417 <p>The initial version of NWC used <a href="04.html">NIP-04</a> encryption. NIP-04 is deprecated and replaced by <a href="44.html">NIP-44</a>. Always prefer NIP-44. Legacy wallet services or clients may not have migrated yet. Both sides negotiate the scheme through the <code>encryption</code> tag on the info event.</p>418 <div class="tscroll"><table class="ref">419 <caption>Encryption codes</caption>420 <tr><th>Encryption code</th><th>Use</th><th>Notes</th></tr>421 <tr><td class="btn">nip44_v2</td><td><a href="44.html">NIP-44</a></td><td>Required.</td></tr>422 <tr><td class="btn">nip04</td><td><a href="04.html">NIP-04</a></td><td>Deprecated. Present only for backward compatibility.</td></tr>423 <tr><td><span class="kbd"><not present></span></td><td><a href="04.html">NIP-04</a></td><td>Deprecated. Present only for backward compatibility.</td></tr>424 </table></div>425 <p>Negotiation has two steps:</p>426 <ol>427 <li>The wallet service puts an <code>encryption</code> tag in the info event. The tag holds a space-separated list of schemes the service supports, for example <code>nip44_v2 nip04</code>.</li>428 <li>The client puts an <code>encryption</code> tag in each request event. The tag names the single scheme used for that request. The client should always prefer NIP-44 when the wallet service supports it.</li>429 </ol>430 <p>When a client establishes a connection, it reads the info event and looks for the <code>encryption</code> tag. <strong>Absence of the tag means the wallet supports only NIP-04.</strong> If the tag is present, the client picks the best scheme that both sides support. That should always be NIP-44 when possible.</p>431 <p>The scheme named in a request MUST be one the wallet service advertised. If the wallet service does not support the requested scheme, it returns an <code>UNSUPPORTED_ENCRYPTION</code> error. A request without an <code>encryption</code> tag means NIP-04.</p>432433 <figure class="diagram">434 <div class="frame">435 <svg viewBox="0 0 560 180" role="img" aria-label="Decision flow: read info event; if encryption tag lists nip44_v2 use NIP-44, otherwise fall back to NIP-04">436 <rect class="d-actor" x="10" y="65" width="140" height="50" rx="8"/>437 <text class="d-actor-label" x="80" y="87" text-anchor="middle">Read info event</text>438 <text class="d-note" x="80" y="104" text-anchor="middle">kind 13194</text>439 <rect class="d-actor" x="215" y="65" width="150" height="50" rx="8"/>440 <text class="d-actor-label" x="290" y="87" text-anchor="middle">encryption tag?</text>441 <text class="d-note" x="290" y="104" text-anchor="middle">space-separated list</text>442 <rect class="d-actor" x="430" y="15" width="120" height="50" rx="8"/>443 <text class="d-actor-label" x="490" y="37" text-anchor="middle">nip44_v2</text>444 <text class="d-note" x="490" y="54" text-anchor="middle">preferred</text>445 <rect class="d-actor" x="430" y="115" width="120" height="50" rx="8"/>446 <text class="d-actor-label" x="490" y="137" text-anchor="middle">nip04</text>447 <text class="d-note" x="490" y="154" text-anchor="middle">legacy fallback</text>448 <path class="d-msg d-flow" d="M152,90 L212,90"/>449 <path class="d-msg d-flow" d="M367,80 C395,70 400,55 427,45"/>450 <path class="d-msg blue" d="M367,100 C395,110 400,125 427,135"/>451 <text class="d-label" x="400" y="35" text-anchor="middle">lists nip44_v2</text>452 <text class="d-label" x="398" y="150" text-anchor="middle">absent / nip04 only</text>453 </svg>454 </div>455 <figcaption>Fig. 47-2 — <b>Encryption negotiation.</b> The client reads the info event and picks the best shared scheme. No tag means NIP-04 only.</figcaption>456 </figure>457458 <h2 class="bar" id="extensions"><span class="num">8</span> Extensions</h2>459 <p>This NIP defines the core protocol and a small common command set. Optional NWC extension specifications MAY define additional methods, metadata conventions, pairing flows, and more granular authorization behavior. The extensions live in the dedicated NWC repository at <code>https://github.com/nostr-wallet-connect/nwc</code>. That repository reserves <code>01.md</code> for the NWC core specification, which corresponds to this simplified NIP-47. Optional features start at <code>02.md</code>.</p>460 <div class="box tech">461 <span class="tag">Tech Note</span>462 <p>Earlier versions of this NIP defined notification events (kind 23196, later 23197 for NIP-44) and extra commands such as <code>multi_pay_invoice</code>, <code>pay_keysend</code>, and <code>list_transactions</code>. The 2026-08 simplification moved all of them out to the NWC extensions repository. The current core spec defines no notification kinds. Deployed wallet services may still emit the legacy notification kinds; consult the extension specs to handle them.</p>463 </div>464465 <h2 class="bar" id="relays"><span class="num">9</span> Relay Choice</h2>466 <p>This NIP sets no requirements on the type of relay. If the user relies on a custodial service, a relay hosted by that custodian can make sense. The relay may then enforce authentication and so prevent metadata leaks. Skipping the third-party relay also improves reliability in that setup.</p>467468 <h2 class="bar" id="examples"><span class="num">10</span> Worked Example</h2>469 <p>The user scans the QR code from the wallet service, follows a <code>nostr+walletconnect://</code> deeplink, or types the connection details by hand. Then a payment runs in three steps:</p>470 <ol>471 <li>The client sends a kind 23194 event to the wallet service. The content is a <code>pay_invoice</code> request. The signing key is the <code>secret</code> from the connection string.</li>472 <li>The wallet service verifies that the author's key may perform the payment, decrypts the payload, and sends the payment.</li>473 <li>The wallet service answers with a kind 23195 event. The content holds either an error or a preimage.</li>474 </ol>475476 <figure class="diagram">477 <div class="frame">478 <svg viewBox="0 0 560 290" role="img" aria-label="Sequence diagram: client fetches the info event, sends an encrypted pay_invoice request, and receives the encrypted response with the preimage">479 <line class="d-wire" x1="110" y1="40" x2="110" y2="275" stroke-dasharray="3 4"/>480 <line class="d-wire" x1="450" y1="40" x2="450" y2="275" stroke-dasharray="3 4"/>481 <rect class="d-actor" x="55" y="8" width="110" height="30" rx="6"/>482 <text class="d-actor-label" x="110" y="28" text-anchor="middle">Client</text>483 <rect class="d-actor" x="395" y="8" width="110" height="30" rx="6"/>484 <text class="d-actor-label" x="450" y="28" text-anchor="middle">Relay + service</text>485 <path class="d-msg" d="M110,62 L445,62"/>486 <text class="d-label" x="280" y="55" text-anchor="middle">["REQ", "nwc", {"kinds":[13194], "authors":[svc]}]</text>487 <path class="d-msg blue" d="M450,95 L115,95"/>488 <text class="d-label" x="280" y="88" text-anchor="middle">info event: capabilities + encryption modes</text>489 <path class="d-msg" d="M110,140 L445,140"/>490 <text class="d-label" x="280" y="133" text-anchor="middle">["EVENT", {kind:23194, pay_invoice, encrypted}]</text>491 <path class="d-msg blue" d="M450,173 L115,173"/>492 <text class="d-label" x="280" y="166" text-anchor="middle">["OK", <id>, true, ""]</text>493 <path class="d-msg blue d-flow" d="M450,225 L115,225"/>494 <text class="d-label" x="280" y="218" text-anchor="middle">{kind:23195, result: preimage, encrypted}</text>495 <text class="d-note" x="280" y="258" text-anchor="middle">The service pays the invoice between OK and the 23195 response.</text>496 <circle r="4" fill="#f26a00">497 <animateMotion dur="2.6s" repeatCount="indefinite" path="M110,140 L445,140"/>498 </circle>499 </svg>500 </div>501 <figcaption>Fig. 47-3 — <b>Pay-invoice flow.</b> Info lookup, encrypted request, encrypted response with the preimage.</figcaption>502 </figure>503504 <div class="crt">505 <div class="titlebar">Session transcript - relay from the connection URI</div>506<pre><span class="c">-- 1. client discovers capabilities --</span>507<span class="send">→ ["REQ", "nwc-info", {"kinds":[13194], "authors":["c04ccd5c…"]}]</span>508<span class="recv">← ["EVENT", "nwc-info", {"kind":13194,509 "tags":[["encryption","nip44_v2 nip04"],["extensions","02 03 04 06 07 08"]],510 "content":"pay_invoice get_balance get_info make_invoice lookup_invoice", …}]</span>511<span class="recv">← ["EOSE", "nwc-info"]</span>512513<span class="c">-- 2. client pays, signing with the URI secret --</span>514<span class="send">→ ["EVENT", {"kind":23194, "pubkey":"b8e6bf46…",515 "tags":[["encryption","nip44_v2"],["p","c04ccd5c…"]],516 "content":"<nip44: {"method":"pay_invoice",517 "params":{"invoice":"lnbc50n1…"}}>", …}]</span>518<span class="recv">← ["OK", "df467db0…", true, ""]</span>519520<span class="c">-- 3. wallet service responds --</span>521<span class="recv">← ["EVENT", "nwc-sub", {"kind":23195, "pubkey":"c04ccd5c…",522 "tags":[["p","b8e6bf46…"],["e","df467db0…"]],523 "content":"<nip44: {"result_type":"pay_invoice", "error":null,524 "result":{"preimage":"0123456789abcdef…","fees_paid":4000}}>", …}]</span>525526<span class="c">-- a failure instead carries an error object --</span>527<span class="c"> {"result_type":"pay_invoice", "result":null,528 "error":{"code":"INSUFFICIENT_BALANCE",529 "message":"not enough funds to cover fee reserve"}}</span></pre>530 </div>531532 <p>A full published info event, from the spec appendix:</p>533 <div class="crt">534 <div class="titlebar">Appendix - example info event</div>535<pre>{536 <span class="k">"id"</span>: <span class="s">"df467db0a9f9ec77ffe6f561811714ccaa2e26051c20f58f33c3d66d6c2b4d1c"</span>,537 <span class="k">"pubkey"</span>: <span class="s">"c04ccd5c82fc1ea3499b9c6a5c0a7ab627fbe00a0116110d4c750faeaecba1e2"</span>,538 <span class="k">"created_at"</span>: 1713883677,539 <span class="k">"kind"</span>: 13194,540 <span class="k">"tags"</span>: [541 [<span class="s">"encryption"</span>, <span class="s">"nip44_v2 nip04"</span>],542 [<span class="s">"extensions"</span>, <span class="s">"02 03 04 06 07 08"</span>]543 ],544 <span class="k">"content"</span>: <span class="s">"pay_invoice get_balance get_info make_invoice lookup_invoice"</span>,545 <span class="k">"sig"</span>: <span class="s">"31f57b369459b5306a5353aa9e03be7fbde169bc881c3233625605dd12f53548179def16b9fe1137e6465d7e4d5bb27ce81fd6e75908c46b06269f4233c845d8"</span>546}</pre>547 </div>548549 <h2 class="bar" id="history"><span class="num">11</span> Version History</h2>550 <p>NIP-47 started as a hackathon-era idea for one-tap zaps. It grew many commands, then slimmed back down to a small core plus extensions. Notable changes, newest first:</p>551 <ul class="patchnotes">552 <li><span class="ver">2026</span><span class="when">2026-08</span>Core spec simplified to five commands. Notifications, keysend, multi-pay, and transaction listing moved to the optional NWC extensions repository. The <code>extensions</code> tag arrived.</li>553 <li><span class="ver">2026</span><span class="when">2026-02</span>Hold-invoice support added the <code>accepted</code> state to <code>lookup_invoice</code>; the spec text was restructured.</li>554 <li><span class="ver">2025</span><span class="when">2025-10</span>Deep-link pairing (<code>nostr+walletconnect://</code> as a handled URI) documented.</li>555 <li><span class="ver">2025</span><span class="when">2025-09</span><code>metadata</code> params added to <code>make_invoice</code> and payment commands; zap metadata usage clarified; <code>make_invoice</code> <code>state</code> marked optional.</li>556 <li><span class="ver">2025</span><span class="when">2025-07</span>The <code>encryption</code> tag arrived to negotiate the upgrade from <a href="04.html">NIP-04</a> to <a href="44.html">NIP-44</a>; transactions gained a <code>state</code> field.</li>557 <li><span class="ver">2024</span><span class="when">2024-06</span>Notification events added (moved out again in 2026).</li>558 <li><span class="ver">2023</span><span class="when">2023-12</span>The extensions concept first proposed.</li>559 <li><span class="ver">2023</span><span class="when">2023-05</span><code>lud16</code> parameter added to the connection string.</li>560 <li class="minor"><span class="ver">2023</span><span class="when">2023-04</span><code>PAYMENT_FAILED</code> error code added; early feedback rounds and typo fixes.</li>561 <li><span class="ver">Origin</span><span class="when">2023-03-30</span>Semisol publishes "NIP-47 Wallet Connect".</li>562 </ul>563564 <div class="credits">565 <h3 class="cardhead">Original Proposal - Credits</h3>566 <table>567 <tr><td>Author</td><td>Semisol</td></tr>568 <tr><td>First commit</td><td>2023-03-30 — “NIP-47 Wallet Connect”</td></tr>569 <tr><td>Status</td><td>draft - optional — wallets and payment-capable clients implement it</td></tr>570 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/47.md">47.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/47.md">full change history</a></td></tr>571 </table>572 </div>573574 <h2 class="bar"><span class="num">12</span> See Also</h2>575 <ul>576 <li><a href="01.html">NIP-01</a> — base event format, replaceable and ephemeral storage classes used by kinds 13194, 23194, and 23195.</li>577 <li><a href="44.html">NIP-44</a> — the required encryption scheme for request and response payloads.</li>578 <li><a href="04.html">NIP-04</a> — the deprecated legacy encryption scheme kept for backward compatibility.</li>579 <li><a href="57.html">NIP-57</a> — lightning zaps; the main consumer of <code>pay_invoice</code> and its zap metadata.</li>580 <li><a href="46.html">NIP-46</a> — remote signing; the same remote-delegation pattern applied to keys instead of wallets.</li>581 </ul>582 </div>583584 <aside>585 <div class="cartridge">586 <div class="inner">587 <h3 class="cardhead">Spec Card</h3>588 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>589 <dl>590 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>591 <dt>Defines</dt><dd>connection URI - 5 wallet commands - error codes - encryption negotiation</dd>592 <dt>Kinds</dt><dd>13194 (info, replaceable) - 23194/23195 (request/response, ephemeral)</dd>593 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="44.html">NIP-44</a> (<a href="04.html">NIP-04</a> legacy)</dd>594 <dt>Transport</dt><dd>relay WebSocket - E2E-encrypted events</dd>595 <dt>Crypto</dt><dd>NIP-44 v2 - per-connection secp256k1 keys</dd>596 <dt>First commit</dt><dd>2023-03-30</dd>597 <dt>Last updated</dt><dd>2026-08-01</dd>598 </dl>599 </div>600 </div>601 </aside>602 </main>603604 <nav class="navbtns">605 <a href="46.html">◀ NIP-46: Nostr Remote Signing</a>606 <a href="48.html">NIP-48: Bridged Events ▶</a>607 </nav>608609 <footer class="pagefoot">610 <a href="../index.html">Nostr Protocol Manual</a>611 <span class="pageno">NIP 47 / F4</span>612 </footer>613</div>614615<script src="../assets/webmcp.js" defer></script>616</body>617</html>