Chapter 40 - Relay Ops
40
Nostr Implementation Possibility - Hex № 40

Expiration Timestamp

One tag gives an event a best-before date. After that moment, relays hide it and clients ignore it.

1 Overview

Events on Nostr live forever by default. A relay stores a regular event and serves it to anyone who asks. Some content should not live forever. A flash sale ends. A meetup announcement goes stale. This NIP adds a self-destruct timer for such events.

The tool is one tag: expiration. The tag holds a unix timestamp in seconds. The timestamp marks the moment when the event SHOULD be considered expired. Relays and clients both apply this rule. Relays SHOULD delete the event at or after that moment.

Support is optional. A relay advertises support in its NIP-11 information document. A client checks that document before it publishes an expiring event.

Warning!

Expiration is a housekeeping hint, not a security feature. The event is public on every relay until deletion. Any third party can download and keep a copy before it expires. Do not use this tag to protect conversations or secrets.

created_at client publishes fresh — relay stores and serves the event expiration SHOULD be deleted expired — hidden from clients, deletion may lag
Fig. 40-1 — Event lifetime. The event is normal until the expiration moment. After it, relays stop serving the event and delete it when they can.

2 The expiration Tag

Add one expiration tag to the tags array of any event. The tag has one value. The value is a unix timestamp in seconds, written as a string. The value is required.

Tag format - expiration
tag: expiration
values:
 - [UNIX timestamp in seconds]: required
Tag reference
TagValueRule
expirationunix timestamp, seconds, as a stringRequired value. Marks the moment when the event SHOULD be considered expired and SHOULD be deleted by relays.

The timestamp uses the same format as the created_at field from NIP-01: seconds since the unix epoch. Interpret it as the time at which relays should delete the event.

Example - expiring event
{
  "pubkey": <pub-key>,
  "created_at": 1000000000,
  "kind": 1,
  "tags": [
    ["expiration", "1600000000"]
  ],
  "content": "This message will expire at the specified timestamp and be deleted by relays.\n",
  "id": <event-id>
}
Hint!

The tag value is a string, like every tag value in Nostr. Write "1600000000", with quotes. The created_at field next to it is a bare integer. Do not mix the two forms up.

3 Client Behavior

Not every relay implements this NIP. A relay that does lists the number 40 in the supported_nips field of its NIP-11 information document. Clients SHOULD use that field to learn if a relay supports this NIP.

  • Clients SHOULD NOT send expiration events to relays that do not support this NIP.
  • Clients SHOULD ignore events that have expired. Apply this rule even when a relay still delivers an expired event.

4 Relay Behavior

A relay that supports this NIP applies four rules:

  • The relay is not required to delete an expired event at the exact expiration moment. It may delete late, and it may persist the event indefinitely.
  • The relay SHOULD NOT send expired events to clients, even if it still stores them.
  • The relay SHOULD drop any event that is already expired when a client publishes it.
  • An expiration timestamp does not affect the storage of ephemeral events. Ephemeral kinds (see NIP-01) are not stored anyway.
Tech Note

The source spec writes "Relays MAY NOT delete expired messages immediately". "MAY NOT" is not an RFC 2119 keyword, and it can read as a prohibition. The next clause ("and MAY persist them indefinitely") shows the intent: deletion is permitted but not required to be immediate. This page restates that intent.

Client Relay NIP-11 request: is 40 in supported_nips? {"supported_nips": [1, 11, 40, …]} ["EVENT", {…, "tags":[["expiration","1600000000"]]}] ["OK", <id>, true, ""] later, after expiry: ["REQ", "sub1", {…}] ["EOSE", "sub1"] — expired event not sent The relay hides the expired event and deletes it on its own schedule.
Fig. 40-2 — Expiration flow. The client checks support first, then publishes. After the timestamp, the relay stops serving the event.

5 Suggested Use Cases

  • Temporary announcements. An event organizer posts announcements about an upcoming event. The posts expire when the announcement is no longer useful.
  • Limited-time offers. A business posts a special offer that is only available for a limited time. The offer expires after that time.

6 Worked Example

A cafe posts a lunch offer at 12:00 that expires at 15:00 the same day. The session below shows the publish, a fresh read, and a read after expiry. Orange lines go to the relay. Blue lines come from the relay.

Session transcript - wss://relay.example.com
-- 12:00 - the cafe publishes an offer that expires at 15:00 --
→ ["EVENT", {"id":"7d1e02fa…","pubkey":"ac93be21…","created_at":1749812400,
     "kind":1,"tags":[["expiration","1749823200"]],
     "content":"Lunch special: soup + coffee, until 15:00 today!","sig":"91cc04d7…"}]
← ["OK", "7d1e02fa…", true, ""]

-- 13:30 - a customer reads the feed; the offer is still fresh --
→ ["REQ", "menu", {"kinds":[1], "authors":["ac93be21…"]}]
← ["EVENT", "menu", {"id":"7d1e02fa…", "content":"Lunch special: …", …}]
← ["EOSE", "menu"]
→ ["CLOSE", "menu"]

-- 16:00 - after expiry, the relay no longer serves the event --
→ ["REQ", "menu2", {"kinds":[1], "authors":["ac93be21…"]}]
← ["EOSE", "menu2"]

-- publishing an already-expired event gets dropped --
→ ["EVENT", {"id":"e4b09cd2…", "tags":[["expiration","1600000000"]], …}]
← ["OK", "e4b09cd2…", false, "invalid: event is expired"]

A careful client also ignores expired events on its own. If a lagging relay still delivers the offer at 16:00, the client compares the tag value with the current time and hides the event.

7 Version History

NIP-40 arrived in December 2022 and has changed little since. Notable changes, newest first:

  • 20252025-12Added the relay tag to the preamble, marking the NIP as one that relays implement.
  • 20232023-11fiatjaf reformatted the JSON example and removed the listed NIP authors, as part of a repo-wide cleanup.
  • 20232023-04Grammar and typo fixes.
  • 20222022-12A rapid series of edits filled in the spec: client behavior via supported_nips, relay deletion rules, the ephemeral-event carve-out, use cases, and the privacy warning.
  • 20222022-12The tag value was fixed as a UNIX timestamp in seconds.
  • Origin2022-12-04Thomas created 40.md with the expiration tag proposal.

Original Proposal - Credits

AuthorThomas
First commit2022-12-04 — “Create 40.md”
Statusdraft - optional - relay — relays advertise support via NIP-11
Source40.md on GitHub - full change history

8 See Also

  • NIP-01 — defines the event object, the created_at timestamp format, and ephemeral kinds.
  • NIP-11 — the relay information document with the supported_nips field that clients check.
  • NIP-09 — deletion requests; the author asks for removal instead of scheduling it in advance.