Chapter 52 - Scheduling
52
Nostr Implementation Possibility - Hex № 52

Calendar Events

Date-based and time-based calendar events, the calendars that collect them, and the RSVPs that answer them — all as addressable Nostr events.

1 Overview

This NIP puts calendars on Nostr. A calendar event is an occurrence at a specific moment, or between two moments. Think of a meetup, a conference talk, a vacation, or a public holiday.

One word of caution on terms. Everywhere else in this manual, "event" means any signed Nostr event from NIP-01. This chapter always says calendar event for the scheduling object. It says event only for the underlying Nostr object.

The NIP defines four kinds. Kind 31922 is a date-based calendar event. Kind 31923 is a time-based calendar event. Kind 31924 is a calendar: a list that collects calendar events. Kind 31925 is an RSVP: a user's answer to a calendar event. All four are addressable events per NIP-01, and users can delete them per NIP-09.

A calendar event does not have to belong to a calendar. It stands on its own. Calendars only group and curate.

Calendar event kind 31922 / 31923 Calendar kind 31924 RSVP kind 31925 "a" — request inclusion "a" — includes "a" (+ optional "e", "p")
Fig. 52-1 — The four kinds. Calendars point at calendar events. RSVPs point at calendar events. Calendar events may ask a calendar to include them.
Hint!

Addressable means: the relay keeps only the latest event per pubkey + kind + d value. Edit a calendar event by publishing a new event with the same d tag. Other events point at it with an a tag: <kind>:<pubkey>:<d value>.

2 Common Tags

Both calendar event kinds (31922 and 31923) share one set of tags. The client that creates the calendar event generates the d identifier.

Tags common to kind 31922 and kind 31923
TagUseMeaning
drequiredA short unique string identifier. Generated by the client that creates the calendar event.
titlerequiredThe title of the calendar event.
summaryoptionalA brief description of the calendar event.
imageoptionalThe URL of an image for the calendar event.
locationoptional, repeatedA location: an address, GPS coordinates, a meeting room name, or a video-call link.
goptionalA geohash. It ties the calendar event to a searchable physical location.
poptional, repeatedA participant: 32-byte hex pubkey, optional recommended relay URL, and the participant's role in the meeting.
toptional, repeatedA hashtag that categorizes the calendar event.
roptional, repeatedA reference link: web page, document, video call, recorded video, and so on.
arepeatedA reference to a kind 31924 calendar. It requests inclusion in that calendar. See below.
Warning!

The name tag is deprecated. It held the name of the calendar event. Read it only when title is absent. Never write it in new calendar events.

Collaborative calendar event requests

A calendar event can carry an a tag that points at a kind 31924 calendar. Clients should read this as a request: "add me to that calendar". The calendar owner approves by adding a matching a tag to the calendar. This lets many users contribute calendar events to calendars they do not own, subject to the owner's approval.

3 Date-Based Calendar Event — Kind 31922

A date-based calendar event starts on a date and ends before a later date. Use it when the time of day and the time zone do not matter: an anniversary, a public holiday, vacation days. It is an addressable event of kind 31922. The .content SHOULD be a description of the calendar event.

Besides the common tags, it takes two date tags:

Kind 31922 date tags
TagUseMeaning
startrequiredInclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than end, if end exists.
endoptionalExclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as start.
Wire format - kind 31922 date-based calendar event
{
  "id":         <32-byte lowercase hex sha256 of the serialized event>,
  "pubkey":     <32-byte lowercase hex public key of the creator>,
  "created_at": <unix timestamp, seconds>,
  "kind": 31922,
  "content":    "<description of calendar event>",
  "tags": [
    ["d", "<random-identifier>"],
    ["title", "<title of calendar event>"],
    // dates
    ["start", "<YYYY-MM-DD>"],
    ["end", "<YYYY-MM-DD>"],
    // location
    ["location", "<location>"],
    ["g", "<geohash>"],
    // participants
    ["p", "<hex pubkey>", "<relay URL>", "<role>"],
    ["p", "<hex pubkey>", "<relay URL>", "<role>"]
  ]
}

