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-86 - Relay Management API — Nostr Instruction Manual</title>7<meta name="description" content="NIP-86: the Relay Management API. A JSON-RPC-like protocol over HTTP for banning pubkeys, moderating events, managing roles, and configuring a relay. 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 86 - Relay Ops</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="86">86</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 86</div>50 <h1>Relay Management API</h1>51 <p class="sub">A JSON-RPC-like protocol over HTTP that lets a relay operator ban pubkeys, moderate events, manage roles, and change relay settings.</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 relay operator needs day-to-day controls: ban a spammer, approve a reported event, block an abusive IP, rename the relay. This NIP defines a <strong>management API</strong> for those tasks. A relay may implement it. Support is optional.</p>60 <p>The API is a request-response protocol over HTTP. It is not part of the <a href="01.html">NIP-01</a> WebSocket flow. It lives on the <em>same URI</em> as the relay's WebSocket endpoint. A special <code>Content-Type</code> header tells the relay that an HTTP request is a management call, not a WebSocket upgrade.</p>61 <p>The protocol defines 25 methods. A management client first calls <code>supportedmethods</code> to discover what the relay implements. Every call must carry a signed <a href="98.html">NIP-98</a> authorization event, so only authorized keys can manage the relay.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>This API is for relay <em>operators and moderators</em>, not for regular users. A normal client never needs it. If you build a relay dashboard, this chapter is your control panel wiring diagram.</p>66 </div>6768 <h2 class="bar" id="spec"><span class="num">2</span> Transport and Message Format</h2>69 <p>The relay watches for HTTP or HTTPS requests with the header <code>Content-Type: application/nostr+json+rpc</code> on a URI that supports WebSocket upgrades. When such a request arrives, the relay parses the body as a JSON document with two fields:</p>7071 <div class="crt">72 <div class="titlebar">Wire format - request body</div>73<pre>{74 <span class="k">"method"</span>: <span class="c"><method-name></span>,75 <span class="k">"params"</span>: [<span class="c"><array></span>, <span class="c"><of></span>, <span class="c"><parameters></span>]76}</pre>77 </div>7879 <p>The relay then returns a JSON response with this shape:</p>8081 <div class="crt">82 <div class="titlebar">Wire format - response body</div>83<pre>{84 <span class="k">"result"</span>: <span class="c"><arbitrary value — shape depends on the method></span>,85 <span class="k">"error"</span>: <span class="c"><optional error message, present if the call has errored></span>86}</pre>87 </div>8889 <div class="tscroll"><table class="ref">90 <caption>Message fields</caption>91 <tr><th>Field</th><th>Direction</th><th>Rule</th></tr>92 <tr><td class="btn">method</td><td>request</td><td>The name of the method to call. One of the 25 names in §3.</td></tr>93 <tr><td class="btn">params</td><td>request</td><td>An array of positional parameters. May be empty.</td></tr>94 <tr><td class="btn">result</td><td>response</td><td>The return value. Each method defines its own shape.</td></tr>95 <tr><td class="btn">error</td><td>response</td><td>An optional error message string. Present when the call fails.</td></tr>96 </table></div>9798 <figure class="diagram">99 <div class="frame">100 <svg viewBox="0 0 560 265" role="img" aria-label="Sequence diagram: admin client sends an authorized HTTP RPC request, the relay verifies the NIP-98 event and returns a result, or 401 without valid authorization">101 <line class="d-wire" x1="110" y1="40" x2="110" y2="250" stroke-dasharray="3 4"/>102 <line class="d-wire" x1="450" y1="40" x2="450" y2="250" stroke-dasharray="3 4"/>103 <rect class="d-actor" x="55" y="8" width="110" height="30" rx="6"/>104 <text class="d-actor-label" x="110" y="28" text-anchor="middle">Admin client</text>105 <rect class="d-actor" x="395" y="8" width="110" height="30" rx="6"/>106 <text class="d-actor-label" x="450" y="28" text-anchor="middle">Relay (HTTP)</text>107 <path class="d-msg d-flow" d="M110,70 L445,70"/>108 <text class="d-label" x="280" y="55" text-anchor="middle">POST / — Content-Type: application/nostr+json+rpc</text>109 <text class="d-label" x="280" y="88" text-anchor="middle">Authorization: Nostr <NIP-98 event></text>110 <path class="d-msg blue" d="M450,125 L115,125"/>111 <text class="d-label" x="280" y="118" text-anchor="middle">200 {"result": …}</text>112 <path class="d-msg" d="M110,175 L445,175"/>113 <text class="d-label" x="280" y="168" text-anchor="middle">same call, missing or invalid Authorization</text>114 <path class="d-msg blue" d="M450,208 L115,208"/>115 <text class="d-label" x="280" y="201" text-anchor="middle">401 Unauthorized</text>116 <text class="d-note" x="280" y="240" text-anchor="middle">Same URI as the WebSocket endpoint. The Content-Type header selects the RPC path.</text>117 <circle r="4" fill="#f26a00">118 <animateMotion dur="2.6s" repeatCount="indefinite" path="M110,70 L445,70"/>119 </circle>120 </svg>121 </div>122 <figcaption>Fig. 86-1 — <b>One RPC round trip.</b> The relay verifies the <a href="98.html">NIP-98</a> event before it runs the method. No valid event, no service.</figcaption>123 </figure>124125 <h2 class="bar" id="methods"><span class="num">3</span> The Methods</h2>126 <p>The tables below list every method a relay may support. Params are positional. Unless noted, a write method returns <code>result: true</code> — a boolean always set to <code>true</code>. List methods return arrays. A relay may support any subset; report the subset with <code>supportedmethods</code>.</p>127128 <h3>Discovery</h3>129 <div class="tscroll"><table class="ref">130 <caption>Introspection method</caption>131 <tr><th>Method</th><th>Params</th><th>Result</th></tr>132 <tr><td class="btn">supportedmethods</td><td><code>[]</code></td><td><code>["<method-name>", …]</code> — an array with the names of all the other supported methods.</td></tr>133 </table></div>134135 <h3>Pubkey moderation</h3>136 <p>A <strong>ban</strong> rejects a pubkey. An <strong>allow</strong> whitelists a pubkey. The reason parameter is an optional free-text string.</p>137 <div class="tscroll"><table class="ref">138 <caption>Pubkey ban and allow lists</caption>139 <tr><th>Method</th><th>Params</th><th>Result</th></tr>140 <tr><td class="btn">banpubkey</td><td><code>["<32-byte-hex-public-key>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>141 <tr><td class="btn">unbanpubkey</td><td><code>["<32-byte-hex-public-key>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>142 <tr><td class="btn">listbannedpubkeys</td><td><code>[]</code></td><td><code>[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, …]</code> — an array of objects.</td></tr>143 <tr><td class="btn">allowpubkey</td><td><code>["<32-byte-hex-public-key>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>144 <tr><td class="btn">unallowpubkey</td><td><code>["<32-byte-hex-public-key>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>145 <tr><td class="btn">listallowedpubkeys</td><td><code>[]</code></td><td><code>[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, …]</code> — an array of objects.</td></tr>146 </table></div>147148 <h3>Roles</h3>149 <p>A <strong>role</strong> is a named label a relay can attach to a pubkey, for example "moderator". A role has an <code>id</code>, a display <code>label</code>, a <code>description</code>, a <code>color</code>, and a sort <code>order</code>.</p>150 <div class="tscroll"><table class="ref">151 <caption>Role management</caption>152 <tr><th>Method</th><th>Params</th><th>Result</th></tr>153 <tr><td class="btn">createrole</td><td><code>[id, label, description, color, order]</code></td><td><code>true</code></td></tr>154 <tr><td class="btn">editrole</td><td><code>[id, label, description, color, order]</code></td><td><code>true</code></td></tr>155 <tr><td class="btn">deleterole</td><td><code>[id]</code></td><td><code>true</code></td></tr>156 <tr><td class="btn">assignrole</td><td><code>["<32-byte-hex-public-key>", "<role-id>"]</code></td><td><code>true</code></td></tr>157 <tr><td class="btn">unassignrole</td><td><code>["<32-byte-hex-public-key>", "<role-id>"]</code></td><td><code>true</code></td></tr>158 </table></div>159160 <div class="box tech">161 <span class="tag">Tech Note</span>162 <p>The spec leaves gaps here. The role params have no defined types or formats: the shape of <code>color</code> and the semantics of <code>order</code> are unstated. There is no method to list roles or role assignments. Also, <code>unbanpubkey</code> and <code>unallowpubkey</code> take an optional reason with no stated use, while <code>unblockip</code> takes none. Expect implementations to differ.</p>163 </div>164165 <h3>Event moderation</h3>166 <p>These methods work a moderation queue. The relay collects reported or held events. A moderator lists them, then allows or bans each one by event id.</p>167 <div class="tscroll"><table class="ref">168 <caption>Event queue and ban list</caption>169 <tr><th>Method</th><th>Params</th><th>Result</th></tr>170 <tr><td class="btn">listeventsneedingmoderation</td><td><code>[]</code></td><td><code>[{"id": "<32-byte-hex>", "reason": "<optional-reason>"}]</code> — an array of objects.</td></tr>171 <tr><td class="btn">allowevent</td><td><code>["<32-byte-hex-event-id>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>172 <tr><td class="btn">banevent</td><td><code>["<32-byte-hex-event-id>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>173 <tr><td class="btn">listbannedevents</td><td><code>[]</code></td><td><code>[{"id": "<32-byte hex>", "reason": "<optional-reason>"}, …]</code> — an array of objects.</td></tr>174 </table></div>175176 <h3>Relay profile</h3>177 <p>These methods edit the relay's public identity. Clients read that identity through the <a href="11.html">NIP-11</a> relay information document.</p>178 <div class="tscroll"><table class="ref">179 <caption>Relay identity settings</caption>180 <tr><th>Method</th><th>Params</th><th>Result</th></tr>181 <tr><td class="btn">changerelayname</td><td><code>["<new-name>"]</code></td><td><code>true</code></td></tr>182 <tr><td class="btn">changerelaydescription</td><td><code>["<new-description>"]</code></td><td><code>true</code></td></tr>183 <tr><td class="btn">changerelayicon</td><td><code>["<new-icon-url>"]</code></td><td><code>true</code></td></tr>184 </table></div>185186 <h3>Kind policy</h3>187 <p>These methods control which event kinds the relay accepts. The kind number is a bare JSON number, not a string.</p>188 <div class="tscroll"><table class="ref">189 <caption>Allowed-kind list</caption>190 <tr><th>Method</th><th>Params</th><th>Result</th></tr>191 <tr><td class="btn">allowkind</td><td><code>[<kind-number>]</code></td><td><code>true</code></td></tr>192 <tr><td class="btn">disallowkind</td><td><code>[<kind-number>]</code></td><td><code>true</code></td></tr>193 <tr><td class="btn">listallowedkinds</td><td><code>[]</code></td><td><code>[<kind-number>, …]</code> — an array of numbers.</td></tr>194 </table></div>195196 <h3>IP blocking</h3>197 <div class="tscroll"><table class="ref">198 <caption>IP block list</caption>199 <tr><th>Method</th><th>Params</th><th>Result</th></tr>200 <tr><td class="btn">blockip</td><td><code>["<ip-address>", "<optional-reason>"]</code></td><td><code>true</code></td></tr>201 <tr><td class="btn">unblockip</td><td><code>["<ip-address>"]</code></td><td><code>true</code></td></tr>202 <tr><td class="btn">listblockedips</td><td><code>[]</code></td><td><code>[{"ip": "<ip-address>", "reason": "<optional-reason>"}, …]</code> — an array of objects.</td></tr>203 </table></div>204205 <figure class="diagram">206 <div class="frame">207 <svg viewBox="0 0 560 210" role="img" aria-label="Map of the seven method families: discovery, pubkey moderation, roles, event moderation, relay profile, kind policy, and IP blocking, all reaching the relay over one HTTP endpoint">208 <rect class="d-actor" x="225" y="80" width="110" height="50" rx="8"/>209 <text class="d-actor-label" x="280" y="102" text-anchor="middle">Relay</text>210 <text class="d-note" x="280" y="119" text-anchor="middle">one HTTP URI</text>211 <rect class="d-actor" x="10" y="14" width="150" height="30" rx="6"/>212 <text class="d-actor-label" x="85" y="34" text-anchor="middle">Discovery — 1</text>213 <rect class="d-actor" x="10" y="90" width="150" height="30" rx="6"/>214 <text class="d-actor-label" x="85" y="110" text-anchor="middle">Pubkeys — 6</text>215 <rect class="d-actor" x="10" y="166" width="150" height="30" rx="6"/>216 <text class="d-actor-label" x="85" y="186" text-anchor="middle">Roles — 5</text>217 <rect class="d-actor" x="400" y="4" width="150" height="30" rx="6"/>218 <text class="d-actor-label" x="475" y="24" text-anchor="middle">Events — 4</text>219 <rect class="d-actor" x="400" y="56" width="150" height="30" rx="6"/>220 <text class="d-actor-label" x="475" y="76" text-anchor="middle">Profile — 3</text>221 <rect class="d-actor" x="400" y="108" width="150" height="30" rx="6"/>222 <text class="d-actor-label" x="475" y="128" text-anchor="middle">Kinds — 3</text>223 <rect class="d-actor" x="400" y="160" width="150" height="30" rx="6"/>224 <text class="d-actor-label" x="475" y="180" text-anchor="middle">IPs — 3</text>225 <path class="d-msg" d="M162,29 C200,40 200,70 222,85"/>226 <path class="d-msg" d="M162,105 L222,105"/>227 <path class="d-msg" d="M162,181 C200,170 200,140 222,125"/>228 <path class="d-msg blue" d="M398,19 C360,30 360,65 338,82"/>229 <path class="d-msg blue" d="M398,71 C370,78 360,85 338,92"/>230 <path class="d-msg blue" d="M398,123 C370,116 360,111 338,108"/>231 <path class="d-msg blue" d="M398,175 C360,164 360,134 338,120"/>232 </svg>233 </div>234 <figcaption>Fig. 86-2 — <b>The management surface.</b> Seven method families, 25 methods, one authorized endpoint.</figcaption>235 </figure>236237 <h2 class="bar" id="auth"><span class="num">4</span> Authorization</h2>238 <p>Every request must contain an <code>Authorization</code> header with a valid <a href="98.html">NIP-98</a> event. Two adjustments apply on top of NIP-98:</p>239 <ul>240 <li>The <code>payload</code> tag is <strong>required</strong>, not optional. It carries the sha256 hash of the request body, so the relay can verify the RPC call was not altered.</li>241 <li>The <code>u</code> tag is the relay URL.</li>242 </ul>243 <p>If the <code>Authorization</code> header is missing or invalid, the endpoint should return a 401 response.</p>244 <div class="box warn">245 <span class="tag">Warning!</span>246 <p>Authorization is not the same as permission. NIP-98 proves <em>who</em> signed the request. The relay still decides <em>which</em> pubkeys may call which methods. That policy is up to the relay operator; this NIP does not define it.</p>247 </div>248249 <h2 class="bar" id="examples"><span class="num">5</span> Worked Example</h2>250 <p>A moderator discovers the relay's methods, bans a spammer, and confirms the ban. Orange lines go to the relay. Blue lines come from the relay.</p>251 <div class="crt">252 <div class="titlebar">Management session - https://relay.example.com</div>253<pre><span class="c">-- discover what the relay supports --</span>254<span class="send">→ POST / HTTP/1.1255 Content-Type: application/nostr+json+rpc256 Authorization: Nostr eyJpZCI6IjVjODNkYTc3…</span>257<span class="send">→ {"method":"supportedmethods","params":[]}</span>258<span class="recv">← {"result":["banpubkey","unbanpubkey","listbannedpubkeys","allowkind"]}</span>259260<span class="c">-- ban a spammer (each call repeats the headers above) --</span>261<span class="send">→ {"method":"banpubkey",262 "params":["22a12a128a3be27cd7fb250cbe796e692896398dc1440ae3fa567812c8107c1c",263 "spam"]}</span>264<span class="recv">← {"result":true}</span>265266<span class="c">-- confirm --</span>267<span class="send">→ {"method":"listbannedpubkeys","params":[]}</span>268<span class="recv">← {"result":[{"pubkey":"22a12a128a3be27cd7fb250cbe796e692896398dc1440ae3fa567812c8107c1c",269 "reason":"spam"}]}</span>270271<span class="c">-- a failed call and a missing Authorization header --</span>272<span class="recv">← {"error":"method not supported"}</span>273<span class="recv">← HTTP/1.1 401 Unauthorized</span></pre>274 </div>275276 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>277 <p>NIP-86 arrived in late 2024 and has grown by small additions. Notable changes, newest first:</p>278 <ul class="patchnotes">279 <li><span class="ver">2026</span><span class="when">2026-06</span>Added the role methods: <code>createrole</code>, <code>editrole</code>, <code>deleterole</code>, <code>assignrole</code>, <code>unassignrole</code>.</li>280 <li class="minor"><span class="ver">2026</span><span class="when">2026-06</span>A <code>signevent</code> method was added, then reverted one day later. It is not part of the spec.</li>281 <li><span class="ver">2026</span><span class="when">2026-02</span>Added <code>unbanpubkey</code> and <code>unallowpubkey</code>, so bans and allows can be undone.</li>282 <li><span class="ver">Origin</span><span class="when">2024-11</span>fiatjaf added NIP-86 with the initial method set, transport rules, and NIP-98 authorization.</li>283 </ul>284285 <div class="credits">286 <h3 class="cardhead">Original Proposal - Credits</h3>287 <table>288 <tr><td>Author</td><td>fiatjaf</td></tr>289 <tr><td>First commit</td><td>2024-11-25 — “add NIP-86: Relay Management API” (<a href="https://github.com/nostr-protocol/nips/pull/1325">PR #1325</a>)</td></tr>290 <tr><td>Status</td><td>draft - optional — relays may implement any subset of the methods</td></tr>291 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/86.md">86.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/86.md">full change history</a></td></tr>292 </table>293 </div>294295 <h2 class="bar"><span class="num">7</span> See Also</h2>296 <ul>297 <li><a href="98.html">NIP-98</a> — the HTTP Auth event that every management call must carry.</li>298 <li><a href="01.html">NIP-01</a> — the base protocol; defines events, kinds, pubkeys, and the WebSocket endpoint this API shares.</li>299 <li><a href="11.html">NIP-11</a> — the relay information document that exposes the name, description, and icon these methods change.</li>300 <li><a href="42.html">NIP-42</a> — client authentication inside the WebSocket connection; a different mechanism for a different layer.</li>301 </ul>302 </div>303304 <aside>305 <div class="cartridge">306 <div class="inner">307 <h3 class="cardhead">Spec Card</h3>308 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>309 <dl>310 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>311 <dt>Defines</dt><dd>25 management methods - JSON-RPC-like request/response</dd>312 <dt>Kinds</dt><dd>none — no new event kinds</dd>313 <dt>Requires</dt><dd><a href="98.html">NIP-98</a> auth - <a href="01.html">NIP-01</a> keys</dd>314 <dt>Transport</dt><dd>HTTP(S) - <code>application/nostr+json+rpc</code> - same URI as the WebSocket</dd>315 <dt>Crypto</dt><dd>signed NIP-98 event with required <code>payload</code> tag</dd>316 <dt>First commit</dt><dd>2024-11-25</dd>317 <dt>Last updated</dt><dd>2026-06-29</dd>318 </dl>319 </div>320 </div>321 </aside>322 </main>323324 <nav class="navbtns">325 <a href="85.html">◀ NIP-85: Trusted Assertions</a>326 <a href="87.html">NIP-87: Cashu and Fedimint Discoverability ▶</a>327 </nav>328329 <footer class="pagefoot">330 <a href="../index.html">Nostr Protocol Manual</a>331 <span class="pageno">NIP 86 / F4</span>332 </footer>333</div>334335<script src="../assets/webmcp.js" defer></script>336</body>337</html>