technical intermediate ⏱️ 13 minutes

Nostr Events Explained: Complete Technical Guide

Deep dive into Nostr events - structure, kinds, signatures, tags, and how events flow through the decentralized network.

Updated: 19 January 2025 By Nostr.co.uk

Introduction

Everything you do on Nostr—posting, liking, messaging, following—is an event. Understanding events is understanding Nostr at the protocol level.

This guide explains event structure, types, how they’re created and verified, and why this simple design makes Nostr powerful.

What is a Nostr Event?

The Core Concept

An event is a signed piece of data representing an action or content on Nostr.

Examples of Events:

  • A post you write (kind:1)
  • A reaction/like (kind:7)
  • Your profile information (kind:0)
  • Your follow list (kind:3)
  • An encrypted message (kind:4)
  • A deletion request (kind:5)

Key Characteristics:

  • Events are immutable (once signed, content can’t change)
  • Events are verifiable (signature proves authorship)
  • Events are portable (any relay can store them)
  • Events are self-contained (include all necessary metadata)

The Event Structure

Every Nostr event follows this JSON structure:

{
  "id": "4b697394206de8e4c68b3d695d7b9e6d5d1e9e3b4c5f9d7e8a6b3c4d5e6f7a8b",
  "pubkey": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
  "created_at": 1674055201,
  "kind": 1,
  "tags": [
    ["e", "referenced_event_id", "wss://relay.example.com"],
    ["p", "mentioned_pubkey"]
  ],
  "content": "Hello Nostr! This is my first post.",
  "sig": "signature_hex_here"
}

Let’s break down each field.

Event Fields Explained

1. ID (Event Identifier)

"id": "4b697394206de8e4c68b3d695d7b9e6d5d1e9e3b4c5f9d7e8a6b3c4d5e6f7a8b"

What it is:

  • SHA-256 hash of serialized event data
  • 64 hexadecimal characters
  • Unique identifier for this event

How it’s calculated:

  1. Serialize event data (pubkey, created_at, kind, tags, content)
  2. SHA-256 hash the serialized JSON
  3. Result is the event ID

Purpose:

  • Unique reference to this event
  • Used for replies, quotes, reactions
  • Prevents duplicates
  • Verifies event integrity

Format (NIP-19):

  • Raw: 4b697394206de8e4c68b3d695d7b9e6d5d1e9e3b4c5f9d7e8a6b3c4d5e6f7a8b
  • Bech32: note1fd5h8dpqm6xwf35t84v44au7d4w3n03mfh0e6l52dvly6hnk0zdqz4dgt9

2. Pubkey (Author’s Public Key)

"pubkey": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"

What it is:

  • The author’s public key (64 hex characters)
  • Identifies who created this event
  • Used to verify the signature

Purpose:

  • Proves authorship (via signature verification)
  • Links event to an identity
  • Allows filtering by author

Format:

  • Raw hex: 3bf0c63...
  • Bech32 (npub): npub180cvv83m27afv2pzhd97pl3aw5lu729x0cmnj7zv0edxcnjle67q23pczl

3. Created_at (Timestamp)

"created_at": 1674055201

What it is:

  • Unix timestamp (seconds since Jan 1, 1970 UTC)
  • When the event was created
  • Set by the author’s client

Purpose:

  • Chronological ordering
  • Time-based filtering
  • Determining recency

Important Notes:

  • Client-side timestamp (not verified by relays)
  • Can be set to any value (future or past)
  • Relays may reject events with unreasonable timestamps
  • Used for sorting feeds

Example: 1674055201 = January 18, 2023, 17:13:21 UTC

4. Kind (Event Type)

"kind": 1

What it is:

  • Integer indicating event type
  • Defines how to interpret the event
  • Different kinds serve different purposes

Purpose:

  • Categorizes events
  • Clients filter by kind
  • Determines event handling

Common Kinds (we’ll cover this in detail below)

5. Tags (Metadata Array)

"tags": [
  ["e", "referenced_event_id", "wss://relay.example.com", "reply"],
  ["p", "mentioned_pubkey"],
  ["t", "nostr"],
  ["t", "bitcoin"]
]

What it is:

  • Array of arrays (each tag is an array)
  • Metadata about the event
  • References to other events, users, topics

Purpose:

  • Link events together (replies, quotes)
  • Mention users
  • Add hashtags
  • Provide context
  • Relay hints

Tag Structure:

  • First element: tag type (letter)
  • Remaining elements: tag values
  • Variable length

