Skip to content

Conversation

@alejandro-runner
Copy link

This NIP defines a protocol to manage reservations via Nostr while maintaining the privacy of the customer.

The term "reservations" is used as a broad term and could be applied to restaurants, hotels, or any other business offering multiple appointments. This NIP also defines a business transaction attestation event associated with a successfully completed reservation so that customers can issue a verified business review.

The reservation process uses 4 different messages, each with its own kind, that are sent unsigned, sealed, and gift wrapped between the parties to maintain the privacy of the customer. Only the customer and the business are aware of the reservation.

The protocol defines a fifth new kind that businesses use to send a transaction attestation token to customers. Customers add this token to their reviews so that readers can identify this review as coming from a verified customer. The reservation and associated business transaction remain private until and unless the customer publishes a review.

This NIP builds on the kind:31555 review event used by the Gamma Markets specification addendum to NIP-99.

@alejandro-runner
Copy link
Author

alejandro-runner commented Nov 12, 2025

Hi everyone, I hope you find this NIP proposal useful. I'm excited to contribute to Nostr in more ways than just a user.

I have implemented this NIP draft with the Synvya for Restaurants client (https://github.com/Synvya/client) and an AI Concierge to find and book restaurants (https://github.com/Synvya/ai-concierge).

I'm happy to answer any questions and fix any issues with the proposal, it's my first serious contribution to an open source project so I'm sure there is stuff to be fixed :)

Copy link
Member

@staab staab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, it's better to flatten data fields and put them in tags rather than json-encode them and put them in the content. For example,

Rumor Event Structure:

{
  "id": "<32-byte hex of unsigned event hash>",
  "pubkey": "<senderPublicKey>",
  "created_at": <unix timestamp in seconds>,
  "kind": 9901,
  "tags": [
    ["p", "<businessPublicKey>", "<relayUrl>"]
    // Additional tags MAY be included
  ],
  "content": "<content-in-plain-text>"
  // Note: No signature field - this is an unsigned rumor
}

Content Structure:

{
  "party_size": <integer between 1 and 20>,
  "iso_time": "<ISO8601 datetime with timezone>",
  "notes": "<optional string, max 2000 chars>",
  "contact": {
    "name": "<optional string, max 200 chars>",
    "phone": "<optional string, max 64 chars>",
    "email": "<optional email>"
  },
  "constraints": {
    "earliest_iso_time": "<optional ISO8601 datetime>",
    "latest_iso_time": "<optional ISO8601 datetime>",
  }
}

Should be:

{
  "id": "<32-byte hex of unsigned event hash>",
  "pubkey": "<senderPublicKey>",
  "created_at": <unix timestamp in seconds>,
  "kind": 9901,
  "tags": [
    ["p", "<businessPublicKey>", "<relayUrl>"]
    ["party_size", "<integer between 1 and 20>"],
    ["iso_time", "<ISO8601 datetime with timezone>"],
    ["notes", "<optional string, max 2000 chars>"],
    ["name", "<optional string, max 200 chars>"],
    ["phone", "<optional string, max 64 chars>"],
    ["email", "<optional email>"],
    ["earliest_iso_time", "<optional ISO8601 datetime>"],
    ["latest_iso_time", "<optional ISO8601 datetime>"]
  ],
  "content": "<content-in-plain-text>"
  // Note: No signature field - this is an unsigned rumor
}

This trades parsing json for parsing integers sometimes, but it's much more idiomatic and easier to work with (since you can't trust that events follow a given format anyway).

Another thing that's missing is recommendations for where to send events. Should it use the outbox model? DM relays? Should stuff be sent to the business' relay only? Stuff like that.

For time fields, iso_time is fine, but you might compare how NIP 52 calendar events are done for consistency, and because unix timestamps are more idomatic on nostr.

In general, try to remove everything you can from the NIP. Long nips are less likely to be read. Brevity is the soul of wit.

rp.md Outdated

### Handler Information Event (kind:31990)

Businesses MUST publish a `kind:31990` handler information event that declares support for all reservation message kinds:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't overload 31990, they're already overloaded but this just makes things worse. Handlers are for apps that can handle an event kind, not pubkeys. Choose a new kind here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the kind:31989 and kind:31990 events. I don't fully understand application handlers anyway.

Synvya's primary use case is AI assistants and I'm using NIP-32 labels on the kind:0 event of the business to allow AI assistants to quickly find certain types of business (e.g. label restaurant in namespace com.synvya.merchant). I can add another label reservations under the same namespace to signal support for this nip.

At some point, a more generic namespace might be appropriate but for the time being I don't want to use a too generic namespace since it's shared globally.


Once a reservation is fulfilled, for example when the restaurant check is closed, the business privately issues a token to the customer to attest the business transaction. This token is independent of the review itself and only attests for the fact that the customer and the business executed a transaction:
- It's created before the review is written
- Does not endorse the content of the review
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this

…y the format as `tel:` URI. Specify format of `email` as `mailto:` URI. Both `telephone` and `email` remain optional but at least one of them must be present.

Use NIP-73 with `nip` external content ID to signal support for this NIP.
…anizing the reservation differs from the reservation holder.

Made duration mandatory for `kind:9902` and `kind:9904`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants