{"id":"112048906a9beeab9c815546d48187c33cab2cc9b17e11e046e6c766f941bcb9","pubkey":"d1678e7ef965374bbea308a1215609a78376dc158277a7d657680f9d5efd5c38","created_at":1784647992,"kind":30817,"tags":[["d","nut-26"],["title","NUT-26: Payment Request Bech32m Encoding"],["summary","A bech32m encoding of payment requests, smaller than the CBOR form and friendlier to QR codes."],["s","optional"],["t","cashu"],["t","ecash"],["t","nut"],["alt","A specification: NUT-26: Payment Request Bech32m Encoding"],["client","openspecs-import"],["published_at","1768122596"],["proxy","https://github.com/cashubtc/nuts/blob/a845dfc998abae501fc3419592d53dc995d34b12/26.md","web"],["x","d66e85a7404aa4e562784675d9520b64315824f6d12bae36ec94a41ab6d18c89"]],"content":"# NUT-26: Payment Request Bech32m Encoding\n\n`optional` `depends on: NUT-18`\n\n---\n\nThis specification defines an alternative encoding format for Payment Requests using Bech32m encoding with TLV (Tag-Length-Value) serialization. This format provides better QR code compatibility and typically 30-60% size reduction compared to the CBOR+base64 encoding defined in NUT-18.\n\n## Encoded Request Format\n\nPayment requests are serialized using TLV encoding, then encoded with Bech32m:\n\n`\"creqb\" + \"1\" + bech32m(TLV(PaymentRequest))`\n\nThe human-readable part (HRP) is `\"creqb\"` and the version separator is `\"1\"`. The data payload is TLV-encoded as described below, then encoded with Bech32m (not standard Bech32).\n\n> [!NOTE]\n> Implementations SHOULD output uppercase Bech32m strings for optimal QR code compatibility. Uppercase alphanumeric characters use QR \"alphanumeric mode\" which is more space-efficient than \"byte mode\" required for mixed-case. Decoders MUST accept both uppercase and lowercase input.\n\nWhen parsing a `creq` parameter, implementations SHOULD support both formats:\n\n1. If the parameter starts with `creqA` (case-insensitive), parse as NUT-18 CBOR+base64 format\n2. If the parameter is valid Bech32m with HRP `creqb`, parse as NUT-26 format\n3. Otherwise, return an error\n\n## TLV Structure\n\nThe payment request is encoded as a sequence of TLV fields. Each TLV entry consists of:\n\n- **Type** (1 byte): Field identifier\n- **Length** (2 bytes, big-endian): Length of value in bytes\n- **Value** (variable): Field data\n\nFields of type `u64` are encoded as fixed 8-byte big-endian values.\n\n### Top-Level TLV Tags\n\n| Tag  | Field            | Type      | Description                                                                                       |\n| ---- | ---------------- | --------- | ------------------------------------------------------------------------------------------------- |\n| 0x01 | id               | string    | Payment identifier (corresponds to `i` in JSON)                                                   |\n| 0x02 | amount           | u64       | Amount in base units (corresponds to `a` in JSON)                                                 |\n| 0x03 | unit             | u8/string | Currency unit (corresponds to `u` in JSON)                                                        |\n| 0x04 | single_use       | u8        | Single-use flag: 0=false, 1=true (corresponds to `s` in JSON)                                     |\n| 0x05 | mint             | string    | Mint URL (repeatable for multiple mints, corresponds to `m` in JSON)                              |\n| 0x06 | description      | string    | Human-readable description (corresponds to `d` in JSON)                                           |\n| 0x07 | transport        | sub-TLV   | Transport configuration (repeatable, corresponds to `t` in JSON)                                  |\n| 0x08 | nut10            | sub-TLV   | NUT-10 spending conditions (corresponds to `nut10` in JSON)                                       |\n| 0x09 | mint_preferred   | u8        | Mint list strictness flag: 0=false, 1=true; defaults to 0 if absent (corresponds to `mp` in JSON) |\n| 0x0a | supported_method | sub-TLV   | Supported payment method with optional fee (repeatable, corresponds to `sm` in JSON)              |\n\nAll fields are optional. Unknown tags MUST be ignored to maintain forward compatibility.\n\n### Unit Encoding (Tag 0x03)\n\nThe unit field uses a compact encoding:\n\n- **Value 0x00**: Represents `sat` (Bitcoin satoshis)\n- **String value**: Any other unit is encoded as a UTF-8 string (e.g., `\"msat\"`, `\"usd\"`, `\"eur\"`)\n\n### Transport Sub-TLV (Tag 0x07)\n\nTransport configurations are encoded as nested TLV structures. Each transport has the following sub-tags:\n\n| Sub-Tag | Field     | Type        | Description                                       |\n| ------- | --------- | ----------- | ------------------------------------------------- |\n| 0x01    | kind      | u8          | Transport type: 0=nostr, 1=http_post              |\n| 0x02    | target    | bytes       | Transport target (interpretation depends on kind) |\n| 0x03    | tag_tuple | sub-sub-TLV | Generic tag tuple (repeatable)                    |\n\n#### Transport Type Mapping\n\nThe kind field (sub-tag 0x01) identifies the transport method. The following transport types are defined:\n\n| Kind Value | Transport Type | Description                            | Target Format                         |\n| ---------- | -------------- | -------------------------------------- | ------------------------------------- |\n| 0x00       | nostr          | Nostr-based transport using NIP-04 DMs | 32-byte X-only public key (raw bytes) |\n| 0x01       | http_post      | HTTP POST to specified URL             | UTF-8 encoded URL string              |\n\n> [!NOTE]\n> If no transport is specified (tag 0x07 is absent), the payment is assumed to be in-band, consistent with NUT-18 semantics.\n\n**JSON Representation:**\n\nIn the NUT-18 JSON format, transports are represented with a `type` field:\n\n```json\n{\n  \"t\": [\n    { \"type\": \"nostr\", \"target\": \"npub1...\", \"tags\": [[\"n\", \"17\"]] },\n    { \"type\": \"post\", \"target\": \"https://callback.example.com/pay\" }\n  ]\n}\n```\n\nWhen encoding to TLV, the `type` string is converted to the corresponding numeric kind value.\n\n#### Transport Target Encoding (Sub-Tag 0x02)\n\nThe target field is interpreted based on the transport kind:\n\n- **kind=0 (nostr)**: 32-byte X-only public key (raw bytes, not bech32-encoded)\n- **kind=1 (http_post)**: UTF-8 encoded URL string\n\n#### Nostr Transport Details\n\nFor Nostr transports (`kind=0`), the target field contains the raw 32-byte X-only public key (not bech32-encoded). NIPs and relay URLs are encoded using generic tag tuples (sub-tag 0x03), consistent with NUT-18's tags array.\n\n**Encoding (JSON to TLV):**\n\n1. Parse the `nprofile` or `npub` from the JSON target field using NIP-19\n2. Store the raw 32-byte X-only public key in target (sub-tag 0x02)\n3. Store any relay URLs from the nprofile as tag tuples with key `\"r\"`\n4. Store NIPs from the tags array as tag tuples with key `\"n\"`\n\n**Decoding (TLV to JSON):**\n\n- If no `\"r\"` tag tuples are present: encode public key as `npub`\n- If `\"r\"` tag tuples are present: encode as `nprofile` using NIP-19 format\n\n#### Tag Tuple Encoding (Sub-Tag 0x03)\n\nGeneric tag tuples are encoded as:\n\n1. Key length (1 byte)\n2. Key string (UTF-8)\n3. For each value:\n   - Value length (1 byte)\n   - Value string (UTF-8)\n\nThis allows encoding arbitrary key-value pairs for extensibility.\n\n### NUT-10 Sub-TLV (Tag 0x08)\n\nNUT-10 spending conditions are encoded as nested TLV structures:\n\n| Sub-Tag | Field     | Type        | Description                                                  |\n| ------- | --------- | ----------- | ------------------------------------------------------------ |\n| 0x01    | kind      | u8          | Secret kind (0=P2PK, 1=HTLC, etc.)                           |\n| 0x02    | data      | bytes       | Kind-specific data (UTF-8 encoded)                           |\n| 0x03    | tag_tuple | sub-sub-TLV | Tag tuple (repeatable, uses same encoding as transport tags) |\n\n#### NUT-10 Kind Enumeration\n\nThe following kind values are defined for NUT-10 spending conditions:\n\n| Kind Value | Name | Description                                                      |\n| ---------- | ---- | ---------------------------------------------------------------- |\n| 0x00       | P2PK | Pay to Public Key - requires signature from specified public key |\n| 0x01       | HTLC | Hash Time Locked Contract - requires preimage of hash            |\n\nAdditional kind values may be defined in future NUT specifications. Unknown kind values SHOULD be preserved when re-encoding but MAY be ignored during validation.\n\n### Supported Method Sub-TLV (Tag 0x0a)\n\nEach supported method is encoded as a nested TLV structure. The tag is repeatable, one sub-TLV per method, decoding to the `sm` array in JSON:\n\n| Sub-Tag | Field  | Type   | Description                                                       |\n| ------- | ------ | ------ | ----------------------------------------------------------------- |\n| 0x01    | method | string | Method name, e.g. `\"bolt11\"` (corresponds to `mn` in JSON)        |\n| 0x02    | fee    | u64    | Optional per-method fee; absent = 0 (corresponds to `mf` in JSON) |\n\nWhen the per-method fee applies and how much the payer owes is defined in [NUT-18][18].\n\n## Example\n\nThis is an example payment request expressed as JSON:\n\n```json\n{\n  \"i\": \"demo123\",\n  \"a\": 1000,\n  \"u\": \"sat\",\n  \"s\": true,\n  \"m\": [\"https://mint.example.com\"],\n  \"d\": \"Coffee payment\"\n}\n```\n\nThis payment request encodes to the NUT-26 format as:\n\n```\nCREQB1QYQQWER9D4HNZV3NQGQQSQQQQQQQQQQRAQPSQQGQQSQQZQG9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5RQQRJRDANXVET9YPCXZ7TDV4H8GXHR3TQ\n```\n\n## BIP-321 Integration\n\nNUT-26 payment requests can be included in [BIP-321](https://bips.dev/321/) Bitcoin URIs using the `creq` query parameter. This enables unified QR codes that support multiple payment methods.\n\nThe full Bech32m-encoded string (including the `creqb1` prefix) is used as the parameter value.\n\n> [!NOTE]\n> Implementations SHOULD use uppercase for optimal QR code compatibility. Decoders MUST accept both uppercase and lowercase input.\n\n### Unified QR Codes\n\nBy including both Lightning payment data and Cashu payment requests in a single BIP-321 URI, a single QR code can serve as an entry point for multiple payment methods. Wallets that support a given payment method can use it, while others can fall back to supported methods.\n\n### Examples\n\n#### Cashu Only\n\nA Bitcoin URI containing only a Cashu payment request:\n\n```\nbitcoin:?creq=CREQB1QYQQWER9D4HNZV3NQGQQSQQQQQQQQQQRAQPSQQGQQSQQZQG9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5RQQRJRDANXVET9YPCXZ7TDV4H8GXHR3TQ\n```\n\n#### BOLT11 + Cashu\n\nA unified QR code supporting both Lightning (BOLT11) and Cashu:\n\n```\nbitcoin:?lightning=lnbc210n1p56amv8sp5v5gvxh0swyje66pcxtqtqh3qmzxd74fkxhjmzgzw7nff9fuhcdgqpp566zkpvgxn832cg06ghlk48tqntffkp6nsemw8g836pjfw4tdhdmsdqgde6hgv3kxqyjw5qcqpjrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glc7rf05uqqg8gqqqqqqqlgqqqqrucqjq9qxpqysgqrdvjgsemgtxs3wa38xf8qs3awqf5ksw0d3mpm07t9yl7xkasyzgz8rw5qlas6r4ers68u7nmgvqsgar4t9lr47fwlaue302nrasdekgqnvfjmp&creq=CREQB1QYQQWER9D4HNZV3NQGQQSQQQQQQQQQQRAQPSQQGQQSQQZQG9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5RQQRJRDANXVET9YPCXZ7TDV4H8GXHR3TQ\n```\n\n#### BOLT12 + Cashu\n\nA unified QR code supporting both LightningOffers (BOLT12) and Cashu:\n\n```\nbitcoin:?lno=lno1pgzkcctzv4kpvggzu2th0tw73fx2ygyd7gyuul490zkhkmz75ncz6q9nkyp9m78932tq&creq=CREQB1QYQQWER9D4HNZV3NQGQQSQQQQQQQQQQRAQPSQQGQQSQQZQG9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5RQQRJRDANXVET9YPCXZ7TDV4H8GXHR3TQ\n```\n\n[00]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crqqpd6d9\n[10]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnqx5tye7\n[18]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnsn4ruvd\n","sig":"d437579ddad335d8acbbb066d5c094fb9d11e045cd885f9345288a4b6fdaad23fa21fab64633b58b7d269632f21cc4a844c01ab8326d07a5aa8ad0fcea4b0c40"}