Common Tags (we’ll cover in detail below)

6. Content

"content": "Hello Nostr! This is my first post."

What it is:

  • The actual content/message
  • Always a string
  • Can be empty

Format Depends on Kind:

  • kind:1 (note): Plain text, Markdown, or formatted text
  • kind:0 (profile): JSON metadata (name, about, picture)
  • kind:4 (DM): Encrypted string
  • kind:30023 (long-form): Markdown article
  • kind:7 (reaction): Emoji or ”+” for like

Encoding:

  • UTF-8 string
  • Can include emojis, special characters
  • Clients may support Markdown, HTML (rendering varies)

7. Sig (Signature)

"sig": "3045022100d8c6e8f7a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7..."

What it is:

  • Schnorr signature (secp256k1)
  • Signs the event ID with the author’s private key
  • 128 hexadecimal characters

Purpose:

  • Proves the author created this event
  • Prevents tampering (changing any field breaks signature)
  • Allows verification without trusting relays

How it Works:

  1. Author’s client creates event
  2. Client calculates event ID (hash)
  3. Client signs ID with private key
  4. Signature added to event
  5. Recipients verify signature using author’s public key

Verification:

Valid Signature = Sign(Event ID, Private Key)
Verification = Verify(Event ID, Signature, Public Key)

If verification succeeds, the event is authentic and unmodified.

Event Kinds (Types)

Event kind determines what the event represents.

Regular Events (Replaceable)

kind:0 - Profile Metadata

Stores user profile information.

{
  "kind": 0,
  "content": "{\"name\":\"Alice\",\"about\":\"Nostr enthusiast\",\"picture\":\"https://example.com/pic.jpg\"}",
  "tags": []
}

Characteristics:

  • Replaceable: Newest overwrites older
  • Only one kind:0 per pubkey matters (latest)
  • JSON string in content field

Content Fields (common):

  • name: Display name
  • about: Bio/description
  • picture: Profile picture URL
  • banner: Banner image URL
  • nip05: NIP-05 verification (email-like identifier)
  • lud16: Lightning address (for zaps)
  • website: Personal website

kind:3 - Contact List (Follow List)

Your list of followed public keys.

{
  "kind": 3,
  "content": "",
  "tags": [
    ["p", "pubkey1", "wss://relay1.com", "Alice"],
    ["p", "pubkey2", "wss://relay2.com", "Bob"]
  ]
}

Characteristics:

  • Replaceable: Newest overwrites
  • Tags list followed pubkeys
  • Content usually empty (or relay list)

Tag Structure:

  • ["p", "pubkey", "relay_hint", "petname"]
  • petname: optional friendly name

Ephemeral Events

kind:20000-29999 - Ephemeral Events

Events not meant to be stored.

Examples:

  • kind:20001 - Authentication events
  • kind:22242 - Client authentication

Characteristics:

  • Relays may not store them
  • Meant for real-time communication
  • Short-lived

Regular Events (Non-Replaceable)

kind:1 - Short Text Note

Standard social media post.

{
  "kind": 1,
  "content": "This is a note!",
  "tags": [
    ["t", "nostr"]
  ]
}

Characteristics:

  • Most common event type
  • Plain text (clients may render Markdown)
  • Can include mentions, hashtags, links

kind:4 - Encrypted Direct Message

Private message (NIP-04 encrypted).

{
  "kind": 4,
  "content": "encrypted_string_here",
  "tags": [
    ["p", "recipient_pubkey"]
  ]
}

Characteristics:

  • Content encrypted using recipient’s public key
  • Only sender and recipient can decrypt
  • Metadata (who messaged whom) is public
  • See Privacy Guide for limitations

kind:5 - Event Deletion

Request to delete previous event.

{
  "kind": 5,
  "tags": [
    ["e", "event_id_to_delete"]
  ]
}

Characteristics:

  • Requests deletion of your own events
  • Relays may honor or ignore
  • Not enforceable (some relays will keep event)
  • Best effort deletion

kind:6 - Repost

Sharing another’s event (like retweet).

{
  "kind": 6,
  "content": "{original_event_json}",
  "tags": [
    ["e", "original_event_id"],
    ["p", "original_author_pubkey"]
  ]
}

kind:7 - Reaction

Like or emoji reaction.

{
  "kind": 7,
  "content": "+",
  "tags": [
    ["e", "reacted_event_id"],
    ["p", "reacted_event_author"]
  ]
}

