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-89 - Recommended Application Handlers — Nostr Instruction Manual</title>7<meta name="description" content="NIP-89: recommended application handlers. Kinds 31989 and 31990 let clients discover which application can open an unknown event kind. Complete manual page.">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 89 - Discovery</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="89">89</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 89</div>50 <h1>Recommended Application Handlers</h1>51 <p class="sub">A yellow-pages system for event kinds: when your client meets an event it cannot open, it finds an app that can — recommended by people you follow.</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>Nostr has thousands of event kinds. No single client implements all of them. A social client that meets a music-track event, a poll, or a wiki page needs a way to hand that event to a program that understands it. This NIP defines that hand-off.</p>60 <p>It uses two addressable event kinds. A <strong>handler information</strong> event (<code>kind:31990</code>) is published by an application. It declares which event kinds the application handles and how to open an entity in it. A <strong>recommendation</strong> event (<code>kind:31989</code>) is published by a user. It says: for this event kind, I recommend these applications.</p>61 <p>Discovery then rides on the social graph. When a client meets an unknown kind, it queries for recommendations from the user and the user's follows. Trust in the handler comes from trust in the recommenders, not from an app store.</p>62 <p>Three actors take part in the workflow:</p>63 <ul>64 <li><strong>The application</strong> publishes <code>kind:31990</code> with its redirect rules. It does not have to be a separate entity; it can share a pubkey with user A.</li>65 <li><strong>User A</strong> recommends an app for a specific event kind. User A publishes <code>kind:31989</code>.</li>66 <li><strong>User B</strong> seeks a recommendation. User B queries for <code>kind:31989</code> and then, from the results, queries for <code>kind:31990</code>.</li>67 </ul>6869 <figure class="diagram">70 <div class="frame">71 <svg viewBox="0 0 560 210" role="img" aria-label="An app publishes kind 31990, user A publishes kind 31989, user B queries the relay for both">72 <rect class="d-actor" x="225" y="12" width="110" height="44" rx="8"/>73 <text class="d-actor-label" x="280" y="39" text-anchor="middle">App</text>74 <rect class="d-actor" x="225" y="130" width="110" height="44" rx="8"/>75 <text class="d-actor-label" x="280" y="157" text-anchor="middle">Relay</text>76 <rect class="d-actor" x="10" y="127" width="110" height="50" rx="8"/>77 <text class="d-actor-label" x="65" y="156" text-anchor="middle">User A</text>78 <rect class="d-actor" x="440" y="127" width="110" height="50" rx="8"/>79 <text class="d-actor-label" x="495" y="156" text-anchor="middle">User B</text>80 <path class="d-msg d-flow" d="M280,58 L280,127"/>81 <text class="d-label" x="292" y="96">kind:31990</text>82 <path class="d-msg d-flow" d="M122,150 L222,150"/>83 <text class="d-label" x="172" y="142" text-anchor="middle">kind:31989</text>84 <path class="d-msg blue d-flow" d="M337,152 L437,152"/>85 <text class="d-label" x="387" y="144" text-anchor="middle">REQ / EVENT</text>86 <circle r="4" fill="#f26a00">87 <animateMotion dur="2.6s" repeatCount="indefinite" path="M122,150 L222,150"/>88 </circle>89 <circle r="4" fill="#2e5f9e">90 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M337,152 L437,152"/>91 </circle>92 </svg>93 </div>94 <figcaption>Fig. 89-1 — <b>Three actors.</b> The app declares itself, user A recommends it, user B discovers both through relays.</figcaption>95 </figure>9697 <div class="box hint">98 <span class="tag">Hint!</span>99 <p>Both kinds sit in the 30000-39999 range, so they are addressable events (<a href="01.html">NIP-01</a>). A relay keeps only the latest event per pubkey + kind + <code>d</code>-tag value. A user updates a recommendation by publishing a newer event with the same <code>d</code> value.</p>100 </div>101102 <h2 class="bar" id="spec"><span class="num">2</span> Recommendation Event — kind:31989</h2>103 <p>User A publishes this event to recommend applications for one event kind:</p>104105 <div class="crt">106 <div class="titlebar">Wire format - kind:31989 recommendation</div>107<pre>{108 <span class="k">"kind"</span>: 31989,109 <span class="k">"pubkey"</span>: <span class="c"><recommender-user-pubkey></span>,110 <span class="k">"tags"</span>: [111 [<span class="s">"d"</span>, <span class="c"><supported-event-kind></span>],112 [<span class="s">"a"</span>, <span class="s">"31990:app1-pubkey:<d-identifier>"</span>, <span class="s">"wss://relay1"</span>, <span class="s">"ios"</span>],113 [<span class="s">"a"</span>, <span class="s">"31990:app2-pubkey:<d-identifier>"</span>, <span class="s">"wss://relay2"</span>, <span class="s">"web"</span>]114 ]115 <span class="c">// other fields...</span>116}</pre>117 </div>118119 <div class="tscroll"><table class="ref">120 <caption>Tags in kind:31989</caption>121 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>122 <tr><td class="btn">d</td><td>event kind, as a string</td><td>The event kind this recommendation covers, for example <code>"31337"</code>.</td></tr>123 <tr><td class="btn">a</td><td><code>31990:<app pubkey>:<d identifier></code></td><td>Address of one recommended handler event. Multiple <code>a</code> tags can appear on the same event. The next value SHOULD be a relay hint. The value after that SHOULD be the platform where the recommendation applies (for example <code>web</code>, <code>ios</code>).</td></tr>124 </table></div>125126 <h2 class="bar" id="handler"><span class="num">3</span> Handler Information — kind:31990</h2>127 <p>The application publishes this event. It lists the event kinds the app supports and templates for opening entities in the app:</p>128129 <div class="crt">130 <div class="titlebar">Wire format - kind:31990 handler information</div>131<pre>{132 <span class="k">"kind"</span>: 31990,133 <span class="k">"pubkey"</span>: <span class="c"><application-pubkey></span>,134 <span class="k">"content"</span>: <span class="c"><optional-kind:0-style-metadata></span>,135 <span class="k">"tags"</span>: [136 [<span class="s">"d"</span>, <span class="c"><random-id></span>],137 [<span class="s">"k"</span>, <span class="c"><supported-event-kind></span>],138 [<span class="s">"latest"</span>, <span class="s">"35128:<application-pubkey>:<site-d-identifier>"</span>, <span class="s">"wss://relay1"</span>],139 [<span class="s">"next"</span>, <span class="s">"35128:<application-pubkey>:<next-site-d-identifier>"</span>, <span class="s">"wss://relay1"</span>],140 [<span class="s">"web"</span>, <span class="s">"https://..../a/<bech32>"</span>, <span class="s">"nevent"</span>],141 [<span class="s">"web"</span>, <span class="s">"https://..../p/<bech32>"</span>, <span class="s">"nprofile"</span>],142 [<span class="s">"web"</span>, <span class="s">"https://..../e/<bech32>"</span>],143 [<span class="s">"ios"</span>, <span class="s">".../<bech32>"</span>]144 ]145 <span class="c">// other fields...</span>146}</pre>147 </div>148149 <div class="tscroll"><table class="ref">150 <caption>Fields and tags in kind:31990</caption>151 <tr><th>Item</th><th>Rule</th></tr>152 <tr><td class="btn">content</td><td>Optional stringified JSON metadata object, in the <code>kind:0</code> style of <a href="01.html">NIP-01</a>. Use it when the publishing pubkey is not itself an application. If <code>content</code> is empty, clients use the pubkey's own <code>kind:0</code> for display data (name, picture, web, LUD16, and so on).</td></tr>153 <tr><td class="btn">d</td><td>A random identifier. Unlike <code>kind:31989</code>, it does not carry the supported kind.</td></tr>154 <tr><td class="btn">k</td><td>One supported event kind. Repeat the tag for each supported kind that shares the same handler URLs. The same pubkey can also publish multiple <code>kind:31990</code> events for different apps that handle the same event kind.</td></tr>155 <tr><td class="btn">latest</td><td>SHOULD appear on web applications when applicable. Same shape as an <code>a</code> tag: value <code><kind>:<pubkey>:<d-tag></code>, then an optional relay hint. References the current <code>kind:35128</code> site manifest (<a href="5a.html">NIP-5A</a>) for the application.</td></tr>156 <tr><td class="btn">next</td><td>Same shape as <code>latest</code>. References the next site manifest intended for rollout or upgrade.</td></tr>157 <tr><td class="btn">web / ios / ...</td><td>Platform handler tags. The value is a URL template. <code><bech32></code> in the template MUST be replaced by the client with the <a href="19.html">NIP-19</a>-encoded entity to load.</td></tr>158 </table></div>159160 <p>An app can register multiple platform tags. A tag can carry a <a href="19.html">NIP-19</a> entity name (for example <code>nevent</code>, <code>nprofile</code>) to scope the template to that entity type. A platform tag without an entity name SHOULD be treated as the generic handler for any NIP-19 entity that no other tag handles.</p>161 <p>App descriptor events SHOULD tag or otherwise reference their related site manifest events when applicable. Clients can then discover the deployed site manifest for an application directly from the upstream app descriptor.</p>162163 <div class="box tech">164 <span class="tag">Tech Note</span>165 <p>The source counts tag positions inconsistently. It calls the NIP-19 entity name "the second value of the array", and the relay hint in <code>kind:31989</code> <code>a</code> tags likewise "the second value" — but in every example these sit in the third array slot, after the tag name and the primary value. Read the positions from the examples, as this page does, not from the prose.</p>166 </div>167168 <h2 class="bar" id="clienttag"><span class="num">4</span> The client Tag</h2>169 <p>When a client publishes an event on behalf of a user, it MAY add a <code>client</code> tag. The tag identifies the client that published the note. It is a tuple of a <code>name</code>, an <code>address</code> that identifies a handler event, and a relay <code>hint</code> for finding that handler event:</p>170171 <div class="crt">172 <div class="titlebar">Example - client tag on a kind:1 note</div>173<pre>{174 <span class="k">"kind"</span>: 1,175 <span class="k">"tags"</span>: [176 [<span class="s">"client"</span>, <span class="s">"My Client"</span>, <span class="s">"31990:app1-pubkey:<d-identifier>"</span>, <span class="s">"wss://relay1"</span>]177 ]178 <span class="c">// other fields...</span>179}</pre>180 </div>181182 <div class="box warn">183 <span class="tag">Warning!</span>184 <p>The <code>client</code> tag has privacy implications: it reveals which software a user runs on every event. Clients SHOULD let users opt out of the tag.</p>185 </div>186187 <h2 class="bar" id="flow"><span class="num">5</span> Discovery Flow</h2>188 <p>The end-to-end flow works like this. User A uses a nostr app that is not centered on <code>kind:1</code>. User A chooses to announce and recommend that app as a handler for its kind.</p>189 <p>Later, user B sees an event of an unknown kind in a social-media-centric client. The client still lets user B interact with the event, for example by tapping on it. The client may then query for handlers recommended by the user and by the user's follows.</p>190191 <figure class="diagram">192 <div class="frame">193 <svg viewBox="0 0 560 300" role="img" aria-label="Sequence diagram: client requests kind 31989 recommendations, receives one, requests the addressed kind 31990 handler, receives it, then opens the handler URL">194 <line class="d-wire" x1="110" y1="40" x2="110" y2="285" stroke-dasharray="3 4"/>195 <line class="d-wire" x1="450" y1="40" x2="450" y2="285" stroke-dasharray="3 4"/>196 <rect class="d-actor" x="55" y="8" width="110" height="30" rx="6"/>197 <text class="d-actor-label" x="110" y="28" text-anchor="middle">User B client</text>198 <rect class="d-actor" x="395" y="8" width="110" height="30" rx="6"/>199 <text class="d-actor-label" x="450" y="28" text-anchor="middle">Relay</text>200 <path class="d-msg" d="M110,62 L445,62"/>201 <text class="d-label" x="280" y="55" text-anchor="middle">["REQ", …, {"kinds":[31989], "#d":["31337"], "authors":[follows]}]</text>202 <path class="d-msg blue" d="M450,102 L115,102"/>203 <text class="d-label" x="280" y="95" text-anchor="middle">["EVENT", …, {kind:31989, "a" → handler}]</text>204 <path class="d-msg" d="M110,142 L445,142"/>205 <text class="d-label" x="280" y="135" text-anchor="middle">["REQ", …, fetch the a-tagged kind:31990]</text>206 <path class="d-msg blue" d="M450,182 L115,182"/>207 <text class="d-label" x="280" y="175" text-anchor="middle">["EVENT", …, {kind:31990, "web" URL template}]</text>208 <path class="d-msg d-flow" d="M110,222 L110,258"/>209 <text class="d-note" x="280" y="245" text-anchor="middle">Client replaces <bech32> in the template and opens the handler URL.</text>210 </svg>211 </div>212 <figcaption>Fig. 89-2 — <b>Handler discovery.</b> Two lookups: recommendations first, then the handler event they address.</figcaption>213 </figure>214215 <h3>Alternative query, bypassing kind:31989</h3>216 <p>A client can also skip the recommendation step. It queries directly for <code>kind:31990</code> events whose <code>k</code> tag matches the unknown kind:</p>217 <div class="crt">218 <div class="titlebar">Direct handler query</div>219<pre><span class="send">→ ["REQ", <span class="c"><id></span>, { "kinds": [31990], "#k": [<span class="c"><desired-event-kind></span>], "authors": [...] }]</span></pre>220 </div>221 <div class="box danger">222 <span class="tag">Warning!</span>223 <p>The direct query drops the social-graph filter. Any pubkey can publish a <code>kind:31990</code> event. Clients SHOULD apply spam-prevention mechanisms or query high-quality restricted relays, so they do not direct users to malicious handlers.</p>224 </div>225226 <h2 class="bar" id="examples"><span class="num">6</span> Worked Example</h2>227 <p>User A uses Zapstr, a client centered on <code>kind:31337</code> track events. With Zapstr, user A publishes a recommendation of Zapstr as a <code>kind:31337</code> handler:</p>228229 <div class="crt">230 <div class="titlebar">User A recommends a kind:31337 handler</div>231<pre>{232 <span class="k">"kind"</span>: 31989,233 <span class="k">"tags"</span>: [234 [<span class="s">"d"</span>, <span class="s">"31337"</span>],235 [<span class="s">"a"</span>, <span class="s">"31990:1743058db7078661b94aaf4286429d97ee5257d14a86d6bfa54cb0482b876fb0:abcd"</span>,236 <span class="c"><relay-url></span>, <span class="s">"web"</span>]237 ]238 <span class="c">// other fields...</span>239}</pre>240 </div>241242 <p>User B follows user A. In their timeline, user B sees a <code>kind:1</code> note that tags a <code>kind:31337</code> event. User B's client does not know how to handle <code>kind:31337</code>, so it displays the event with its <code>alt</code> tag (<a href="31.html">NIP-31</a>). When user B clicks the event, the client queries for a handler:</p>243244 <div class="crt">245 <div class="titlebar">Session transcript - handler lookup</div>246<pre><span class="c">-- find recommendations from user B and their follows --</span>247<span class="send">→ ["REQ", <span class="c"><id></span>, { "kinds": [31989], "#d": ["31337"],248 "authors": [<span class="c"><user></span>, <span class="c"><users-contact-list></span>] }]</span>249<span class="recv">← ["EVENT", <span class="c"><id></span>, {"kind":31989, "tags":[["d","31337"],250 ["a","31990:1743058d…76fb0:abcd", "wss://relay1", "web"]], …}]</span>251<span class="recv">← ["EOSE", <span class="c"><id></span>]</span>252253<span class="c">-- fetch the a-tagged handler event --</span>254<span class="send">→ ["REQ", <span class="c"><id2></span>, { "kinds": [31990],255 "authors": ["1743058d…76fb0"], "#d": ["abcd"] }]</span>256<span class="recv">← ["EVENT", <span class="c"><id2></span>, {"kind":31990, "tags":[["d","abcd"],["k","31337"],257 ["web","https://zapstr.example/track/<bech32>","nevent"]], …}]</span></pre>258 </div>259260 <p>User B's client reads the app and handler information from the <code>a</code>-tagged <code>kind:31990</code> event. It replaces <code><bech32></code> in the URL template with the <a href="19.html">NIP-19</a> <code>nevent</code> for the track. Then it redirects user B to that URL.</p>261262 <div class="box tech">263 <span class="tag">Tech Note</span>264 <p>The fallback display in this flow depends on the <code>alt</code> tag from <a href="31.html">NIP-31</a>, which this manual marks as deprecated. The source text still cites it. Treat <code>alt</code> as a best-effort fallback only. The source also says the client "MIGHT" query the user's and follows' handlers — a non-standard keyword; read it as MAY.</p>265 </div>266267 <h2 class="bar" id="history"><span class="num">7</span> Version History</h2>268 <p>NIP-89 arrived in mid-2023 and has stayed close to its original shape. The client tag and the site-manifest links came later. Notable changes, newest first:</p>269 <ul class="patchnotes">270 <li><span class="ver">2026</span><span class="when">2026-06</span>Added <code>latest</code> and <code>next</code> tags linking handler events to <code>kind:35128</code> site manifests (<a href="5a.html">NIP-5A</a>).</li>271 <li class="minor"><span class="ver">2024</span><span class="when">2024-06</span>Fixed the <code>REQ</code> examples; assorted JSON and heading formatting fixes through 2026.</li>272 <li><span class="ver">2023</span><span class="when">2023-11</span>Made the <code>client</code> tag backwards compatible: the human-readable name moved to the first value, before the handler address.</li>273 <li><span class="ver">2023</span><span class="when">2023-11</span>Added the <code>client</code> tag for identifying the publishing client.</li>274 <li class="minor"><span class="ver">2023</span><span class="when">2023-08</span>Reworded <code>set_metadata</code> to <code>metadata</code>; removed NIP-33 references after its merge into <a href="01.html">NIP-01</a>.</li>275 <li><span class="ver">Origin</span><span class="when">2023-06</span>Pablo Fernandez proposed kinds 31989 and 31990 in PR #530.</li>276 </ul>277278 <div class="credits">279 <h3 class="cardhead">Original Proposal - Credits</h3>280 <table>281 <tr><td>Author</td><td>Pablo Fernandez</td></tr>282 <tr><td>First commit</td><td>2023-06-07 — “NIP-89: Recommended Application Handlers (#530)”</td></tr>283 <tr><td>Pull request</td><td><a href="https://github.com/nostr-protocol/nips/pull/530">nostr-protocol/nips #530</a></td></tr>284 <tr><td>Status</td><td>draft - optional</td></tr>285 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/89.md">89.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/89.md">full change history</a></td></tr>286 </table>287 </div>288289 <h2 class="bar"><span class="num">8</span> See Also</h2>290 <ul>291 <li><a href="01.html">NIP-01</a> — addressable events, <code>a</code> tags, filters, and the <code>kind:0</code> metadata shape reused in <code>content</code>.</li>292 <li><a href="19.html">NIP-19</a> — the bech32 entities (<code>nevent</code>, <code>nprofile</code>, …) substituted into handler URL templates.</li>293 <li><a href="31.html">NIP-31</a> — the <code>alt</code> tag used to display unknown kinds before a handler is found (deprecated).</li>294 <li><a href="5a.html">NIP-5A</a> — static websites; the <code>kind:35128</code> site manifests referenced by <code>latest</code> and <code>next</code> tags.</li>295 <li><a href="02.html">NIP-02</a> — the follow list that scopes recommendation queries to trusted authors.</li>296 </ul>297 </div>298299 <aside>300 <div class="cartridge">301 <div class="inner">302 <h3 class="cardhead">Spec Card</h3>303 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>304 <dl>305 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span></dd>306 <dt>Defines</dt><dd>kind 31989 - kind 31990 - client tag - k/latest/next/platform tags</dd>307 <dt>Kinds</dt><dd>31989, 31990 (addressable)</dd>308 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="19.html">NIP-19</a></dd>309 <dt>Related</dt><dd><a href="5a.html">NIP-5A</a> site manifests</dd>310 <dt>First commit</dt><dd>2023-06-07</dd>311 <dt>Last updated</dt><dd>2026-06-16</dd>312 </dl>313 </div>314 </div>315 </aside>316 </main>317318 <nav class="navbtns">319 <a href="88.html">◀ NIP-88: Polls</a>320 <a href="90.html">NIP-90: Data Vending Machine ▶</a>321 </nav>322323 <footer class="pagefoot">324 <a href="../index.html">Nostr Protocol Manual</a>325 <span class="pageno">NIP 89 / F4</span>326 </footer>327</div>328329<script src="../assets/webmcp.js" defer></script>330</body>331</html>