Chapter 19 - Encoding
19
Nostr Implementation Possibility - Hex № 19

Bech32-Encoded Entities

Checksummed, human-friendly text formats for keys, ids, and shareable pointers. For display, input, and sharing only - never for the wire.

1 Overview

A raw Nostr key or event id is a 32-byte value shown as 64 hex characters. A public key, a private key, and an event id all look the same: a long string of hex digits. This NIP defines bech32-encoded text formats that give each kind of value its own readable prefix.

Bech32 is a checksummed text encoding. A typo in a bech32 string almost always fails the checksum, so a client can reject a bad paste instead of silently using the wrong key. NIP-19 uses the classic bech32 checksum, not the newer bech32m variant used by some other systems.

These formats exist for humans: showing a key on screen, printing a QR code, copying and pasting, typing an id into a search box, or sharing a link. They are not part of the core protocol described in NIP-01. The core protocol still runs on hex and raw bytes.

Hint!

Store keys and ids as hex or binary inside your app. Encode to bech32 only at the last moment, when you show a value to a user or accept one from a user. Convert back to hex immediately on input.

2 Bare Keys and IDs

Three entities are 32-byte values with no extra metadata. Each gets its own bech32 prefix so a client, and a human, can tell them apart at a glance.

Bare bech32 prefixes
PrefixEncodesRaw size
npuba public key32 bytes
nseca private key32 bytes
notean event id32 bytes
Example - hex public key to npub
-- hex pubkey --
3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d

-- same key, npub form --
npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6
Warning!

Bare bech32 forms never go inside a signed event or a NIP-05 JSON response. An npub key MUST NOT appear in a NIP-01 event or a NIP-05 response. Only lowercase hex is valid there.

A client should still accept a key typed as hex or as npub. Convert whichever form arrives into hex internally, right away.

3 Shareable Identifiers with TLV

Sharing a profile or an event often means more than the bare key or id. An app may want to attach relay hints, an author, or a kind, so another app can find and display the entity faster. For this, NIP-19 defines a second family of prefixes whose payload is a binary-encoded list of TLV items: Type, Length, Value.

Each TLV item has a 1-byte type (T, a number 0-255), a 1-byte length (L, a number 0-255), and then exactly L bytes of value (V). Items are packed back to back until the payload ends.

TLV item anatomy (repeats until the payload ends) T L V 1 byte 1 byte L bytes type 0-3 length of V the value itself next TLV item ...
Fig. 19-1 — TLV item anatomy. Type and length are always 1 byte each. The value's size is whatever length says.
TLV-based shareable prefixes
PrefixPoints toStatus
nprofilea nostr profiledraft
neventa nostr eventdraft
naddra nostr addressable event coordinatedraft
nrelaya nostr relaydeprecated
Deprecated

nrelay is deprecated as of 2024. Do not encode new nrelay strings. A relay URL can travel instead as a relay TLV item (type 1) inside nprofile, nevent, or naddr.

TLV Type Registry

Four TLV types are standardized. The same type number means something different depending on which prefix carries it:

TLV types 0-3
TypeNameIn nprofileIn neventIn naddr
0special32-byte profile public key32-byte event idthe identifier - the event's d tag value. Empty string for a normal (non-addressable) replaceable event.
1relayoptional relay URL, ascii, may repeatoptional relay URL, ascii, may repeatoptional relay URL, ascii, may repeat
2authornot usedoptional 32-byte author pubkeyrequired 32-byte author pubkey
3kindnot usedoptional kind, 32-bit unsigned integer, big-endianrequired kind, 32-bit unsigned integer, big-endian

Type 1 (relay) may appear more than once in the same payload; each occurrence is one more relay hint. A decoder that meets a TLV type it does not recognize or support must ignore that item, not raise an error. This keeps old decoders working when a future NIP adds new TLV types.

Tech Note