Content:

  • "+" = like/upvote
  • "-" = dislike
  • Emoji = custom reaction (❤️, 😂, etc.)

Long-Form Content

kind:30023 - Long-Form Article

Blog post, article, long content.

{
  "kind": 30023,
  "content": "# Article Title\n\nMarkdown content here...",
  "tags": [
    ["d", "article-slug"],
    ["title", "Article Title"],
    ["published_at", "1674055201"],
    ["t", "nostr"],
    ["t", "bitcoin"]
  ]
}

Characteristics:

  • Parameterized Replaceable (by “d” tag)
  • Markdown content
  • Tags include title, publish timestamp
  • Used by Habla, Highlighter clients

Other Kinds

kind:40 - Channel Creation

Create public chat channel.

kind:41 - Channel Metadata

Update channel info.

kind:42 - Channel Message

Message in public channel.

kind:9735 - Zap Receipt

Proof of Lightning payment (zap).

kind:10000+ - Special Events

Various application-specific events.

See NIPs documentation for complete list.

Tags Explained

Tags provide metadata and link events together.

Common Tag Types

“e” Tag - Event Reference

References another event.

["e", "event_id", "relay_hint", "marker"]

Components:

  1. "e" - Tag type
  2. Event ID (hex or note1…)
  3. Relay URL (optional, where to find event)
  4. Marker (optional): “reply”, “root”, “mention”

Usage:

  • Replies: Tag parent event
  • Quotes: Reference quoted event
  • Threads: Link to root event

Example (reply to event):

["e", "original_event_id", "wss://relay.damus.io", "reply"]

“p” Tag - Pubkey Reference

Mentions or references a user.

["p", "pubkey", "relay_hint"]

Usage:

  • Mention user in note
  • Tag author of replied/quoted event
  • DM recipient
  • Follow list entries

Example:

["p", "3bf0c63fcb...", "wss://relay.damus.io"]

“t” Tag - Hashtag/Topic

Categorizes event by topic.

["t", "nostr"]

Usage:

  • Hashtags for discovery
  • Topic indexing
  • Search optimization

Example (multiple tags):

"tags": [
  ["t", "nostr"],
  ["t", "bitcoin"],
  ["t", "censorship-resistance"]
]

“d” Tag - Identifier (Parameterized Replaceable)

Unique identifier for replaceable events.

["d", "article-slug"]

Usage:

  • Identify which long-form article to replace
  • Allow multiple replaceable events of same kind
  • Create “addressable” events

Example:

  • kind:30023 with ["d", "my-first-article"]
  • Later update: same kind + same “d” tag → replaces first

Other Tags:

  • ["a", "kind:pubkey:d-tag"] - Addressable event reference
  • ["r", "url"] - URL reference
  • ["g", "geohash"] - Geolocation
  • ["nonce", "value", "difficulty"] - Proof of work
  • ["subject", "text"] - Subject line (DMs)
  • ["title", "text"] - Article title

Custom Tags: Apps can define their own

Event Lifecycle

Creation

Step 1: Client Creates Event

User interacts with client (writes post, clicks like, etc.)

Step 2: Client Builds Event Object

const event = {
  pubkey: user_pubkey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 1,
  tags: [],
  content: "Hello Nostr!"
};

Step 3: Calculate Event ID

const event_data = JSON.stringify([
  0,
  event.pubkey,
  event.created_at,
  event.kind,
  event.tags,
  event.content
]);
const event_id = sha256(event_data);
event.id = event_id;

Step 4: Sign Event

event.sig = sign(event.id, user_private_key);

Step 5: Publish to Relays

Client sends signed event to configured relays.

Relay Storage

Relay Receives Event:

  1. Verify Signature:

    • Extract pubkey
    • Verify signature matches event ID
    • Reject if invalid
  2. Validate Event:

    • Check required fields present
    • Verify timestamp reasonable
    • Apply relay policies (content rules, rate limits)
  3. Store Event:

    • Save to database
    • Index by pubkey, kind, tags, timestamp
  4. Respond to Client:

    ["OK", "event_id", true, ""]
    

    Or:

    ["OK", "event_id", false, "error: reason"]
    
  5. Distribute to Subscribers:

    • Send event to clients subscribed to matching filters

Distribution

Clients Subscribe:

["REQ", "subscription_id", {"kinds": [1], "authors": ["pubkey1", "pubkey2"]}]

Relay Sends Matching Events:

