Open Markets Protocol
Orders
Private checkout messages, order identity, address handling, status transitions, shipping updates, and their NIP-17 transport envelope.
Scope
An order is a private proposal from a buyer to a merchant to purchase one or more offers. Order data can contain names, addresses, contact details, and commercial terms; it MUST NOT be published as a public Nostr event.
Open Markets preserves Gamma's kind 16 business messages while correcting one important description: kinds 16 and 17 are not defined by NIP-17. They are Open Markets rumor kinds transported by NIP-17, NIP-44, and NIP-59.
Message matrix
| Rumor kind | Type | Direction | Purpose |
|---|---|---|---|
14 | none | either | Human-readable order conversation |
16 | 1 | buyer → merchant | Order creation |
16 | 2 | normally merchant → buyer | Payment request |
16 | 3 | either, role constrained | Order status or cancellation |
16 | 4 | merchant → buyer | Shipping update |
17 | none | buyer → merchant | Payment receipt |
This page defines order creation, status, shipping, and communication. Payment request and receipt schemas are defined in Payments.
Private transport
The examples below show decrypted unsigned rumors. These objects MUST NOT be published directly.
For every recipient, a sending client MUST:
- Construct an unsigned rumor with
id, real authorpubkey, currentcreated_at,kind,tags, andcontent. - Compute the rumor ID but omit
sig. - Serialize and NIP-44-encrypt the rumor into a kind
13seal. - Sign the seal with the same key identified by the rumor's
pubkey. - Require the seal to have an empty tag array.
- NIP-44-encrypt the seal into a kind
1059gift wrap. - Sign the wrap with a new random one-time key.
- Put only the recipient routing
ptag on the outer wrap. - Publish the wrap only to relays in the recipient's latest valid kind
10050inbox event. - Publish a separate wrap to the sender when recoverable history is desired.
The seal and wrap timestamps SHOULD be randomized up to two days into the past as required by NIP-17/NIP-59. The rumor timestamp is the authoritative business-message time.
{
"kind": 1059,
"pubkey": "<fresh-one-time-pubkey>",
"created_at": 1786800000,
"tags": [["p", "<recipient-pubkey>"]],
"content": "<nip44-encrypted-kind-13-seal>",
"id": "<gift-wrap-id>",
"sig": "<one-time-key-signature>"
}
On receipt, a client MUST validate the outer signature before decrypting, validate the kind 13 signature, require seal.pubkey == rumor.pubkey, recompute the rumor ID, and validate the inner recipient and schema before changing local order state.
Kind 21059 ephemeral gift wraps SHOULD NOT carry orders because an offline recipient cannot retrieve them later.
Order identity
The buyer creates the order value. It MUST be non-empty, stable throughout the order, and unique among orders authored by that buyer. Implementations SHOULD generate at least 128 bits of cryptographic randomness.
For lookup and deduplication, clients MUST scope an order by all three values:
<buyer-pubkey>:<merchant-pubkey>:<order-id>
A bare order string is not globally unique. Every later message MUST carry the original order value.
Order creation
Order creation is a kind 16, type 1 rumor authored by the buyer and addressed to one merchant.
Required tags
| Tag | Cardinality | Meaning |
|---|---|---|
p | exactly 1 | Merchant public key |
subject | exactly 1 | Human-readable subject |
type | exactly 1 | 1 |
order | exactly 1 | Buyer-generated identifier |
amount | exactly 1 | Proposed total in satoshis |
item | 1 or more | Offer address and quantity |
Each item has this shape:
["item", "30402:<merchant-pubkey>:<offer-d>", "<positive-integer-quantity>"]
Optional tags
| Tag | Meaning |
|---|---|
shipping | Selected kind 30406 shipping address |
address | Free-form human-readable delivery address |
address_* | Structured address components |
email | Buyer-provided contact email |
phone | Buyer-provided contact telephone |
client | Optional NIP-89 software attribution |
The rumor content MAY contain private order notes or special requests.
Structured addresses
Gamma PR #5 correctly identifies that an unstructured address is difficult to implement, but its proposed pseudo-JSON is neither valid JSON nor globally applicable. Open Markets preserves the existing address tag and adds optional components:
["address", "Ada Lovelace, 12 Market St, Miami, FL 33101, US"]
["address_name", "Ada Lovelace"]
["address_line1", "12 Market St"]
["address_line2", "Suite 4"]
["address_city", "Miami"]
["address_region", "FL"]
["address_postal", "33101"]
["address_country", "US"]
["address_note", "Leave with reception"]
address_country SHOULD use ISO 3166-1 alpha-2. address_region represents a state, province, territory, or similar administrative area. Clients MUST ignore unknown address_* tags and MAY add jurisdiction-specific fields.
All address and contact tags MUST remain inside the encrypted rumor. Clients MUST NOT copy them to the seal or gift wrap.
Example
{
"id": "<rumor-id>",
"pubkey": "<buyer-pubkey>",
"created_at": 1786924900,
"kind": 16,
"tags": [
["p", "<merchant-pubkey>"],
["subject", "Order coffee-2026-08-17-01"],
["type", "1"],
["order", "coffee-2026-08-17-01"],
["amount", "185000"],
["item", "30402:<merchant-pubkey>:coffee-ethiopia-250g", "1"],
["shipping", "30406:<merchant-pubkey>:us-standard"],
["address_name", "Ada Lovelace"],
["address_line1", "12 Market St"],
["address_city", "Miami"],
["address_region", "FL"],
["address_postal", "33101"],
["address_country", "US"]
],
"content": "Please leave the package at reception."
}
Order validation
The merchant MUST treat a received order as a proposal. Before requesting payment, the merchant MUST:
- Verify the buyer's transport, rumor ID, author, and recipient.
- Require exactly one type
1, order, amount, subject, and merchantptag. - Require at least one valid item.
- Require every quantity to be a positive base-10 integer.
- Resolve every item to a valid kind
30402offer. - Require each item's offer author to match the recipient merchant unless multi-merchant checkout is explicitly supported.
- Recalculate stock, price, recurrence, shipping eligibility, shipping cost, and the total.
- Treat the buyer's
amountas a proposed total, not as an authoritative price. - Reject an invalid or unavailable shipping option.
- Handle personal data according to applicable privacy and retention requirements.
Gamma order amounts are integer satoshis even when offers are priced in fiat. When conversion is necessary, the client MUST show the buyer the rate, source, resulting satoshi amount, and time of calculation. Open Markets does not standardize exchange-rate policy.
Order status
A status update is kind 16, type 3.
Recognized states are:
pending → confirmed → processing → completed
└──────────────→ cancelled
| Status | Meaning | Authorized author |
|---|---|---|
pending | Order received, payment not verified | Merchant |
confirmed | Payment independently verified | Merchant |
processing | Fulfillment is underway | Merchant |
completed | Merchant considers fulfillment complete | Merchant |
cancelled | Order will not continue | Merchant; buyer as constrained below |
pending is optional. A merchant MUST NOT publish confirmed merely because a syntactically valid receipt arrived; the payment proof must be verified.
{
"id": "<rumor-id>",
"pubkey": "<merchant-pubkey>",
"created_at": 1786925700,
"kind": 16,
"tags": [
["p", "<buyer-pubkey>"],
["subject", "order-info"],
["type", "3"],
["order", "coffee-2026-08-17-01"],
["status", "confirmed"]
],
"content": "Payment received."
}
The p tag always identifies the recipient. This retains merged Gamma PR #3: merchant updates point to the buyer, while a buyer cancellation points to the merchant.
Clients MUST deduplicate updates by rumor ID. To display current state, they MUST consider only valid, role-authorized transitions and order them by rumor created_at, using rumor ID as a deterministic tie-breaker. Relay arrival order is not authoritative.
Unknown status values MUST be displayed as unsupported and MUST NOT be mapped silently to a known state.
Cancellation
A buyer MAY send type 3 with status=cancelled, preferably before confirmation. The merchant MAY cancel by addressing the buyer.
{
"id": "<rumor-id>",
"pubkey": "<buyer-pubkey>",
"created_at": 1786925200,
"kind": 16,
"tags": [
["p", "<merchant-pubkey>"],
["subject", "order-info"],
["type", "3"],
["order", "coffee-2026-08-17-01"],
["status", "cancelled"]
],
"content": "I do not accept the revised total."
}
For an unpaid order, cancellation is terminal. After payment or confirmation, a cancellation records the sender's position but does not reverse a payment or compel the other party. Open Markets currently defines no refund, chargeback, escrow, arbitration, or forced rollback event.
Shipping updates
Shipping updates use kind 16, type 4, authored by the merchant.
Recognized shipping states are processing, shipped, delivered, and exception. The required tags are recipient p, subject, type 4, order, and status. Optional tags are tracking, carrier, and Unix eta.
{
"id": "<rumor-id>",
"pubkey": "<merchant-pubkey>",
"created_at": 1787011200,
"kind": 16,
"tags": [
["p", "<buyer-pubkey>"],
["subject", "shipping-info"],
["type", "4"],
["order", "coffee-2026-08-17-01"],
["status", "shipped"],
["tracking", "TRACK123456"],
["carrier", "Example Post"],
["eta", "1787356800"]
],
"content": "The package has left the warehouse."
}
Shipping state is independent of order state. delivered does not automatically create completed; the merchant publishes the corresponding order update explicitly.
General communication
Parties SHOULD use NIP-17 kind 14 rumors for questions, clarifications, delivery problems, and other human conversation. The subject SHOULD contain the order ID. An e tag MAY identify the direct parent message for reply UX.
{
"id": "<rumor-id>",
"pubkey": "<buyer-pubkey>",
"created_at": 1786924950,
"kind": 14,
"tags": [
["p", "<merchant-pubkey>"],
["subject", "coffee-2026-08-17-01"]
],
"content": "Can I change the delivery day?"
}
The order tag remains the required machine-readable correlation key on structured kind 16 and 17 messages. Reply tags do not replace it.
NIP-69 boundary
NIP-69 kind 38383 is a public P2P Bitcoin/fiat liquidity advertisement with premiums, payment methods, optional bonds, and its own statuses. It MUST NOT be treated as an Open Markets purchase order.
An application supporting both protocols MUST keep their state machines, identities, ratings, and expiration behavior separate. Open Markets does not import NIP-69 bonds or imply escrow.
Security requirements
- Clients MUST use NIP-44 v2 inside NIP-59 wrapping and MUST NOT fall back to NIP-04 for new orders.
- Clients MUST enforce encrypted-payload size limits before expensive decoding.
- Inbox relays SHOULD require NIP-42 authorization for gift-wrap retrieval.
- Clients MUST deduplicate rumors and reject role-invalid transitions.
- Contact information, addresses, invoices, payment proofs, and eCash tokens MUST never appear in public events.
- A
clienttag is optional attribution and MUST NOT grant authority. - Implementations SHOULD minimize retention of decrypted personal data and provide deletion controls.