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-69 - Peer-to-peer Order events — Nostr Instruction Manual</title>7<meta name="description" content="NIP-69: peer-to-peer order events. Kind 38383 addressable events that pool buy and sell orders from all P2P trading platforms into one shared order book.">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 69 - Commerce</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="69">69</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 69</div>50 <h1>Peer-to-peer Order events</h1>51 <p class="sub">One shared order book for every peer-to-peer Bitcoin trading platform, built from kind 38383 events.</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>A <strong>peer-to-peer (P2P) platform</strong> matches people who want to trade Bitcoin for fiat money without a central custodian. Many such platforms exist. Each platform keeps its own private list of open orders. This splits the liquidity: a trader on one platform cannot see the orders on another. Sometimes there are not enough matching orders to trade.</p>60 <p>This NIP defines one simple event format for a P2P <strong>order</strong>. Every participating platform publishes its orders as Nostr events. Every platform reads the orders of all the others from relays. The result is one big shared liquidity pool. A pool of many platforms can compete with centralized exchanges, where a single authority controls the liquidity.</p>61 <p>An order event is an <a href="01.html">NIP-01</a> addressable event with kind <code>38383</code>. All order data lives in tags. The <code>content</code> field stays empty. A platform updates an order by publishing a new event with the same <code>d</code> tag; the relay keeps only the latest version.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>You do not trade through this event. The event only advertises the order. The actual trade happens on the platform named in the <code>y</code> tag, often reached through the URL in the <code>source</code> tag.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 200" role="img" aria-label="Three P2P platforms publish kind 38383 orders to shared relays; a client reads one combined order book">71 <rect class="d-actor" x="10" y="14" width="120" height="42" rx="8"/>72 <text class="d-actor-label" x="70" y="40" text-anchor="middle">Platform A</text>73 <rect class="d-actor" x="10" y="79" width="120" height="42" rx="8"/>74 <text class="d-actor-label" x="70" y="105" text-anchor="middle">Platform B</text>75 <rect class="d-actor" x="10" y="144" width="120" height="42" rx="8"/>76 <text class="d-actor-label" x="70" y="170" text-anchor="middle">Platform C</text>77 <rect class="d-actor" x="235" y="75" width="110" height="50" rx="8"/>78 <text class="d-actor-label" x="290" y="97" text-anchor="middle">Relays</text>79 <text class="d-note" x="290" y="114" text-anchor="middle">shared pool</text>80 <rect class="d-actor" x="430" y="75" width="120" height="50" rx="8"/>81 <text class="d-actor-label" x="490" y="97" text-anchor="middle">Client</text>82 <text class="d-note" x="490" y="114" text-anchor="middle">one order book</text>83 <path class="d-msg d-flow" d="M132,35 C185,42 190,70 233,85"/>84 <path class="d-msg d-flow" d="M132,100 L233,100"/>85 <path class="d-msg d-flow" d="M132,165 C185,158 190,130 233,115"/>86 <path class="d-msg blue d-flow" d="M347,100 L428,100"/>87 <text class="d-label" x="182" y="93" text-anchor="middle">kind 38383</text>88 <text class="d-label" x="388" y="93" text-anchor="middle">REQ</text>89 <circle r="4" fill="#f26a00">90 <animateMotion dur="2.6s" repeatCount="indefinite" path="M132,35 C185,42 190,70 233,85"/>91 </circle>92 <circle r="4" fill="#2e5f9e">93 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M347,100 L428,100"/>94 </circle>95 </svg>96 </div>97 <figcaption>Fig. 69-1 — <b>The shared liquidity pool.</b> Each platform publishes orders as kind 38383 events. Any client reads them all as one order book.</figcaption>98 </figure>99100 <h2 class="bar" id="spec"><span class="num">2</span> The Order Event</h2>101 <p>An order event is an addressable event, as defined in <a href="01.html">NIP-01</a>. It uses kind <code>38383</code>. The relay keeps only the latest event for each <code>pubkey</code> + <code>kind</code> + <code>d</code> value. A complete order event looks like this:</p>102103 <div class="crt">104 <div class="titlebar">Wire format - kind 38383 order event</div>105<pre>{106 <span class="k">"id"</span>: <span class="s">"84fad0d29cb3529d789faeff2033e88fe157a48e071c6a5d1619928289420e31"</span>,107 <span class="k">"pubkey"</span>: <span class="s">"dbe0b1be7aafd3cfba92d7463edbd4e33b2969f61bd554d37ac56f032e13355a"</span>,108 <span class="k">"created_at"</span>: 1702548701,109 <span class="k">"kind"</span>: 38383,110 <span class="k">"tags"</span>: [111 [<span class="s">"d"</span>, <span class="s">"ede61c96-4c13-4519-bf3a-dcf7f1e9d842"</span>],112 [<span class="s">"k"</span>, <span class="s">"sell"</span>],113 [<span class="s">"f"</span>, <span class="s">"VES"</span>],114 [<span class="s">"s"</span>, <span class="s">"pending"</span>],115 [<span class="s">"amt"</span>, <span class="s">"0"</span>],116 [<span class="s">"fa"</span>, <span class="s">"100"</span>],117 [<span class="s">"pm"</span>, <span class="s">"face to face"</span>, <span class="s">"bank transfer"</span>],118 [<span class="s">"premium"</span>, <span class="s">"1"</span>],119 [<span class="s">"rating"</span>, <span class="s">"{\"total_reviews\":1,\"total_rating\":3.0,\"last_rating\":3,\"max_rate\":5,\"min_rate\":1}"</span>],120 [<span class="s">"source"</span>, <span class="s">"https://t.me/p2plightning/xxxxxxx"</span>],121 [<span class="s">"network"</span>, <span class="s">"mainnet"</span>],122 [<span class="s">"layer"</span>, <span class="s">"lightning"</span>],123 [<span class="s">"name"</span>, <span class="s">"Nakamoto"</span>],124 [<span class="s">"g"</span>, <span class="c"><geohash></span>],125 [<span class="s">"bond"</span>, <span class="s">"0"</span>],126 [<span class="s">"expires_at"</span>, <span class="s">"1719391096"</span>],127 [<span class="s">"expiration"</span>, <span class="s">"1719995896"</span>],128 [<span class="s">"y"</span>, <span class="s">"lnp2pbot"</span>],129 [<span class="s">"z"</span>, <span class="s">"order"</span>]130 ],131 <span class="k">"content"</span>: <span class="s">""</span>,132 <span class="k">"sig"</span>: <span class="s">"7e8fe1eb644f33ff51d8805c02a0e1a6d034e6234eac50ef7a7e0dac68a0414f…"</span>133}</pre>134 </div>135136 <h2 class="bar" id="tags"><span class="num">3</span> Tag Reference</h2>137 <p>The source spec marks each tag as mandatory or optional. A mandatory tag must appear in every order event. An optional tag may be omitted.</p>138139 <div class="tscroll"><table class="ref">140 <caption>Mandatory tags</caption>141 <tr><th>Tag</th><th>Meaning</th><th>Rule</th></tr>142 <tr><td class="btn">d</td><td>Order ID</td><td>A unique identifier for the order. Also the addressable-event key.</td></tr>143 <tr><td class="btn">k</td><td>Order type</td><td><code>sell</code> or <code>buy</code>.</td></tr>144 <tr><td class="btn">f</td><td>Currency</td><td>The asset being traded, as an ISO 4217 currency code.</td></tr>145 <tr><td class="btn">s</td><td>Status</td><td><code>pending</code>, <code>canceled</code>, <code>in-progress</code>, <code>success</code>, or <code>expired</code>. See §4.</td></tr>146 <tr><td class="btn">amt</td><td>Amount</td><td>The amount of Bitcoin to trade, in satoshis. <code>0</code> means: get the satoshi amount from a public price API after the taker accepts the order.</td></tr>147 <tr><td class="btn">fa</td><td>Fiat amount</td><td>The fiat amount being traded. A range order carries two values: the minimum and the maximum amount.</td></tr>148 <tr><td class="btn">pm</td><td>Payment method</td><td>The payment method for the trade. For multiple payment methods, the spec text says: separate them with a comma. See the Tech Note below.</td></tr>149 <tr><td class="btn">premium</td><td>Premium</td><td>The percentage of premium the maker is willing to pay.</td></tr>150 <tr><td class="btn">network</td><td>Network</td><td>The network for the trade: <code>mainnet</code>, <code>testnet</code>, <code>signet</code>, etc.</td></tr>151 <tr><td class="btn">layer</td><td>Layer</td><td>The layer for the trade: <code>onchain</code>, <code>lightning</code>, <code>liquid</code>, etc.</td></tr>152 <tr><td class="btn">expires_at</td><td>Expires at</td><td>Unix timestamp. The expiration date of the order while in <code>pending</code> status. After this time, the status SHOULD change to <code>expired</code>.</td></tr>153 <tr><td class="btn">expiration</td><td>Expiration</td><td>Unix timestamp. The expiration date of the event itself. After this time, the relay SHOULD delete the event, per <a href="40.html">NIP-40</a>.</td></tr>154 <tr><td class="btn">y</td><td>Platform</td><td>The platform that created the order.</td></tr>155 <tr><td class="btn">z</td><td>Document</td><td>Always <code>order</code>.</td></tr>156 </table></div>157158 <div class="tscroll"><table class="ref">159 <caption>Optional tags</caption>160 <tr><th>Tag</th><th>Meaning</th><th>Rule</th></tr>161 <tr><td class="btn">source</td><td>Source</td><td>The source of the order. It can be a URL that redirects to the order.</td></tr>162 <tr><td class="btn">rating</td><td>Rating</td><td>The rating of the maker. This NIP does not define how the rating is calculated; each platform defines its own method.</td></tr>163 <tr><td class="btn">name</td><td>Name</td><td>The name of the maker.</td></tr>164 <tr><td class="btn">g</td><td>Geohash</td><td>The geohash of the operation. Useful for a face-to-face trade.</td></tr>165 <tr><td class="btn">bond</td><td>Bond</td><td>The bond amount. The bond is a security deposit that both parties must pay.</td></tr>166 </table></div>167168 <div class="box tech">169 <span class="tag">Tech Note</span>170 <p>The spec text and its own example disagree on <code>pm</code>. The text says: separate multiple payment methods with a comma. The example event instead lists them as separate tag elements: <code>["pm", "face to face", "bank transfer"]</code>. Implementations in the wild follow the example. Expect both shapes when parsing.</p>171 </div>172173 <div class="box warn">174 <span class="tag">Warning!</span>175 <p>Do not confuse the two expiry tags. <code>expires_at</code> expires the <em>order</em>: the status SHOULD flip to <code>expired</code>. <code>expiration</code> expires the <em>event</em>: the relay SHOULD delete it (<a href="40.html">NIP-40</a>). Set <code>expiration</code> later than <code>expires_at</code>, or the order vanishes before it can expire.</p>176 </div>177178 <h2 class="bar" id="status"><span class="num">4</span> Order Status</h2>179 <p>The <code>s</code> tag carries the order status. The spec defines five values. A platform replaces the order event to change the status. Because the event is addressable, the relay keeps only the newest version.</p>180 <ul>181 <li><strong>pending</strong> — the order is open and waits for a taker.</li>182 <li><strong>in-progress</strong> — a taker accepted the order; the trade is running.</li>183 <li><strong>success</strong> — the trade completed.</li>184 <li><strong>canceled</strong> — the maker or the platform canceled the order.</li>185 <li><strong>expired</strong> — the <code>expires_at</code> time passed while the order was still <code>pending</code>.</li>186 </ul>187188 <figure class="diagram">189 <div class="frame">190 <svg viewBox="0 0 560 190" role="img" aria-label="Order status state machine: pending moves to in-progress, canceled, or expired; in-progress moves to success or canceled">191 <rect class="d-actor" x="20" y="70" width="110" height="44" rx="8"/>192 <text class="d-actor-label" x="75" y="97" text-anchor="middle">pending</text>193 <rect class="d-actor" x="225" y="20" width="120" height="44" rx="8"/>194 <text class="d-actor-label" x="285" y="47" text-anchor="middle">in-progress</text>195 <rect class="d-actor" x="440" y="20" width="100" height="44" rx="8"/>196 <text class="d-actor-label" x="490" y="47" text-anchor="middle">success</text>197 <rect class="d-actor" x="225" y="126" width="120" height="44" rx="8"/>198 <text class="d-actor-label" x="285" y="153" text-anchor="middle">canceled</text>199 <rect class="d-actor" x="440" y="126" width="100" height="44" rx="8"/>200 <text class="d-actor-label" x="490" y="153" text-anchor="middle">expired</text>201 <path class="d-msg d-flow" d="M132,80 C170,68 185,55 223,45"/>202 <path class="d-msg" d="M132,104 C170,116 185,132 223,145"/>203 <path class="d-msg d-flow" d="M347,42 L438,42"/>204 <path class="d-msg" d="M330,66 C370,90 400,120 438,140"/>205 <path class="d-msg blue" d="M132,92 C260,92 340,110 438,142"/>206 <text class="d-label" x="175" y="45" text-anchor="middle">taker accepts</text>207 <text class="d-label" x="392" y="35" text-anchor="middle">trade done</text>208 <text class="d-label" x="168" y="135" text-anchor="middle">cancel</text>209 <text class="d-label" x="280" y="104" text-anchor="middle">expires_at passes</text>210 </svg>211 </div>212 <figcaption>Fig. 69-2 — <b>Status lifecycle.</b> Every change is a new kind 38383 event with the same <code>d</code> value and a new <code>s</code> tag.</figcaption>213 </figure>214215 <div class="box tech">216 <span class="tag">Tech Note</span>217 <p>The spec lists the five status values but defines no transition rules between them. The transitions in Fig. 69-2 are the common practice of the listed implementations, not normative text. The only normative rule is: after <code>expires_at</code>, a <code>pending</code> order SHOULD become <code>expired</code>.</p>218 </div>219220 <h2 class="bar" id="examples"><span class="num">5</span> Worked Example</h2>221 <p>A trader wants to browse open sell orders for Venezuelan bolivar (<code>VES</code>) over Lightning. The client filters kind <code>38383</code> by the indexed single-letter tags from <a href="01.html">NIP-01</a>. Orange lines go to the relay. Blue lines come from the relay.</p>222 <div class="crt">223 <div class="titlebar">Session transcript - wss://relay.mostro.network</div>224<pre><span class="c">-- browse open sell orders in VES --</span>225<span class="send">→ ["REQ", "book", {"kinds":[38383], "#f":["VES"], "#s":["pending"], "#k":["sell"]}]</span>226<span class="recv">← ["EVENT", "book", {"kind":38383, "pubkey":"dbe0b1be…", "tags":[227 ["d","ede61c96-4c13-4519-bf3a-dcf7f1e9d842"], ["k","sell"], ["f","VES"],228 ["s","pending"], ["amt","0"], ["fa","100"], ["pm","face to face","bank transfer"],229 ["premium","1"], ["network","mainnet"], ["layer","lightning"],230 ["expires_at","1719391096"], ["expiration","1719995896"],231 ["y","lnp2pbot"], ["z","order"]], "content":"", …}]</span>232<span class="recv">← ["EOSE", "book"]</span>233234<span class="c">-- a taker accepts; the platform replaces the order --</span>235<span class="send">→ ["EVENT", {"kind":38383, "pubkey":"dbe0b1be…", "tags":[236 ["d","ede61c96-4c13-4519-bf3a-dcf7f1e9d842"], ["s","in-progress"],237 ["k","sell"], ["f","VES"], <span class="c">…same order, new status…</span>], "content":"", …}]</span>238<span class="recv">← ["OK", "9c31aa02…", true, ""]</span>239<span class="c">-- the relay keeps only this latest version for d=ede61c96-… --</span></pre>240 </div>241 <p>The relay treats both events as one address: <code>38383:dbe0b1be…:ede61c96-4c13-4519-bf3a-dcf7f1e9d842</code>. Old versions may be discarded. Subscribers see the status change live.</p>242243 <h2 class="bar" id="implementations"><span class="num">6</span> Implementations</h2>244 <p>The spec lists four platforms that publish kind 38383 order events:</p>245 <ul>246 <li><strong>Mostro</strong> — a Lightning P2P daemon; see the <a href="https://mostro.network/protocol/">Mostro protocol specification</a>.</li>247 <li><strong>@lnp2pBot</strong> — a Telegram bot for Lightning P2P trades.</li>248 <li><strong>Robosats</strong> — added NIP-69 order publishing in a dedicated pull request.</li>249 <li><strong>Peach Bitcoin</strong> — via a Nostr announcer bot.</li>250 </ul>251 <p>The spec also references two earlier designs: the messages specification of the original peer-to-peer NIP proposal (a draft <code>31001.md</code> file, linked at a fixed old commit) and the <strong>n3xB</strong> exchange protocol.</p>252253 <h2 class="bar" id="history"><span class="num">7</span> Version History</h2>254 <p>The proposal started in June 2024 and received its NIP number in November 2024. Notable changes, newest first:</p>255 <ul class="patchnotes">256 <li><span class="ver">2025</span><span class="when">2025-11</span>Added order expiration support: the <code>expires_at</code> and <code>expiration</code> tags.</li>257 <li><span class="ver">2025</span><span class="when">2025-07</span>Added Peach Bitcoin to the implementations list.</li>258 <li class="minor"><span class="ver">2025</span><span class="when">2025-02 / 2025-04</span>Repository-wide fixes to references, titles, and descriptions.</li>259 <li><span class="ver">2024</span><span class="when">2024-11</span>The proposal received the number NIP-69 and entered the README index.</li>260 <li><span class="ver">2024</span><span class="when">2024-07</span>Added more optional tags and small fixes.</li>261 <li><span class="ver">2024</span><span class="when">2024-06</span>Added the <code>source</code> tag.</li>262 <li><span class="ver">Origin</span><span class="when">2024-06-27</span>Francisco Calderón proposed "a very simple spec for p2p events".</li>263 </ul>264265 <div class="credits">266 <h3 class="cardhead">Original Proposal - Credits</h3>267 <table>268 <tr><td>Author</td><td>Francisco Calderón</td></tr>269 <tr><td>First commit</td><td>2024-06-27 — "Proposal of a very simple spec for p2p events" (numbered NIP-69 on 2024-11-04)</td></tr>270 <tr><td>Status</td><td>draft - optional</td></tr>271 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/69.md">69.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/69.md">full change history</a></td></tr>272 </table>273 </div>274275 <h2 class="bar"><span class="num">8</span> See Also</h2>276 <ul>277 <li><a href="01.html">NIP-01</a> — addressable events, tag indexing, and the <code>REQ</code> filters used to query the order book.</li>278 <li><a href="40.html">NIP-40</a> — the <code>expiration</code> tag; relays delete the event after the given timestamp.</li>279 <li><a href="15.html">NIP-15</a> — the marketplace spec for fixed-price product listings; NIP-69 covers P2P currency trades instead.</li>280 <li><a href="99.html">NIP-99</a> — classified listings; a related but more general buy/sell format.</li>281 </ul>282 </div>283284 <aside>285 <div class="cartridge">286 <div class="inner">287 <h3 class="cardhead">Spec Card</h3>288 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>289 <dl>290 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>291 <dt>Defines</dt><dd>P2P order events - order tags - status values</dd>292 <dt>Kind</dt><dd>38383 (addressable)</dd>293 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="40.html">NIP-40</a> (expiration)</dd>294 <dt>First commit</dt><dd>2024-06-27</dd>295 <dt>Last updated</dt><dd>2025-11-13</dd>296 </dl>297 </div>298 </div>299 </aside>300 </main>301302 <nav class="navbtns">303 <a href="68.html">◀ NIP-68: Picture-first feeds</a>304 <a href="70.html">NIP-70: Protected Events ▶</a>305 </nav>306307 <footer class="pagefoot">308 <a href="../index.html">Nostr Protocol Manual</a>309 <span class="pageno">NIP 69 / F4</span>310 </footer>311</div>312313<script src="../assets/webmcp.js" defer></script>314</body>315</html>