NIPs by PolleramaCommunity NIPs, surfaced by trustConnect
npub17c2szua46mc...

Typing Indicator - NIP29 Group Chat

Published Jul 13, 2026
kind 23311 · Typing Indicator

Summary

Define a lightweight typing indicator for NIP-29 groups as an ephemeral Nostr event scoped to a group with the existing h tag.

This is intentionally not stored state. It is a short-lived presence signal for clients that are watching the same group relay.

Event

{
  "kind": 23311,
  "content": "",
  "tags": [
    ["h", "<group-id>"]
  ]
}

Optional tags:

["client", "<client-name>"]

Semantics

  • kind:23311 means the author is currently typing in the group identified by the h tag.
  • kind:23311 falls in the ephemeral range defined by NIP-01 (20000 <= kind < 30000). Relays are not expected to store it, and clients MUST NOT treat it as durable group history.
  • The event MUST be published to the group's host relay, not broadcast to unrelated relays.
  • The event MUST include exactly one h tag with the NIP-29 group id.
  • content SHOULD be an empty string. Clients MUST ignore any content they do not understand and treat every valid event of this kind as a plain typing signal. Future revisions may define explicit state values, such as recording or uploading, without breaking this fallback.

Sender Behavior

Clients SHOULD publish a typing event when the local user starts or continues typing a non-empty draft.

Recommended rate limits:

  • Send immediately when typing starts.
  • Send at most once every 4 seconds while typing continues.
  • Stop sending when the draft is empty or after the message is sent.

Typing events SHOULD NOT include the NIP-29 previous tag. They are live signals, not part of the group timeline, and referencing them from other events is meaningless.

Clients SHOULD NOT send typing events for users who cannot write to the group.

Receiver Behavior

Clients SHOULD show a user as typing for a short window after receiving the latest valid event from that user.

Recommended expiry:

  • 8 seconds after the newest typing event from the same pubkey and group.

Clients SHOULD:

  • Ignore typing events authored by the logged-in user.
  • Ignore typing events without a matching h tag.
  • Ignore typing events from non-members when the group member list is known.
  • Collapse multiple typing users into a compact label, for example Alice and Bob are typing.

Clients MUST NOT add typing events to the visible message timeline or unread counters.

Relay Behavior

  • Normal NIP-29 write rules apply unchanged: the group must exist, and in restricted groups only members can publish.
  • Relays SHOULD NOT require previous timeline references for ephemeral kinds. The reference implementation, relay29, already validates the previous tag only when it is present.
  • Groups that advertise a supported_kinds list in their kind:39000 metadata SHOULD include 23311 when typing indicators are supported. Clients MAY treat its absence from an advertised list as a signal to not send typing events to that group.

Rationale

NIP-29 already scopes group user events with the h tag and explicitly allows groups to accept any event kind carrying it. NIP-01 defines ephemeral event kinds in the 20000 <= kind < 30000 range. A typing indicator naturally fits this model because it is live presence, not history.

The kind 23311 was picked as a free number in the ephemeral range: at the time of writing it has no claim in the public registry of kinds and no known conflicting public usage. It also matches a number already used internally as a typing signal by at least one existing client stack before wrapping and encrypting it, which gives implementers a convergence point.

This event is implemented and working in a production NIP-29 client.

Compatibility

Clients that do not understand kind:23311 will ignore it.

Existing clients using encrypted or wrapped typing schemes are not automatically compatible with this plain NIP-29 event. They may choose to additionally publish or consume this event for public, interoperable group typing.

Example Filter

{
  "kinds": [23311],
  "#h": ["<group-id>"]
}