Chapter 38 - Social
38
Nostr Implementation Possibility - Hex № 38

User Statuses

A one-line live status next to your name: what you do right now, or what track you listen to.

1 Overview

Instant messengers of the 2000s had a status line: "away", "busy", "listening to...". This NIP brings that feature to Nostr. A user publishes a short status event. Clients show the status next to the username on posts and profiles.

A status is an addressable event of kind:30315, as defined in NIP-01. The d tag holds the status type. A relay keeps only the latest event per pubkey, kind, and d value. A new status therefore replaces the old one of the same type automatically.

A status can expire on its own. The event is optionally expiring: add an expiration tag (see NIP-40) and the status goes stale at that timestamp. A music status expires when the track ends. No cleanup event is needed.

Hint!

Think of each d value as one status slot. The general slot and the music slot live side by side. Updating one slot does not touch the other.

Client A sets status Relay slot d = "general" Relay slot d = "music" Client B shows status 30315 latest only
Fig. 38-1 — Status slots. Each d value is a slot. The relay keeps the latest event per slot. Readers always get the current status.

2 The Status Event (kind 30315)

The event named "User Status" uses kind:30315. The content is the status text. The d tag names the status type.

Wire format - kind 30315 user status
{
  "kind": 30315,
  "content": <status text, may contain emoji>,
  "tags": [
    ["d", <status type, e.g. "general" or "music">],
    ["r", <URL, optional>],
    ["expiration", <unix timestamp, optional>]
  ]
}
Tag reference
TagRequiredRule
dyesThe status type. Selects the addressable slot per NIP-01.
roptionalLinks the status to a URL.
poptionalLinks the status to a profile (pubkey).
eoptionalLinks the status to a note (event id).
aoptionalLinks the status to an addressable event.
expirationoptionalUnix timestamp when the status stops being valid. See NIP-40.

A status MAY include one r, p, e, or a tag. The tag turns the status into a link. Example: a "in a meeting" status can carry an r tag with the meeting URL.

Status types

The d tag value is the status type. This NIP defines two common types:

Defined status types (d tag values)
TypeMeaningExpiry
generalA general status: "Working", "Hiking", "Out of office".Optional.
musicThe track the user listens to right now. A live "now playing" stream.Set to the time when the track stops playing.

Clients can use any other d value as a custom status type. This NIP does not define those types.

Content rules

  • The content is the status text.
  • The content MAY include emoji, or NIP-30 custom emoji.
  • An empty content string clears the status. The client should stop showing it.
Tech Note

Source issues, reported as found. (1) Both JSON examples in the source have a trailing comma after the tags array, so they are not valid JSON as printed. (2) The music example spells the Beastie Boys track as "Intergalatic" instead of "Intergalactic". (3) The source says the event is "optionally expiring" and uses an expiration tag but never cites NIP-40, which defines that tag.

3 Status Lifecycle

A status has three exits. A newer event with the same d value replaces it. Its expiration timestamp passes. Or an event with empty content clears it.

LIVE Replaced Expired Cleared newer event, same d expiration timestamp passes event with empty content
Fig. 38-2 — Three ways out. A live status ends by replacement, by expiry, or by an explicit clear.

4 Client Behavior

Clients MAY display the status next to the username on posts or on profiles. The status gives live user information at a glance.

To read statuses, subscribe with a filter on kind:30315 and the authors you follow. Ignore statuses whose expiration timestamp is in the past. Ignore statuses with empty content.

The source lists these use cases:

  • A calendar app updates your general status when you are in a meeting.
  • Nostr Nests updates your general status with a link to the nest when you join.
  • A music streaming service updates your music status while you listen.
  • A podcast app updates your music status with a link so others can listen too.
  • A client reads the system media player and updates the music status.
Warning!

Expiry is cooperative. A relay that implements NIP-40 can drop expired statuses, but clients must still check the expiration tag themselves. Do not show a "now playing" line from last week.

5 Worked Example

Alice sets a general status with a link, streams a track as her music status, and later clears the general status. The transcript shows the full session. Orange lines go to the relay. Blue lines come from the relay.

Session transcript - wss://relay.example.com
-- Alice sets her general status --
→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692840000,
     "content":"Sign up for nostrasia!",
     "tags":[["d","general"], ["r","https://nostr.world"]],
     "id":"7d1f04...", "sig":"9b21ac..."}]
← ["OK", "7d1f04...", true, ""]

-- Alice's player sets a music status that expires with the track --
→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692845400,
     "content":"Intergalatic - Beastie Boys",
     "tags":[["d","music"],
             ["r","spotify:search:Intergalatic%20-%20Beastie%20Boys"],
             ["expiration","1692845589"]],
     "id":"c04b9e...", "sig":"31d7f0..."}]
← ["OK", "c04b9e...", true, ""]

-- Bob's client fetches Alice's statuses --
→ ["REQ", "status", {"kinds":[30315], "authors":["a1ce55..."]}]
← ["EVENT", "status", {"kind":30315, "content":"Sign up for nostrasia!", ...}]
← ["EVENT", "status", {"kind":30315, "content":"Intergalatic - Beastie Boys", ...}]
← ["EOSE", "status"]

-- Later Alice clears the general slot: empty content --
→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692850000,
     "content":"", "tags":[["d","general"]],
     "id":"e83a11...", "sig":"04c2b8..."}]
← ["OK", "e83a11...", true, ""]
-- Bob's client sees the empty content and stops showing the status --

6 Version History

NIP-38 started life two days early as "NIP-315", named after its kind number. Notable changes, newest first:

  • 20252025-08Fixed heading levels in the document.
  • 20242024-08Renamed "parameterized replaceable event" to "addressable event" across the repository, following the NIP-01 terminology change.
  • 20242024-05Moved the content rules (emoji, empty string clears the status) into the Live Statuses section.
  • 2023-242023-2024Removed the author list; JSON and header formatting cleanups.
  • 20232023-08Added the content description: emoji and NIP-30 custom emoji allowed, empty content clears the status.
  • 20232023-08Renamed NIP-315 to NIP-38.
  • Origin2023-08-23William Casarin published "NIP-315: User Statuses" with kind 30315.

Original Proposal - Credits

AuthorWilliam Casarin
First commit2023-08-23 — “NIP-315: User Statuses”
Statusdraft - optional
Source38.md on GitHub - full change history

7 See Also

  • NIP-01 — addressable events, the d tag, and the r/p/e/a tag shapes this NIP builds on.
  • NIP-40 — the expiration tag that makes a status self-destruct.
  • NIP-30 — custom emoji allowed inside the status content.
  • NIP-24 — profile metadata; the static counterpart to a live status.