Events
An event is the immutable, signed unit exchanged by Nostr applications. Its ID commits to the author’s public key, creation time, kind, tags, and content; changing any of those fields produces a different event and requires a new signature.
EventBuilder collects kind, content, tags, and an optional timestamp. Finalization creates the canonical unsigned
representation; signing produces the Event that can be verified, serialized, stored, or published.
Because its ID and signature cover its fields, a finished Event is immutable.
Build, verify, and decode
This example creates a text note, verifies it, round-trips its protocol JSON, and verifies the decoded value again:
use nostr::prelude::*;
fn event_example() -> Result<(), Box<dyn std::error::Error>> {
let keys = Keys::generate();
let event = EventBuilder::new(Kind::TextNote, "Hello, Nostr!").finalize(&keys)?;
event.verify()?;
let decoded = Event::from_json(event.as_json())?;
decoded.verify()?;
println!("Event ID: {}", decoded.id.to_bech32()?);
Ok(())
}
from nostr_sdk import Event, EventBuilder, Filter, Kind, KindStandard, Keys, PublicKey, Timestamp
def event_example() -> None:
keys = Keys.generate()
event = EventBuilder(
Kind.from_std(KindStandard.TEXT_NOTE), "Hello, Nostr!"
).finalize(keys)
assert event.verify()
decoded = Event.from_json(event.as_json())
assert decoded.verify()
print(f"Event ID: {decoded.id().to_bech32()}")
import {
Event,
EventBuilder,
Filter,
Kind,
KindStandard,
Keys,
PublicKey,
Timestamp,
} from "@nostrdevkit/nostr-sdk-node";
function eventExample() {
const keys = Keys.generate();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello, Nostr!",
).finalize(keys);
if (!event.verify()) throw new Error("invalid event");
const decoded = Event.fromJson(event.asJson());
if (!decoded.verify()) throw new Error("invalid decoded event");
console.log("Event ID:", decoded.id().toBech32());
}
import {
Event,
EventBuilder,
Filter,
Kind,
KindStandard,
Keys,
PublicKey,
Timestamp,
uniffiInitAsync,
} from "@nostrdevkit/nostr-sdk-web";
await uniffiInitAsync();
function eventExample() {
const keys = Keys.generate();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello, Nostr!",
).finalize(keys);
if (!event.verify()) throw new Error("invalid event");
const decoded = Event.fromJson(event.asJson());
if (!decoded.verify()) throw new Error("invalid decoded event");
console.log("Event ID:", decoded.id().toBech32());
}
import {
Event,
EventBuilder,
Filter,
Kind,
KindStandard,
Keys,
PublicKey,
Timestamp,
} from "@nostrdevkit/nostr-sdk-react-native";
export function eventExample() {
const keys = Keys.generate();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello, Nostr!",
).finalize(keys);
if (!event.verify()) throw new Error("invalid event");
const decoded = Event.fromJson(event.asJson());
if (!decoded.verify()) throw new Error("invalid decoded event");
console.log("Event ID:", decoded.id().toBech32());
}
import org.nostrdevkit.sdk.*
fun eventExample() {
val keys = Keys.generate()
val event = EventBuilder(
Kind.fromStd(KindStandard.TEXT_NOTE),
"Hello, Nostr!",
).finalize(keys)
check(event.verify())
val decoded = Event.fromJson(event.asJson())
check(decoded.verify())
println("Event ID: ${decoded.id().toBech32()}")
}
import NostrSDK
func eventExample() throws {
let keys = Keys.generate()
let event = try EventBuilder(
kind: Kind.fromStd(e: .textNote),
content: "Hello, Nostr!"
).finalize(signer: keys)
precondition(event.verify())
let decoded = try Event.fromJson(json: event.asJson())
precondition(decoded.verify())
print("Event ID: \(try decoded.id().toBech32())")
}
using Nostr.Sdk;
public static class CoreExample
{
public static void EventExample()
{
var keys = Keys.Generate();
var @event = new EventBuilder(
Kind.FromStd(KindStandard.TextNote),
"Hello, Nostr!"
).Finalize(keys);
if (!@event.Verify()) throw new InvalidOperationException("Invalid event");
var decoded = Event.FromJson(@event.AsJson());
if (!decoded.Verify()) throw new InvalidOperationException("Invalid decoded event");
Console.WriteLine($"Event ID: {decoded.Id().ToBech32()}");
}
}
Event identity and retries
Changing any signed input produces a different event ID and requires a new signature. Retain the signed Event and
resend it when retrying publication.
Rebuilding from an EventBuilder is a new creation attempt. With the default timestamp it creates a new event, even if
the displayed content is identical. That may be intentional for a new note, but it is not an idempotent retry.
The next chapter uses typed event fields to describe a relay query with a filter.