couponsby La Crypta

Voucher

Kind 20402 — CMS-signed proof, never published.

Signed by the Coupon Manager Service (CMS) with COUPON_MANAGER_NSEC. Travels in mint/claim JSON. Never published to relays.

{
  "kind": 20402,
  "pubkey": "<CMS hex>",
  "tags": [
    ["nonce", "hcLPDzERvvHzS4Vn0OLbAQ"],
    ["p", "<owner hex>"],
    ["coupon", "<uuid>"],
    ["phase", "minted"],           // or "claimed"
    ["expiration", "1764633600"]   // NIP-40, only if it expires
  ],
  "content": "{…}"
}

Payload (v: 1)

{
  "v": 1,
  "nonce": "hcLPDzERvvHzS4Vn0OLbAQ",
  "owner": "<merchant hex>",
  "name": "20% de verano",
  "description": "No acumulable.",
  "coupon": { "type": "percent", "percent": 20 },
  "phase": "minted"   // | "claimed"
  // claimedAt, expiresAt, image — omitted when absent, never null
}

Why 20402: ephemeral range. If it leaks to a relay that follows the spec, it is relayed but not stored — right for a bearer nonce.

Verify without calling us

import { verifyEvent } from "nostr-tools/pure"

const cmsPubkey = announcement.tags.find((t) => t[0] === "p")?.[1]
if (voucher.pubkey !== cmsPubkey) throw new Error("different issuer")
if (!verifyEvent(voucher)) throw new Error("invalid signature")

const payload = JSON.parse(voucher.content)
if (payload.nonce !== response.nonce) throw new Error("different nonce")
if (payload.owner !== expectedOwnerHex) throw new Error("different merchant")

TLS proves the response came from the host. The CMS signature proves it came from the service the merchant named.

On this page