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
- You control the secret key that signed the original event.
- You know the event IDs (or coordinates for replaceable/parameterized events) you want to retract.
- You accept that the payload remains public even after the deletion request propagates.
Building the request
Rust
// 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)?;
Python
# 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)
JavaScript
// 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);
Kotlin
// 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)
Swift
// 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)
C#
// 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);
Flutter
TODO
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.