The source spec names naddr's type-0 payload only "the identifier (the d tag)"; it does not spell out that this makes an naddr without an author (type 2) or kind (type 3) ambiguous to decode, even though both are marked required for naddr in the same table. The source also gives no encoding example for nevent, naddr, or the deprecated nrelay - only npub, nsec, and nprofile have worked examples - and it never states why nrelay was deprecated.

4 Usage Rules

Bech32 entities live at the edge of an app, where a human reads or types a value. They never travel inside the protocol messages defined in NIP-01.

Where bech32 entities belong
Use them forNever use them for
Showing a key or id on screenAn event's id, pubkey, or a p/e/a tag value
QR codesA REQ filter, such as authors, ids, #e, or #p
Copy-paste and manual sharingAny field that NIP-01 defines as hex
User input fields (a client converts to hex right away)A NIP-05 JSON response
nostr: links, see NIP-21-
Event - Tags Filters (NIP-01) hex / binary only Display - QR - clipboard nostr: links bech32 (npub, nevent, ...) encode decode on input Relay (WebSocket wire) ✕ bech32 direct to relay - never hex on the wire - always
Fig. 19-2 — The usage boundary. Bech32 forms cross to and from the human edge. Only hex and binary reach the relay.
Warning!

Keep bech32-formatted strings at or under 5000 characters. A very long string is a sign of too many relay hints, or a malformed payload.

5 Worked Examples

These pairs are the reference examples from the spec. Use them to check that an implementation encodes and decodes correctly.

Example - npub (bare public key)
npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg
decodes to hex pubkey
7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e
Example - nsec (bare private key)
nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5
decodes to hex private key
67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa
Example - nprofile (TLV-encoded profile)
nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p

decodes into a TLV list:
pubkey (type 0): 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
relay  (type 1): wss://r.x.com
relay  (type 1): wss://djbas.sadkb.com
T=00 L=20 special - pubkey, 32 bytes: 3bf0c63f…aefa459d T=01 L=0d relay - 13 bytes ascii: wss://r.x.com T=01 L=15 relay - 21 bytes ascii: wss://djbas.sadkb.com Lengths above are hex: 0x20 = 32, 0x0d = 13, 0x15 = 21.
Fig. 19-3 — The nprofile example, byte by byte. Three TLV items concatenated, then bech32-encoded with the nprofile prefix.

6 Version History

Notable changes to NIP-19, newest first:

  • 20262026-03Added the 5000-character size recommendation for bech32-formatted strings. (#2264, hodlbod)
  • 20252025-06Finished renaming "parameterized replaceable event" to "addressable event" throughout the text. (#1948, #1418)
  • 20242024-07Deprecated nrelay.
  • 20232023-08Defined how to encode a normal (non-addressable) replaceable event as naddr: leave the identifier empty.
  • 20232023-04Clarified that unrecognized TLV types must be ignored, not rejected; documented the optional kind TLV on nevent.
  • 20232023-03Documented the optional author pubkey TLV on nevent.
  • 20232023-02The addressable-event identifier took its final shape: renamed nref to nitem to naddr, its tag from i to a, and added the kind TLV.
  • 20232023-01Added TLV-encoded shareable identifiers: nprofile, nevent, and relay entities (nrelay). (#196, Semisol)
  • 20222022-12Added worked examples and clarified the NIP's purpose.
  • Origin2022-12-16fiatjaf published NIP-19: bech32-encoding of keys, ids, and other information. (#57)

Original Proposal - Credits

Authorfiatjaf
First commit2022-12-16 — “add NIP-19: bech32-encoding of stuff. (#57)”
PR#57 on GitHub
Statusdraft - optional
Source19.md on GitHub - full change history

7 See Also

  • NIP-01 — defines the hex id, pubkey, and tag fields that these bech32 forms encode and never replace.
  • NIP-21 — the nostr: URI scheme, the main place a bech32 entity is carried inside a clickable link.
  • NIP-27 — how a client renders an nprofile or nevent mention inline inside note text.