4 Time-Based Calendar Event — Kind 31923

A time-based calendar event spans a start time and an end time. It is an addressable event of kind 31923. The .content should be a description of the calendar event. It is required, but it can be an empty string.

Besides the common tags, it takes these time tags:

Kind 31923 time tags
TagUseMeaning
startrequiredInclusive start as a Unix timestamp in seconds. Must be less than end, if end exists.
endoptionalExclusive end as a Unix timestamp in seconds. If omitted, the calendar event ends instantaneously.
start_tzidoptionalTime zone of the start timestamp, from the IANA Time Zone Database. Example: America/Costa_Rica.
end_tzidoptionalTime zone of the end timestamp, from the IANA Time Zone Database. If omitted and start_tzid is present, the end uses the start's time zone.
DrequiredThe day-granularity Unix timestamp of a day the calendar event takes place on: floor(unix_seconds() / seconds_in_one_day). Include multiple D tags — one SHOULD exist for every day the calendar event covers.
Day buckets: floor(unix seconds / 86400) day 20619 day 20620 day 20621 calendar event: start → end tags: ["D","20619"] and ["D","20620"] — one per covered day
Fig. 52-2 — The D tag. A calendar event that crosses midnight covers two day buckets, so it carries two D tags. Relays index D, so clients can query one day cheaply.
Tech Note

Two source quirks. First: the source's example tag ["D", "82549"] maps to a day in the year 2196; a present-day value is near 20600. Second: D is marked required, but it was added in 2026 — kind 31923 events published before then do not carry it. Query by #D with a fallback.

Wire format - kind 31923 time-based calendar event
{
  "id":         <32-byte lowercase hex sha256 of the serialized event>,
  "pubkey":     <32-byte lowercase hex public key of the creator>,
  "created_at": <unix timestamp, seconds>,
  "kind": 31923,
  "content":    "<description of calendar event>",
  "tags": [
    ["d", "<random-identifier>"],
    ["title", "<title of calendar event>"],
    ["summary", "<brief description>"],
    ["image", "<image URL>"],
    // timestamps
    ["start", "<unix timestamp in seconds>"],
    ["end", "<unix timestamp in seconds>"],
    ["D", "<floor(unix seconds / 86400)>"],
    ["start_tzid", "<IANA time zone id>"],
    ["end_tzid", "<IANA time zone id>"],
    // location
    ["location", "<location>"],
    ["g", "<geohash>"],
    // participants
    ["p", "<hex pubkey>", "<relay URL>", "<role>"],
    ["p", "<hex pubkey>", "<relay URL>", "<role>"]
  ]
}

5 Calendar — Kind 31924

A calendar is a collection of calendar events. It is an addressable list event of kind 31924. One user can keep many calendars: personal, work, travel, meetups, conferences. The .content should be a detailed description of the calendar. It is required, but it can be an empty string.

Kind 31924 tags
TagUseMeaning
drequiredA universally unique identifier. Generated by the client that creates the calendar.
titlerequiredThe calendar title.
arepeatedA reference to a kind 31922 or 31923 calendar event that this calendar includes or responds to.

A calendar can accept requests from other users. When a calendar event points at the calendar with an a tag, that is a request for inclusion. The owner answers by adding the calendar event's coordinates to the calendar.

Wire format - kind 31924 calendar
{
  "id":         <32-byte lowercase hex sha256 of the serialized event>,
  "pubkey":     <32-byte lowercase hex public key of the creator>,
  "created_at": <unix timestamp, seconds>,
  "kind": 31924,
  "content":    "<description of calendar>",
  "tags": [
    ["d", "<random-identifier>"],
    ["title", "<calendar title>"],
    ["a", "<31922 or 31923>:<author pubkey>:<d value>", "<relay URL>"],
    ["a", "<31922 or 31923>:<author pubkey>:<d value>", "<relay URL>"]
  ]
}