["EVENT", "subscription_id", {event_object}]

Client Receives:

  1. Verify signature
  2. Process event (display, store locally, etc.)
  3. Update UI

Verification

Anyone Can Verify:

Every client verifies every event signature:

1. Extract pubkey from event
2. Recalculate event ID from event data
3. Verify signature: verify(event.id, event.sig, event.pubkey)
4. If valid: event is authentic
5. If invalid: reject event

This Prevents:

  • Relays forging events
  • Modified events
  • Impersonation

Trust Model: Don’t trust relays, trust cryptography.

Special Event Behaviors

Replaceable Events

Kinds: 0, 3, and 10000-19999

Behavior:

  • Only the newest event of this kind (per pubkey) is valid
  • Older events discarded
  • Allows updating profile, follow list, etc.

Example: Two kind:0 events from same pubkey

  • First: created_at: 1674050000
  • Second: created_at: 1674060000
  • Only second is valid (newer timestamp)

Parameterized Replaceable Events

Kinds: 30000-39999

Behavior:

  • Replaceable based on kind + pubkey + “d” tag
  • Allows multiple replaceable events of same kind
  • Each “d” tag value creates separate replaceable set

Example: Long-form articles (kind:30023)

  • Article 1: kind:30023, d:“first-article”
  • Article 2: kind:30023, d:“second-article”
  • Update Article 1: kind:30023, d:“first-article” (newer timestamp)
  • Two articles coexist, each replaceable independently

Ephemeral Events

Kinds: 20000-29999

Behavior:

  • Relays may not store
  • Used for real-time, temporary data
  • No expectation of persistence

Example: Authentication challenges

Advanced Event Features

Event Deletion (NIP-09)

Request deletion of your own events.

Deletion Event:

{
  "kind": 5,
  "tags": [
    ["e", "event_id_to_delete"],
    ["e", "another_event_id"]
  ],
  "content": "Reason for deletion (optional)"
}

Relay Behavior (varies):

  • May delete events
  • May keep events
  • No enforcement mechanism
  • Best effort

Implication: Nostr deletions are requests, not guarantees.

Proof of Work (NIP-13)

Optional difficulty requirement.

Purpose:

  • Anti-spam measure
  • Relays can require computational work

Implementation:

"tags": [
  ["nonce", "1234567", "20"]
]

How It Works:

  • Increase nonce until event ID has required leading zeros
  • Higher difficulty = more computation
  • Relays reject events not meeting difficulty

Usage: Limited currently, experimental

Delegation (NIP-26)

Allow another key to post on your behalf.

Use Case:

  • Bot posting for you
  • Temporary delegation
  • Multi-device signing

Implementation: Creates delegation certificate signed by primary key

Status: Specified but limited adoption

Working with Events

Filtering Events

Clients filter events when subscribing:

Filter Examples:

All notes from specific authors:

{"kinds": [1], "authors": ["pubkey1", "pubkey2"]}

Events mentioning you:

{"kinds": [1, 6, 7], "#p": ["your_pubkey"]}

Events with hashtag:

{"kinds": [1], "#t": ["nostr"]}

Recent events:

{"kinds": [1], "since": 1674050000, "limit": 50}

Specific event:

{"ids": ["event_id"]}

Event Limits and Pagination

Limit Parameter:

{"kinds": [1], "limit": 20}

Returns maximum 20 events.

Pagination (via timestamps):

{"kinds": [1], "until": 1674050000, "limit": 20}

Fetch events before timestamp, useful for infinite scroll.

Conclusion

Events are the atomic unit of Nostr—signed, verifiable, portable pieces of data flowing through a decentralized network.

Key Takeaways:

  1. Events are self-contained: Everything needed is in the event
  2. Events are immutable: Signatures prevent tampering
  3. Events are portable: Any relay can store/distribute
  4. Events are verifiable: Cryptography, not trust
  5. Event kinds define purpose and behavior

Understanding events means understanding Nostr at the protocol level. Everything builds on this simple, powerful foundation.

Your posts are events. Your likes are events. Your identity is events.

That’s the beauty of Nostr.

Further Resources

  • How Nostr Works - High-level protocol explanation
  • NIP-01 - Basic protocol and event structure
  • NIP-10 - Text note references (replies, threads)
  • NIP-25 - Reactions
  • All NIPs - Complete protocol specifications

Remember: Events are simple JSON objects, signed and verified. This simplicity enables everything Nostr does. ⚡