Nostr Events Explained: Complete Technical Guide
Deep dive into Nostr events - structure, kinds, signatures, tags, and how events flow through the decentralized network.
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, legacy NIP-04; current DMs use NIP-17: kind 14, sealed in kind 13 and gift-wrapped in kind 1059)
- 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", "root"],
["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:
- Serialize the event data as
[0, pubkey, created_at, kind, tags, content] - SHA-256 hash the serialized JSON
- 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:
note1fd5h89pqdh5wf35t845467u7d4w3a83mf30e6l52dv7y6hn0029syv00lm
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):
npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6
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, 15:20:01 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 name (often, but not always, one letter; only single-letter tags are indexed by relays)
- 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): Plaintext (NIP-10: markup such as Markdown and HTML should not be used)
- kind:0 (profile): JSON metadata (name, about, picture)
- kind:4 (DM, legacy NIP-04): Encrypted string; current DMs use NIP-17 (kind 14, sealed in kind 13 and gift-wrapped in kind 1059)
- 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 (rendering varies)
7. Sig (Signature)
"sig": "<BIP-340 Schnorr signature of the id: 128 hex characters>"
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:
- Author’s client creates event
- Client calculates event ID (hash)
- Client signs ID with private key
- Signature added to event
- 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.
Replaceable Events
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: Your namedisplay_name: An optional, richer display name (NIP-24)about: Bio/descriptionpicture: Profile picture URLbanner: Banner image URLnip05: NIP-05 verification (email-like identifier)lud16: Lightning address (for zaps)website: Personal website
kind:3 - Follow List
Your list of followed public keys.
{
"kind": 3,
"content": "",
"tags": [
["p", "pubkey1", "wss://relay1.example.com", "Alice"],
["p", "pubkey2", "wss://relay2.example.com", "Bob"]
]
}
Characteristics:
- Replaceable: Newest overwrites
- Tags list followed pubkeys
- Content is unused (relay lists are kind 10002)
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: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 using the legacy NIP-04 encryption. Deprecated in favour of NIP-17, which wraps messages in NIP-59 gift wraps so the sender, recipient and timing are hidden too.
{
"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"],
["k", "1"]
]
}
The k tag names the kind of the event being deleted. To delete an addressable event, use an a tag (kind:pubkey:d-identifier).
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 reposts kind 1 notes only; other kinds use a generic repost, kind 16.
{
"kind": 6,
"content": "{original_event_json}",
"tags": [
["e", "original_event_id", "wss://relay.example.com"],
["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:
- Addressable (by “d” tag; kinds 30000-39999)
- Markdown content
- Tags include title, publish timestamp
- Used by Habla, Highlighter clients
Other Kinds
kind:40 - Channel Creation
Create public chat channel (NIP-28, now marked unrecommended; groups use NIP-29).
kind:41 - Channel Metadata
Update channel info (NIP-28, unrecommended).
kind:42 - Channel Message
Message in public channel (NIP-28, unrecommended).
kind:9735 - Zap Receipt
Proof of Lightning payment (zap).
kind:10000-39999 - Event Ranges
- 10000-19999: replaceable events
- 20000-29999: ephemeral events
- 30000-39999: addressable 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", "pubkey"]
Components:
"e"- Tag type- Event ID (hex only;
note1is for display) - Relay URL (optional, where to find event)
- Marker (optional): “reply” or “root”
- Author pubkey of the referenced event (optional)
Usage:
- Replies: Tag parent event
- Quotes: use a
qtag instead - 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 (Addressable)
Unique identifier for addressable 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:
-
Verify Signature:
- Extract pubkey
- Verify signature matches event ID
- Reject if invalid
-
Validate Event:
- Check required fields present
- Verify timestamp reasonable
- Apply relay policies (content rules, rate limits)
-
Store Event:
- Save to database
- Index by pubkey, kind, tags, timestamp
-
Respond to Client:
["OK", "event_id", true, ""]Or:
["OK", "event_id", false, "error: reason"] -
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:
- Verify signature
- Process event (display, store locally, etc.)
- 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)
Addressable 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"],
["k", "1"]
],
"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: Marked unrecommended in the NIPs repository
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:
- Events are self-contained: Everything needed is in the event
- Events are immutable: Signatures prevent tampering
- Events are portable: Any relay can store/distribute
- Events are verifiable: Cryptography, not trust
- 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. ⚡
Frequently asked questions
What are the fields in a Nostr event?
Every Nostr event is a JSON object with seven fields: id (a SHA-256 hash of the serialised event data), pubkey (the author's public key), created_at (a Unix timestamp), kind (an integer event type), tags (an array of metadata arrays), content (the message string), and sig (a Schnorr signature). The id and sig together let anyone verify the event's integrity and authorship.
How are Nostr events verified?
Events are verified cryptographically rather than by trusting relays. Any client recalculates the event id from its data, then checks the Schnorr signature against the author's public key. If the signature is valid, the event is authentic and unmodified; if any field was changed, the signature breaks. This prevents relays forging events and stops impersonation.
Can you delete a Nostr event?
Deletion on Nostr is a request, not a guarantee. You publish a kind 5 event with an 'e' tag referencing the event you want removed (NIP-09), but relays may honour or ignore it. There is no enforcement mechanism, so some relays will keep the original event - deletion is best effort only.