6 Calendar Event RSVP — Kind 31925

An RSVP answers a calendar event. It states the user's attendance intention. It is an addressable event of kind 31925. The .content is optional: a free-form note that adds context to the response.

Invitations work through p tags. If a calendar event tags a pubkey, read that as an invitation from the creator to that user. Clients MAY prompt the tagged user to RSVP. But any user may RSVP, tagged or not. Clients MAY prompt the calendar event creator to invite an untagged user who RSVP'd. Clients also MAY ignore such RSVPs.

Two things stay intentionally undefined. The NIP does not define who is authorized to attend when an untagged user RSVPs; the calendar event creator decides the semantics. The NIP also does not define what happens when a calendar event changes after an RSVP is submitted.

Kind 31925 tags
TagUseMeaning
arequiredCoordinates of the kind 31922 or 31923 calendar event being answered.
eoptionalThe event id of the specific calendar event revision being answered. If present, clients SHOULD read the RSVP as an answer to that revision. Clients MAY treat it as not applying to other revisions.
drequiredA universally unique identifier. Generated by the client that creates the RSVP.
statusrequiredaccepted, declined, or tentative. The attendance status for the referenced calendar event.
fboptionalfree or busy. States whether the user is free or busy for the duration of the calendar event. Omit or ignore this tag when status is declined.
poptionalThe pubkey of the calendar event's author. It lets clients query all RSVPs that pertain to that author.
Wire format - kind 31925 RSVP
{
  "id":         <32-byte lowercase hex sha256 of the serialized event>,
  "pubkey":     <32-byte lowercase hex public key of the creator>,
  "created_at": <unix timestamp, seconds>,
  "kind": 31925,
  "content":    "<note>",
  "tags": [
    ["e", "<kind 31922 or 31923 event id>", "<relay URL>"],
    ["a", "<31922 or 31923>:<author pubkey>:<d value>", "<relay URL>"],
    ["d", "<random-identifier>"],
    ["status", "<accepted / declined / tentative>"],
    ["fb", "<free / busy>"],
    ["p", "<hex pubkey of the calendar event author>", "<relay URL>"]
  ]
}
Tech Note

Source quirks in this section. The RSVP example JSON in the source is malformed: the e tag line lacks a closing quote and a trailing comma. Also, the d tag is a "short unique string" in the common-tags list, but a "universally unique identifier" here and for calendars. Treat both as: any client-generated unique string.

Organizer Guest kind 31923 with ["p", guest_pubkey] — invitation Guest's client MAY prompt: attend? kind 31925 with ["a", coords], ["status","accepted"] Untagged users may RSVP too. The organizer's client MAY prompt to invite them — or ignore the RSVP.
Fig. 52-3 — Invitation and RSVP. A p tag invites. A kind 31925 event answers with a status.

7 Intentionally Unsupported: Recurrence

This NIP does not support recurring calendar events. Recurrence brings heavy complexity: time zone differences between invitees, daylight saving time, leap years, multiple calendar systems, one-off changes in schedule or metadata.

The NIP pushes that complexity up to clients. A client that wants recurrence publishes individual calendar events with duplicated metadata, one per occurrence.

8 Worked Example

Alice organizes a meetup that runs from 17:00 UTC on 2026-06-15 to 01:00 UTC on 2026-06-16. It crosses midnight, so it carries two D tags. She invites Bob with a p tag and asks a community calendar to include the event.

