clone

every nostr nip as a chapter of an illustrated, early-2000s printed game manual

main
nip.guide / site / nips / 73.html
73.html 22.4K
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-73 - External Content IDs — Nostr Instruction Manual</title>7<meta name="description" content="NIP-73: external content IDs. The i and k tags that link Nostr events to books, podcasts, movies, papers, URLs, geohashes, countries, hashtags, and blockchains.">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 73 - Interop</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="73">73</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 73</div>50 <h1>External Content IDs</h1>51 <p class="sub">Two small tags that point a Nostr event at a book, a podcast, a movie, a webpage, or a blockchain transaction — so every client can find all events about the same thing.</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>The world already has global content identifiers. A book has an ISBN. A podcast feed has a GUID. A movie has an ISAN. A paper has a DOI. This NIP lets an event reference these identifiers in a standard way.</p>60 <p>An event adds an <code>i</code> tag with the external identifier. It adds a <code>k</code> tag with the identifier <strong>kind</strong>. Relays index single-letter tags (<a href="01.html">NIP-01</a>). So a client can query one filter and get every event about one book, one episode, or one transaction.</p>61 <p>This NIP defines no event kind of its own. Any event kind may carry <code>i</code> and <code>k</code> tags. Comment threads on external content (<a href="22.html">NIP-22</a>) are the main consumer.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of the <code>i</code> tag as a library card catalog number. The <code>k</code> tag names the catalog. Query <code>{"#i": [&lt;id&gt;]}</code> for one item. Query <code>{"#k": [&lt;kind&gt;]}</code> for the whole catalog.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 210" role="img" aria-label="External identifiers from books, podcasts, and blockchains enter an event as i and k tags; the relay indexes them; a client queries with a #i filter">71 <rect class="d-actor" x="10" y="14" width="130" height="38" rx="8"/>72 <text class="d-actor-label" x="75" y="32" text-anchor="middle">Book</text>73 <text class="d-note" x="75" y="46" text-anchor="middle">isbn:97807…</text>74 <rect class="d-actor" x="10" y="66" width="130" height="38" rx="8"/>75 <text class="d-actor-label" x="75" y="84" text-anchor="middle">Podcast</text>76 <text class="d-note" x="75" y="98" text-anchor="middle">podcast:guid:…</text>77 <rect class="d-actor" x="10" y="118" width="130" height="38" rx="8"/>78 <text class="d-actor-label" x="75" y="136" text-anchor="middle">Blockchain</text>79 <text class="d-note" x="75" y="150" text-anchor="middle">bitcoin:tx:…</text>80 <rect class="d-actor" x="215" y="62" width="130" height="50" rx="8"/>81 <text class="d-actor-label" x="280" y="84" text-anchor="middle">Event</text>82 <text class="d-note" x="280" y="100" text-anchor="middle">["i", id] ["k", kind]</text>83 <rect class="d-actor" x="420" y="62" width="130" height="50" rx="8"/>84 <text class="d-actor-label" x="485" y="84" text-anchor="middle">Relay</text>85 <text class="d-note" x="485" y="100" text-anchor="middle">#i / #k index</text>86 <path class="d-msg" d="M142,33 C180,40 185,58 212,72"/>87 <path class="d-msg" d="M142,85 L212,86"/>88 <path class="d-msg" d="M142,137 C180,130 185,112 212,102"/>89 <path class="d-msg d-flow" d="M347,87 L417,87"/>90 <path class="d-msg blue d-flow" d="M420,124 C370,168 300,180 230,180"/>91 <text class="d-label" x="335" y="188" text-anchor="middle">REQ {"#i": ["isbn:97807…"]}</text>92 <circle r="4" fill="#f26a00">93 <animateMotion dur="2.6s" repeatCount="indefinite" path="M347,87 L417,87"/>94 </circle>95 </svg>96 </div>97 <figcaption>Fig. 73-1 — <b>One id, many events.</b> Each event copies the external identifier into an <code>i</code> tag. The relay index makes the identifier queryable.</figcaption>98 </figure>99100 <h2 class="bar" id="spec"><span class="num">2</span> The i and k Tags</h2>101 <p>Use the <code>i</code> tag to reference an external content identifier. Use the <code>k</code> tag to state the identifier kind. The <code>k</code> value is a fixed string per identifier type. It lets clients query all events for a whole type, not just one item.</p>102 <div class="crt">103 <div class="titlebar">Tag shapes</div>104<pre>[<span class="s">"i"</span>, <span class="c">&lt;external id, formatted per §3&gt;</span>, <span class="c">&lt;url hint, optional — see §5&gt;</span>]105[<span class="s">"k"</span>, <span class="c">&lt;external id kind, e.g. "isbn", "web", "podcast:guid"&gt;</span>]</pre>106 </div>107 <p>Both tags are single-letter tags. Relays index their first value (<a href="01.html">NIP-01</a>). Clients query them with the <code>#i</code> and <code>#k</code> filter keys.</p>108109 <h2 class="bar" id="ids"><span class="num">3</span> Supported ID Types</h2>110 <p>The table lists every supported identifier type. The <code>i</code> tag value must follow the exact format shown. The <code>k</code> tag value is the fixed string in the last column.</p>111 <div class="tscroll"><table class="ref">112 <caption>Supported external identifiers</caption>113 <tr><th>Type</th><th><code>i</code> tag value</th><th><code>k</code> tag value</th></tr>114 <tr><td class="btn">URLs</td><td><code>&lt;URL, normalized, no fragment&gt;</code></td><td><code>web</code></td></tr>115 <tr><td class="btn">Books</td><td><code>isbn:&lt;id, without hyphens&gt;</code></td><td><code>isbn</code></td></tr>116 <tr><td class="btn">Geohashes</td><td><code>geo:&lt;geohash, lowercase&gt;</code></td><td><code>geo</code></td></tr>117 <tr><td class="btn">Countries</td><td><code>iso3166:&lt;code, uppercase&gt;</code></td><td><code>iso3166</code></td></tr>118 <tr><td class="btn">Movies</td><td><code>isan:&lt;id, without version part&gt;</code></td><td><code>isan</code></td></tr>119 <tr><td class="btn">Papers</td><td><code>doi:&lt;id, lowercase&gt;</code></td><td><code>doi</code></td></tr>120 <tr><td class="btn">Hashtags</td><td><code>#&lt;topic, lowercase&gt;</code></td><td><code>#</code></td></tr>121 <tr><td class="btn">Podcast Feeds</td><td><code>podcast:guid:&lt;guid&gt;</code></td><td><code>podcast:guid</code></td></tr>122 <tr><td class="btn">Podcast Episodes</td><td><code>podcast:item:guid:&lt;guid&gt;</code></td><td><code>podcast:item:guid</code></td></tr>123 <tr><td class="btn">Podcast Publishers</td><td><code>podcast:publisher:guid:&lt;guid&gt;</code></td><td><code>podcast:publisher:guid</code></td></tr>124 <tr><td class="btn">Blockchain Transaction</td><td><code>&lt;blockchain&gt;:[&lt;chainId&gt;:]tx:&lt;txid, hex, lowercase&gt;</code></td><td><code>&lt;blockchain&gt;:tx</code></td></tr>125 <tr><td class="btn">Blockchain Address</td><td><code>&lt;blockchain&gt;:[&lt;chainId&gt;:]address:&lt;address&gt;</code></td><td><code>&lt;blockchain&gt;:address</code></td></tr>126 </table></div>127128 <div class="box tech">129 <span class="tag">Tech Note</span>130 <p>The source spec says URLs are "normalized" but defines no normalization algorithm. Only "no fragment" is explicit. Two clients may normalize the same URL differently and miss each other's events. Also note: the hashtag form here overlaps with the <code>t</code> tag from <a href="24.html">NIP-24</a>; the spec does not say how the two relate.</p>131 </div>132133 <h3>URLs (webpages)</h3>134 <p>Use the normalized URL with the fragment removed. The <code>k</code> tag is the hardcoded string <code>web</code>. For the page <code>https://myblog.example.com/post/2012-03-27/hello-world</code>, the tags are:</p>135 <div class="crt">136 <div class="titlebar">Webpage reference</div>137<pre>[138 [<span class="s">"i"</span>, <span class="s">"https://myblog.example.com/post/2012-03-27/hello-world"</span>],139 [<span class="s">"k"</span>, <span class="s">"web"</span>]140]</pre>141 </div>142143 <h3>Books (ISBN)</h3>144 <p>A book identifier is <code>isbn:</code> plus the ISBN <strong>without hyphens</strong>. Example: <code>["i", "isbn:9780765382030"]</code>. Many book search APIs return ISBNs without hyphens. Removing hyphens is trivial. Adding them back needs a library. So the hyphen-free form is the canonical one.</p>145146 <h3>Geohashes</h3>147 <p>A geohash encodes a geographic location as a short string of letters and digits. The identifier is <code>geo:</code> plus the geohash. Geohashes must be lowercase. Example: <code>["i", "geo:ezs42e44yx96"]</code>.</p>148149 <h3>Countries and subdivisions (ISO 3166)</h3>150 <p>ISO 3166 codes reference countries (ISO 3166-1 alpha-2) or subdivisions such as states and provinces (ISO 3166-2). The codes must be uppercase.</p>151 <ul>152 <li>Country (Venezuela): <code>["i", "iso3166:VE"]</code></li>153 <li>Subdivision (California, USA): <code>["i", "iso3166:US-CA"]</code></li>154 </ul>155156 <h3>Movies (ISAN)</h3>157 <p>A movie identifier is <code>isan:</code> plus the ISAN <strong>without the version part</strong>. Versions and edits of a movie are not relevant here. Example: <code>["i", "isan:0000-0000-401A-0000-7"]</code>.</p>158159 <h3>Papers (DOI)</h3>160 <p>A paper identifier is <code>doi:</code> plus the DOI in lowercase. The <code>k</code> tag is <code>doi</code>.</p>161162 <h3>Hashtags</h3>163 <p>A hashtag identifier is <code>#</code> plus the topic in lowercase. The <code>k</code> tag is the single character <code>#</code>.</p>164165 <h3>Podcasts</h3>166 <p>Podcast identifiers reuse the GUIDs from the podcast namespace. Three levels exist:</p>167 <ul>168 <li>Feed: <code>["i", "podcast:guid:c90e609a-df1e-596a-bd5e-57bcc8aad6cc"]</code></li>169 <li>Episode (RSS item): <code>["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f"]</code></li>170 <li>Publisher: <code>["i", "podcast:publisher:guid:18bcbf10-6701-4ffb-b255-bc057390d738"]</code></li>171 </ul>172173 <h2 class="bar" id="blockchain"><span class="num">4</span> Blockchain IDs</h2>174 <p><code>&lt;blockchain&gt;</code> can be any layer-1 chain: <code>bitcoin</code>, <code>ethereum</code>, <code>solana</code>, and so on. When one name covers many chains — for example the EVM chains under <code>ethereum</code> — insert a numeric <code>&lt;chainId&gt;</code> segment. The <code>k</code> tag never includes the chain id. It is always <code>&lt;blockchain&gt;:tx</code> or <code>&lt;blockchain&gt;:address</code>.</p>175176 <figure class="diagram">177 <div class="frame">178 <svg viewBox="0 0 560 150" role="img" aria-label="Anatomy of a blockchain identifier: blockchain name, optional chain id, tx or address keyword, then the id itself">179 <text class="d-actor-label" x="10" y="20">Blockchain id anatomy — i tag value</text>180 <rect x="10" y="34" width="110" height="30" rx="4" fill="#f26a00"/>181 <text class="d-label" x="65" y="53" text-anchor="middle" fill="#fff">ethereum</text>182 <rect x="128" y="34" width="80" height="30" rx="4" fill="#7d8794"/>183 <text class="d-label" x="168" y="53" text-anchor="middle" fill="#fff">100</text>184 <rect x="216" y="34" width="110" height="30" rx="4" fill="#2e5f9e"/>185 <text class="d-label" x="271" y="53" text-anchor="middle" fill="#fff">tx</text>186 <rect x="334" y="34" width="216" height="30" rx="4" fill="#2c7a3f"/>187 <text class="d-label" x="442" y="53" text-anchor="middle" fill="#fff">0x98f7812be496…</text>188 <text class="d-note" x="65" y="82" text-anchor="middle">chain name</text>189 <text class="d-note" x="168" y="82" text-anchor="middle">chainId (optional)</text>190 <text class="d-note" x="271" y="82" text-anchor="middle">tx | address</text>191 <text class="d-note" x="442" y="82" text-anchor="middle">txid or address</text>192 <text class="d-note" x="10" y="115">Segments join with ":" — the k tag drops the chainId:</text>193 <text class="d-label" x="10" y="136">["k", "ethereum:tx"]</text>194 </svg>195 </div>196 <figcaption>Fig. 73-2 — <b>Blockchain id segments.</b> The chain id lives only in the <code>i</code> tag. The <code>k</code> tag stays chain-id-free.</figcaption>197 </figure>198199 <h3>Bitcoin</h3>200 <p>A txid is hex and lowercase. An address is bech32 lowercase, or base58 with its case preserved:</p>201 <div class="crt">202 <div class="titlebar">Bitcoin formats and examples</div>203<pre><span class="c">bitcoin:address:&lt;bech32, lowercase | base58, case sensitive&gt;</span>204<span class="c">bitcoin:tx:&lt;txid, hex, lowercase&gt;</span>205206[207 [<span class="s">"i"</span>, <span class="s">"bitcoin:tx:a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"</span>],208 [<span class="s">"k"</span>, <span class="s">"bitcoin:tx"</span>]209]210[211 [<span class="s">"i"</span>, <span class="s">"bitcoin:address:1HQ3Go3ggs8pFnXuHVHRytPCq5fGG8Hbhx"</span>],212 [<span class="s">"k"</span>, <span class="s">"bitcoin:address"</span>]213]</pre>214 </div>215216 <h3>Ethereum and other EVM chains</h3>217 <p>EVM identifiers carry an integer chain id. Transaction hashes and addresses are hex and lowercase — lowercase the checksummed address before you tag it:</p>218 <div class="crt">219 <div class="titlebar">EVM formats and examples</div>220<pre><span class="c">ethereum:&lt;chainId, integer&gt;:tx:&lt;txHash, hex, lowercase&gt;</span>221<span class="c">ethereum:&lt;chainId, integer&gt;:address:&lt;hex, lowercase&gt;</span>222223<span class="c">-- mainnet address (chainId 1) --</span>224[225 [<span class="s">"i"</span>, <span class="s">"ethereum:1:address:0xd8da6bf26964af9d7eed9e03e53415d37aa96045"</span>],226 [<span class="s">"k"</span>, <span class="s">"ethereum:address"</span>]227]228<span class="c">-- Gnosis chain transaction (chainId 100) --</span>229[230 [<span class="s">"i"</span>, <span class="s">"ethereum:100:tx:0x98f7812be496f97f80e2e98d66358d1fc733cf34176a8356d171ea7fbbe97ccd"</span>],231 [<span class="s">"k"</span>, <span class="s">"ethereum:tx"</span>]232]</pre>233 </div>234235 <h2 class="bar" id="hints"><span class="num">5</span> Optional URL Hints</h2>236 <p>Each <code>i</code> tag may carry a URL hint as its second value. The hint sends people to a website when the client has no opinion about how to open the identifier:</p>237 <div class="crt">238 <div class="titlebar">i tags with url hints</div>239<pre>[<span class="s">"i"</span>, <span class="s">"podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f"</span>,240 <span class="s">"https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"</span>]241[<span class="s">"i"</span>, <span class="s">"isan:0000-0000-401A-0000-7"</span>,242 <span class="s">"https://www.imdb.com/title/tt0120737"</span>]</pre>243 </div>244 <div class="box tech">245 <span class="tag">Tech Note</span>246 <p>In the source spec, both URL-hint examples print the hint URL without quote marks. As written they are not valid JSON. The examples above restore the quotes. The intent — a plain URL string as the third tag element — is not in doubt.</p>247 </div>248 <div class="box warn">249 <span class="tag">Warning!</span>250 <p>Only the first tag value is indexed by relays. Do not put data you need to query into the hint. The hint is a courtesy link, nothing more.</p>251 </div>252253 <h2 class="bar" id="examples"><span class="num">6</span> Worked Example</h2>254 <p>A user reviews a book in a kind-1 note. The note tags the ISBN. A second client later queries every event about that book. Orange lines go to the relay. Blue lines come from the relay.</p>255 <div class="crt">256 <div class="titlebar">Session transcript - wss://relay.example.com</div>257<pre><span class="c">-- publish a note about a book --</span>258<span class="send">→ ["EVENT", {"id":"7fe19a3c…","pubkey":"9d2f04b1…","created_at":1749772800,259 "kind":1,260 "tags":[["i","isbn:9780765382030","https://isbnsearch.org/isbn/9780765382030"],261 ["k","isbn"]],262 "content":"Finished this one in two nights. Read it.",263 "sig":"c02b1745…"}]</span>264<span class="recv">← ["OK", "7fe19a3c…", true, ""]</span>265266<span class="c">-- another client asks: all events about this book --</span>267<span class="send">→ ["REQ", "book", {"#i": ["isbn:9780765382030"]}]</span>268<span class="recv">← ["EVENT", "book", {"id":"7fe19a3c…", "kind":1, "content":"Finished this one…", …}]</span>269<span class="recv">← ["EOSE", "book"]</span>270<span class="send">→ ["CLOSE", "book"]</span>271272<span class="c">-- or: every event that references any book --</span>273<span class="send">→ ["REQ", "shelf", {"#k": ["isbn"], "limit": 50}]</span></pre>274 </div>275276 <h2 class="bar" id="history"><span class="num">7</span> Version History</h2>277 <p>Oscar Merry drafted this NIP for podcast apps. It grew into a general identifier registry. Notable changes, newest first:</p>278 <ul class="patchnotes">279 <li><span class="ver">2026</span><span class="when">2026-01</span>Added ISO 3166 country and subdivision codes.</li>280 <li><span class="ver">2025</span><span class="when">2025-04</span>Added blockchain transaction and address identifiers with optional chain ids.</li>281 <li><span class="ver">2025</span><span class="when">2025-03</span>Hardcoded the webpage <code>k</code> tag to the string <code>web</code>.</li>282 <li><span class="ver">2024</span><span class="when">2024-09</span>Expanded the supported id list beyond the original media types.</li>283 <li><span class="ver">2024</span><span class="when">2024-08</span>Adopted the NIP-73 number; added podcast publisher GUIDs.</li>284 <li><span class="ver">2024</span><span class="when">2024-06</span>Added the optional URL hint as a second <code>i</code> tag value.</li>285 <li class="minor"><span class="ver">2024</span><span class="when">2024-05</span>Removed the media type prefix; switched movies to ISAN with real examples.</li>286 <li><span class="ver">Origin</span><span class="when">2024-04</span>Oscar Merry drafted "External Content IDs".</li>287 </ul>288289 <div class="credits">290 <h3 class="cardhead">Original Proposal - Credits</h3>291 <table>292 <tr><td>Author</td><td>Oscar Merry</td></tr>293 <tr><td>First commit</td><td>2024-04-19 — “Draft External Content IDs” (took the number 73 on 2024-08-05)</td></tr>294 <tr><td>Status</td><td>draft - optional</td></tr>295 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/73.md">73.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/73.md">full change history</a></td></tr>296 </table>297 </div>298299 <h2 class="bar"><span class="num">8</span> See Also</h2>300 <ul>301 <li><a href="01.html">NIP-01</a> — single-letter tag indexing; the <code>#i</code> and <code>#k</code> filters build on it.</li>302 <li><a href="22.html">NIP-22</a> — comments; uses these external ids as the scope of a comment thread.</li>303 <li><a href="24.html">NIP-24</a> — the <code>t</code> hashtag tag that overlaps with the hashtag form here.</li>304 <li><a href="12.html">NIP-12</a> — the origin of generic tag queries, now merged into NIP-01.</li>305 </ul>306 </div>307308 <aside>309 <div class="cartridge">310 <div class="inner">311 <h3 class="cardhead">Spec Card</h3>312 <div class="lights"><span class="led on"></span><span class="led warn"></span><span class="led warn"></span></div>313 <dl>314 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span></dd>315 <dt>Defines</dt><dd><code>i</code> / <code>k</code> tags - 12 external id formats - optional url hints</dd>316 <dt>Kinds</dt><dd>none — works in any event kind</dd>317 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> (tag indexing, filters)</dd>318 <dt>First commit</dt><dd>2024-04-19</dd>319 <dt>Last updated</dt><dd>2026-06-13</dd>320 </dl>321 </div>322 </div>323 </aside>324 </main>325326 <nav class="navbtns">327 <a href="72.html">◀ NIP-72: Moderated Communities (Reddit Style)</a>328 <a href="75.html">NIP-75: Zap Goals ▶</a>329 </nav>330331 <footer class="pagefoot">332 <a href="../index.html">Nostr Protocol Manual</a>333 <span class="pageno">NIP 73 / F4</span>334 </footer>335</div>336337<script src="../assets/webmcp.js" defer></script>338</body>339</html>