diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 403bfd3..24f7d21 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -108,7 +108,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '9.0.x'
+ dotnet-version: '10.0.x'
- name: Check
working-directory: snippets/csharp
diff --git a/snippets/csharp/Nip09.cs b/snippets/csharp/Nip09.cs
new file mode 100644
index 0000000..7bea384
--- /dev/null
+++ b/snippets/csharp/Nip09.cs
@@ -0,0 +1,22 @@
+namespace Snippets;
+
+using Nostr.Sdk;
+
+public class Nip09
+{
+ public static void Run()
+ {
+ var keys = Keys.Parse("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683");
+
+ // ANCHOR: build
+ // Construct the request
+ var id = EventId.Parse("7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96");
+ var request = new EventDeletionRequest(ids: [id], coordinates: [], reason: "these posts were published by accident");
+
+ // Build the event
+ Event e = EventBuilder.Delete(request).SignWithKeys(keys);
+ // ANCHOR_END: build
+
+ Console.WriteLine($"Event deletion request: {e.AsPrettyJson()}");
+ }
+}
\ No newline at end of file
diff --git a/snippets/csharp/Program.cs b/snippets/csharp/Program.cs
index c73c55a..5ad3226 100644
--- a/snippets/csharp/Program.cs
+++ b/snippets/csharp/Program.cs
@@ -19,6 +19,9 @@ public static async Task Main()
// Event Building
await EventBuilding.Build();
+ // NIP09
+ Nip09.Run();
+
// NIP44
Nip44.Run();
diff --git a/snippets/csharp/Snippets.csproj b/snippets/csharp/Snippets.csproj
index eb4e6bd..2b6f06e 100644
--- a/snippets/csharp/Snippets.csproj
+++ b/snippets/csharp/Snippets.csproj
@@ -2,7 +2,7 @@
Exe
- net9.0
+ net10.0
enable
enable
diff --git a/snippets/js/src/nip09.ts b/snippets/js/src/nip09.ts
new file mode 100644
index 0000000..1b15716
--- /dev/null
+++ b/snippets/js/src/nip09.ts
@@ -0,0 +1,24 @@
+import {EventBuilder, EventDeletionRequest, EventId, Keys, loadWasmSync} from "@rust-nostr/nostr-sdk";
+
+function run() {
+ // Load WASM
+ loadWasmSync();
+
+ let keys = Keys.parse("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683");
+
+ // ANCHOR: build
+ // Construct the request
+ let id = EventId.parse("7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96");
+ let request = new EventDeletionRequest();
+ request.ids = [id];
+ request.reason = "these posts were published by accident";
+
+
+ // Build the event
+ let event = EventBuilder.delete(request).signWithKeys(keys);
+ // ANCHOR_END: build
+
+ console.log("Event deletion request: " + event.asPrettyJson())
+}
+
+run();
diff --git a/snippets/kotlin/src/main/kotlin/nip09.kt b/snippets/kotlin/src/main/kotlin/nip09.kt
new file mode 100644
index 0000000..fc91de2
--- /dev/null
+++ b/snippets/kotlin/src/main/kotlin/nip09.kt
@@ -0,0 +1,24 @@
+package rust.nostr.snippets
+
+// ANCHOR: full
+import rust.nostr.sdk.*
+
+fun nip09() {
+ val keys: Keys = Keys.parse("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683")
+
+ // ANCHOR: build
+ // Construct the request
+ val id = EventId.parse("7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96")
+ val request = EventDeletionRequest(ids = listOf(id), coordinates = listOf(), reason = "these posts were published by accident")
+
+ // Build the event
+ val event: Event = EventBuilder.delete(request).signWithKeys(keys)
+ // ANCHOR_END: build
+
+ println("Event deletion request: ${event.asPrettyJson()}")
+}
+
+fun main() {
+ nip09()
+}
+// ANCHOR_END: full
diff --git a/snippets/python/src/nip09.py b/snippets/python/src/nip09.py
new file mode 100644
index 0000000..cae0b30
--- /dev/null
+++ b/snippets/python/src/nip09.py
@@ -0,0 +1,21 @@
+# ANCHOR: full
+from nostr_sdk import Keys, EventDeletionRequest, EventId, Event, EventBuilder
+
+
+def nip09():
+ keys: Keys = Keys.parse("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683")
+
+ # ANCHOR: build
+ # Construct the request
+ id = EventId.parse("7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96")
+ request = EventDeletionRequest(ids=[id], coordinates=[], reason="these posts were published by accident")
+
+ # Build the event
+ event: Event = EventBuilder.delete(request).sign_with_keys(keys)
+ # ANCHOR_END: build
+
+ print(f"Event deletion request: {event.as_pretty_json()}")
+
+if __name__ == '__main__':
+ nip09()
+# ANCHOR_END: full
\ No newline at end of file
diff --git a/snippets/rust/src/main.rs b/snippets/rust/src/main.rs
index 7d1d4a6..80d8e42 100644
--- a/snippets/rust/src/main.rs
+++ b/snippets/rust/src/main.rs
@@ -13,6 +13,7 @@ mod nip47;
mod nip49;
mod nip59;
mod req;
+mod nip09;
#[tokio::main]
async fn main() {}
diff --git a/snippets/rust/src/nip09.rs b/snippets/rust/src/nip09.rs
new file mode 100644
index 0000000..55975f8
--- /dev/null
+++ b/snippets/rust/src/nip09.rs
@@ -0,0 +1,23 @@
+// ANCHOR: full
+use nostr_sdk::prelude::*;
+
+pub fn nip09() -> Result<()> {
+ let keys: Keys = Keys::parse("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683")?;
+
+ // ANCHOR: build
+ // Construct the request
+ let request = EventDeletionRequest::new()
+ .id(EventId::from_hex("7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96")?)
+ // optionally add coordinates for replaceable events
+ // .coordinate(...)
+ .reason("these posts were published by accident");
+
+ // Build the event
+ let event: Event = EventBuilder::delete(request).sign_with_keys(&keys)?;
+ // ANCHOR_END: build
+
+ println!("Event deletion request: {}", event.as_pretty_json());
+
+ Ok(())
+}
+// ANCHOR_END: full
diff --git a/snippets/swift/Sources/Nip09.swift b/snippets/swift/Sources/Nip09.swift
new file mode 100644
index 0000000..b192d49
--- /dev/null
+++ b/snippets/swift/Sources/Nip09.swift
@@ -0,0 +1,20 @@
+// ANCHOR: full
+import Foundation
+import NostrSDK
+
+func nip09() throws {
+ let keys = try Keys.parse(secretKey: "3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683")
+
+ // ANCHOR: build
+ // Construct the request
+ let id = try EventId.parse(id: "7469af3be8c8e06e1b50ef1caceba30392ddc0b6614507398b7d7daa4c218e96")
+ let request = EventDeletionRequest(ids: [id], coordinates: [], reason: "these posts were published by accident")
+
+ // Build the event
+ let builder = EventBuilder.delete(request: request)
+ let event = try builder.signWithKeys(keys: keys)
+ // ANCHOR_END: build
+
+ print("Event deletion request: \(try event.asPrettyJson())");
+}
+// ANCHOR_END: full
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 03d789b..2213295 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -37,6 +37,7 @@
- [NIP-05: Mapping nostr keys to DNS-based internet identifiers](sdk/nips/05.md)
- [NIP-06: Key derivation from seed phrase](sdk/nips/06.md)
- [NIP-07: Browser Extension Signer](sdk/nips/07.md)
+ - [NIP-09: Event Deletion Request](sdk/nips/09.md)
- [NIP-17: Private Direct Messages](sdk/nips/17.md)
- [NIP-19: bech32-encoded entities](sdk/nips/19.md)
- [NIP-21: nostr URI scheme](sdk/nips/21.md)
diff --git a/src/sdk/nips/09.md b/src/sdk/nips/09.md
new file mode 100644
index 0000000..7bd7575
--- /dev/null
+++ b/src/sdk/nips/09.md
@@ -0,0 +1,88 @@
+# NIP-09: Event Deletion Request
+
+[NIP-09] defines a best-effort mechanism for retracting events you previously published.
+It does **not** guarantee erasure—relays and downstream clients are allowed to cache or ignore deletion requests—so treat it as a courtesy protocol for well-behaved peers.
+
+## When to send a deletion event
+
+1. You control the secret key that signed the original event.
+2. You know the event IDs (or coordinates for replaceable/parameterized events) you want to retract.
+3. You accept that the payload remains public even after the deletion request propagates.
+
+## Building the request
+
+
+
+Rust
+
+
+```rust,ignore
+{{#include ../../../snippets/rust/src/nip09.rs:build}}
+```
+
+
+
+Python
+
+
+```python,ignore
+{{#include ../../../snippets/python/src/nip09.py:build}}
+```
+
+
+
+JavaScript
+
+
+```typescript,ignore
+{{#include ../../../snippets/js/src/nip09.ts:build}}
+```
+
+
+
+Kotlin
+
+
+```kotlin,ignore
+{{#include ../../../snippets/kotlin/src/main/kotlin/nip09.kt:build}}
+```
+
+
+
+Swift
+
+
+```swift,ignore
+{{#include ../../../snippets/swift/Sources/Nip09.swift:build}}
+```
+
+
+
+C#
+
+
+```cs,ignore
+{{#include ../../../snippets/csharp/Nip09.cs:build}}
+```
+
+
+
+Flutter
+
+
+
+Then, the deletion event can be sent to every relay that received the original event.
+
+## Caveats
+
+- Relays are free to ignore deletion events entirely or only apply them to new subscribers.
+- Archive relays and scrapers can continue serving the old content indefinitely.
+- Deleting a parameterized replaceable event without its coordinate will have no effect.
+
+In short: use [NIP-09] as part of your UX, but do not treat it as a hard delete.
+
+[NIP-09]: https://github.com/nostr-protocol/nips/blob/master/09.md