Alice's calendar event - kind 31923
{
  "kind": 31923,
  "pubkey": "a1ce4f2b…",
  "created_at": 1780000000,
  "content": "Monthly Nostr meetup. Talks, pizza, key signing.",
  "tags": [
    ["d", "meetup-2026-06"],
    ["title", "Nostr Meetup #14"],
    ["summary", "Talks and pizza"],
    ["start", "1781542800"],
    ["end", "1781571600"],
    ["D", "20619"], ["D", "20620"],
    ["start_tzid", "Europe/Berlin"],
    ["location", "c-base, Rungestr. 20, Berlin"],
    ["g", "u33db2m"],
    ["p", "b0b1d2e3…", "wss://relay.example.com", "speaker"],
    ["t", "meetup"],
    ["r", "https://meetup.example.com/14"],
    ["a", "31924:ca1e4da5…:berlin-community"]
  ],
  "id": …, "sig": …
}

Bob's client sees the p tag and prompts him. He accepts:

Bob's RSVP - kind 31925
{
  "kind": 31925,
  "pubkey": "b0b1d2e3…",
  "content": "See you there!",
  "tags": [
    ["a", "31923:a1ce4f2b…:meetup-2026-06", "wss://relay.example.com"],
    ["e", "9e0c55aa…", "wss://relay.example.com"],
    ["d", "rsvp-77f3"],
    ["status", "accepted"],
    ["fb", "busy"],
    ["p", "a1ce4f2b…"]
  ],
  "created_at": …, "id": …, "sig": …
}

Carol owns the community calendar. She approves Alice's request by adding the coordinates. A client then queries the day and the RSVPs:

Session transcript - wss://relay.example.com
-- Carol approves: her calendar now lists Alice's event --
→ ["EVENT", {"kind":31924, "tags":[["d","berlin-community"],
     ["title","Berlin Community Calendar"],
     ["a","31923:a1ce4f2b…:meetup-2026-06","wss://relay.example.com"]], …}]
← ["OK", "c4a7e1…", true, ""]

-- what happens on June 15? query the day bucket --
→ ["REQ", "day", {"kinds":[31923], "#D":["20619"]}]
← ["EVENT", "day", {"kind":31923, "tags":[["title","Nostr Meetup #14"], …]}]
← ["EOSE", "day"]

-- who is coming? query RSVPs by coordinates --
→ ["REQ", "rsvps", {"kinds":[31925], "#a":["31923:a1ce4f2b…:meetup-2026-06"]}]
← ["EVENT", "rsvps", {"kind":31925, "tags":[["status","accepted"],["fb","busy"], …]}]
← ["EOSE", "rsvps"]

9 Version History

Terry Yiu proposed NIP-52 in mid-2023. The spec has since traded UUIDs for plain identifiers, gained RSVP pointers, and grown day-bucket indexing. Notable changes, newest first:

  • 20262026-02Added the D day-granularity timestamp tag ("timehashes") to kind 31923, so clients can query calendar events by day.
  • 20252025-08Added collaborative calendar event requests: an a tag on a calendar event now asks a kind 31924 calendar for inclusion.
  • 20252025-05Simplified the spec; dropped the UUID requirement — d is now any client-generated unique string.
  • 20242024-08RSVPs gained the optional e (revision) and p (author) tags, revised to stay backwards compatible with the required a tag. Renamed "parameterized replaceable" events to "addressable".
  • 20232023-12Deprecated name in favor of title; made .content required; decoupled the calendar identifier from the calendar name.
  • 2023-252023-2025Housekeeping: kinds written as integers, NIP-12 mention removed, label cruft and typos fixed.
  • Origin2023-06Terry Yiu published "NIP-52: Calendar Events" with the four kinds 31922-31925.

Original Proposal - Credits

AuthorTerry Yiu
First commit2023-06-10 — “NIP-52: Calendar Events”
Statusdraft - optional — clients implement it to offer scheduling
Source52.md on GitHub - full change history

10 See Also

  • NIP-01 — the base protocol: addressable events, a/e/p tags, and tag-filter queries used here.
  • NIP-09 — event deletion; calendar events are deletable through it.
  • NIP-51 — lists; a kind 31924 calendar is a purpose-built addressable list.
  • NIP-53 — live streaming and spaces; live activities that pair well with scheduled calendar events.
  • NIP-19 — naddr encoding for sharing the coordinates of a calendar event.