Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

Rust
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(())
}
Python
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()}")
JavaScript
Node.js
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());
}
Web
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());
}
React Native
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());
}
Kotlin
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()}")
}
Swift
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())")
}
C#
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.