Bitcoin Output Descriptors Reference Guide
Reference guide to Bitcoin output descriptors: syntax, types (pk, pkh, wpkh, tr, multi), checksum, and key expressions. Learn how descriptors replace xpubs.
What Are Output Descriptors?
Bitcoin output descriptors are a standardized language for describing which scripts (and therefore which addresses) a wallet should track. Defined across BIP 380 through BIP 386, descriptors package script type, key material, and derivation metadata into a single, unambiguous string. Instead of handing someone an xpub and hoping they guess the correct address format, you hand them a descriptor that encodes everything needed to reproduce your wallet's address set.
Descriptors were introduced by Pieter Wuille in Bitcoin Core v0.17 (October 2018) and have since become the default wallet representation. As of Bitcoin Core v23.0 (April 2022), all newly created wallets are descriptor wallets by default. The descriptor format also serves as the foundation for miniscript, enabling wallets to reason about arbitrary spending policies without custom code per script template.
Why Descriptors Replace Raw xpubs
An extended public key alone carries no information about how it should be used. When SegWit introduced multiple new address types, the same xpub could produce P2PKH, P2SH-P2WPKH, or native P2WPKH addresses depending on interpretation. This ambiguity caused real interoperability failures: users who exported an xpub from one wallet and imported it into another would see the wrong addresses, miss UTXOs, or report incorrect balances.
Descriptors solve this by making the script type explicit. A descriptor like wpkh([fingerprint/84'/0'/0']xpub...) tells any compliant wallet exactly which derivation path was used, which script type to construct, and which master key the xpub originated from. This eliminates guesswork and enables reliable wallet migration.
For a deeper look at how Bitcoin address types evolved and why this ambiguity emerged, see our research on Bitcoin address types from P2PKH to Taproot.
BIP Specification Map
The descriptor standard is split across seven BIPs, each covering a specific set of descriptor functions. All were authored by Pieter Wuille and Ava Chow, all carry "Deployed" status.
| BIP | Title | Descriptor Functions |
|---|---|---|
| 380 | General Operation | Framework: key expressions, checksum, syntax rules |
| 381 | Non-SegWit Descriptors | pk(), pkh(), sh() |
| 382 | SegWit Descriptors | wpkh(), wsh() |
| 383 | Multisig Descriptors | multi(), sortedmulti() |
| 384 | combo() Descriptor | combo() |
| 385 | raw() and addr() Descriptors | raw(), addr() |
| 386 | Taproot Descriptor | tr() |
Additional BIPs extend the system: BIP 387 adds multi_a() and sortedmulti_a() for tapscript multisig, BIP 389 defines the multipath /<0;1>/ syntax, and BIP 390 introduces musig() for MuSig2 key aggregation.
Descriptor Type Reference
Each descriptor function maps to a specific Bitcoin script template. The following table covers every standardized descriptor type, its output script, and where it can appear.
| Function | Script Type | Context | BIP |
|---|---|---|---|
pk(KEY) | P2PK: bare pubkey | Top-level, inside sh/wsh | 381 |
pkh(KEY) | P2PKH: pubkey hash | Top-level, inside sh | 381 |
wpkh(KEY) | P2WPKH: native SegWit | Top-level, inside sh | 382 |
sh(SCRIPT) | P2SH: script hash wrapper | Top-level only | 381 |
wsh(SCRIPT) | P2WSH: witness script hash | Top-level, inside sh | 382 |
tr(KEY) / tr(KEY, TREE) | P2TR: Taproot | Top-level only | 386 |
multi(k, KEY...) | k-of-n multisig, specified order | Inside sh/wsh | 383 |
sortedmulti(k, KEY...) | k-of-n multisig, keys sorted | Inside sh/wsh | 383 |
multi_a(k, KEY...) | Tapscript k-of-n multisig | Inside tr() script tree | 387 |
sortedmulti_a(k, KEY...) | Tapscript k-of-n, keys sorted | Inside tr() script tree | 387 |
combo(KEY) | P2PK + P2PKH (+ P2WPKH + P2SH-P2WPKH if compressed) | Top-level only | 384 |
addr(ADDR) | Script for the given address | Top-level only | 385 |
raw(HEX) | Exact hex script | Top-level only | 385 |
Key Expression Syntax
Key expressions define how public keys are represented inside descriptors. BIP 380 supports several key formats, each suited to different use cases.
Hex Public Keys
Raw public keys can be specified as hex strings. Compressed keys (33 bytes, prefix 02 or 03) are required for SegWit and Taproot contexts. X-only keys (32 bytes, no prefix) are used inside tr() descriptors. Uncompressed keys (65 bytes, prefix 04) are only valid in legacy pk() and pkh() contexts.
Extended Keys with Derivation Paths
HD wallet xpubs and xprvs (as defined in BIP 32) can include derivation path suffixes. For example, xpub6ERApfZ.../0/2 means "derive child 0, then child 2 from this extended key." Hardened derivation steps use h, ', or H as the suffix (e.g., /44h/0h/0h). Since hardened derivation requires the private key, descriptors with hardened steps after the xpub require an xprv.
Key Origin Information
The optional origin prefix records the master key fingerprint and full derivation path: [d34db33f/84'/0'/0']xpub6ERApfZ.... The fingerprint is 4 bytes (8 hex characters) of the master public key's Hash160. This metadata lets signing devices identify which master key produced the xpub and verify they hold the corresponding private key. It is essential for PSBT workflows, where multiple signers need to determine which inputs they can sign.
Ranged Descriptors and Wildcards
The /* wildcard makes a descriptor ranged: it represents a family of scripts, one per child index. For example, wpkh([d34db33f/84'/0'/0']xpub.../0/*) generates a new P2WPKH address for each index (0, 1, 2, ...). BIP 389 extends this with multipath syntax: /<0;1>/* encodes both the receive chain (index 0) and change chain (index 1) in a single descriptor. For more on how derivation paths work, see the key derivation paths reference.
Descriptor Examples
The following examples demonstrate common wallet configurations. Each shows the descriptor syntax alongside the address type it produces.
pkh([d34db33f/44'/0'/0']xpub6ERApfZ.../0/*)#checksumWrapped SegWit P2SH-P2WPKH (BIP 49):
sh(wpkh([d34db33f/49'/0'/0']xpub6ERApfZ.../0/*))#checksumNative SegWit P2WPKH (BIP 84):
wpkh([d34db33f/84'/0'/0']xpub6ERApfZ.../0/*)#checksumTaproot P2TR single-key (BIP 86):
tr([d34db33f/86'/0'/0']xpub6ERApfZ.../0/*)#checksumwsh(sortedmulti(2,[aabbccdd/48'/0'/0'/2']xpubA.../0/*,[11223344/48'/0'/0'/2']xpubB.../0/*,[55667788/48'/0'/0'/2']xpubC.../0/*))#checksumUsing sortedmulti instead of multi is standard practice for multisig wallets because it ensures the same script regardless of the order keys are provided. This is critical for wallet interoperability: two coordinators given the same three xpubs will always produce identical addresses.
Multipath descriptor with receive and change (BIP 389):
wpkh([d34db33f/84'/0'/0']xpub6ERApfZ.../<0;1>/*)#checksumChecksum Format
Every descriptor can include an 8-character checksum appended after a # separator. The checksum uses the bech32 character set (qpzry9x8gf2tvdw0s3jn54khce6mua7l) and a BCH error-detecting code. It guarantees detection of any single-character error and all errors of up to 3 characters in descriptors up to 49,154 characters long.
Bitcoin Core's getdescriptorinfo RPC computes and appends the correct checksum. Several RPCs including importdescriptors and deriveaddresses require the checksum to be present. When sharing descriptors between wallets, always include the checksum to prevent transcription errors.
Nesting Rules
Not every descriptor can appear inside every other. The nesting rules enforce valid Bitcoin script combinations:
sh()can wrap:wpkh(),wsh(),multi(),sortedmulti()wsh()can wrap:pk(),pkh(),multi(),sortedmulti(), miniscript expressionstr()script tree can contain:pk(),multi_a(),sortedmulti_a(), miniscript expressionscombo(),addr(),raw()are top-level only and cannot be nestedwpkh()andwsh()require compressed public keystr()uses x-only public keys (compressed keys are auto-converted by dropping the prefix byte)
Wallet Support
Descriptor wallet support has expanded steadily since 2018. The following wallets and libraries handle descriptor import, export, or native operation.
| Wallet / Library | Type | Descriptor Support |
|---|---|---|
| Bitcoin Core | Full node | Full native (default since v23) |
| Sparrow Wallet | Desktop | Full import/export |
| Liana | Desktop | Full native with miniscript |
| Bitcoin Dev Kit (BDK) | Library | Full native (primary abstraction) |
| Nunchuk | Mobile/Desktop | Full import/export |
| Specter Desktop | Desktop coordinator | Full support |
| COLDCARD | Hardware | Descriptor export |
| Bitcoin Keeper | Mobile | Import with miniscript |
The Bitcoin Dev Kit (BDK), used by many modern wallet SDKs, treats descriptors as its core wallet primitive. When building on BDK, you define a wallet entirely through its descriptors rather than through separate key and script-type configuration. This makes BDK-based wallets inherently portable: self-custodial applications built with Spark or similar infrastructure can export a user's descriptor for recovery in any compatible wallet.
Bitcoin Core Version History
Descriptor support in Bitcoin Core has evolved across multiple releases:
| Version | Year | Descriptor Feature |
|---|---|---|
| v0.17 | 2018 | Initial descriptor support: scantxoutset, getdescriptorinfo, deriveaddresses |
| v0.18 | 2019 | Checksum support added; importmulti accepts descriptors |
| v0.21 | 2021 | Descriptor wallets introduced (SQLite); importdescriptors RPC |
| v22.0 | 2021 | tr() Taproot descriptors; listdescriptors RPC |
| v23.0 | 2022 | Descriptor wallets become the default; BIP 86 Taproot derivation |
| v25.0 | 2023 | Miniscript signing for wsh() descriptors |
| v26.0 | 2023 | Full miniscript in tr() Taproot descriptors |
Descriptors and Miniscript
Miniscript (BIP 379) is a structured subset of Bitcoin Script that enables wallets to analyze, compose, and generically sign for arbitrary spending policies. Descriptors and miniscript are complementary: a descriptor specifies the keys and derivation, while a miniscript expression inside the descriptor defines the spending conditions.
Miniscript expressions are valid inside wsh() (for SegWit) and inside tr() script tree leaves (for Taproot). For example, a decaying multisig that starts as 3-of-3 and relaxes to 2-of-3 after a certain block height can be expressed as a single miniscript descriptor. For more on how miniscript extends Bitcoin's programmability, see our research on miniscript spending policies and the Bitcoin Script programmability overview.
Practical Usage
Exporting a Wallet Descriptor
In Bitcoin Core, use listdescriptors to export all descriptors for a wallet. The output includes the descriptor string with checksum, the derivation range, and the timestamp. For cold storage recovery planning, store these descriptors alongside your seed phrase: the seed recovers the keys, and the descriptor tells the recovery wallet how to use them.
Importing Descriptors
Use importdescriptors in Bitcoin Core to import one or more descriptors into a wallet. You can specify the derivation range (range parameter) and the rescan timestamp. For watch-only wallets, import the xpub-based descriptor without private keys. For signing wallets, import with xprvs or use PSBT workflows where the signing device holds the keys. See the PSBT workflow guide for details.
Deriving Addresses
The deriveaddresses RPC takes a descriptor and an optional range, returning the corresponding addresses. This is useful for verifying that an xpub viewer generates the same addresses as your signing device. For ranged descriptors, you can generate addresses 0 through 999 to check for any address gap issues.
Frequently Asked Questions
What is a Bitcoin output descriptor?
A Bitcoin output descriptor is a string that fully describes a set of output scripts (addresses) a wallet should track. It encodes the script type (P2PKH, P2WPKH, P2TR, multisig, etc.), the public keys or extended keys, the derivation path, and the master key fingerprint. Descriptors were standardized in BIP 380 through BIP 386 and are the default wallet format in Bitcoin Core since version 23.0.
How do output descriptors differ from xpubs?
An xpub is a raw extended public key with no information about script type or derivation context. A descriptor wraps the xpub with explicit script semantics: it specifies whether the key produces P2PKH, P2WPKH, P2TR, or multisig addresses, records the full derivation path via key origin info, and includes a checksum for error detection. Two wallets given the same descriptor will always produce identical addresses, which is not guaranteed with a bare xpub.
What is the descriptor checksum and how is it computed?
The descriptor checksum is an 8-character suffix appended after a # character. It uses a BCH error-detecting code over the bech32 character set. The algorithm maps each character in the descriptor to a symbol, processes them through a polynomial modular arithmetic function, and outputs 8 characters that detect any single-character error. Bitcoin Core's getdescriptorinfo RPC computes the checksum automatically.
Which wallets support descriptor import and export?
Bitcoin Core has full native support and uses descriptor wallets by default since v23.0 (2022). Sparrow Wallet, Liana, Nunchuk, and Specter Desktop support descriptor import and export. The Bitcoin Dev Kit (BDK) library uses descriptors as its primary wallet abstraction. Hardware wallets like COLDCARD can export descriptors. The ecosystem continues to adopt the format as older xpub-only workflows are phased out.
What is the difference between multi() and sortedmulti()?
Both create k-of-n multisig scripts, but multi() places keys in the exact order specified, while sortedmulti() sorts keys lexicographically before constructing the script. Use sortedmulti() for multisig wallets to ensure that all participants produce identical addresses regardless of the order they provide their keys. This is the standard for interoperable multisig setups using tools like the multisig planner.
How do descriptors work with Taproot?
The tr() descriptor (BIP 386) handles Taproot outputs. A simple key-path spend uses tr(KEY). Script-path spends add a tree of scripts: tr(KEY, {SCRIPT_A, SCRIPT_B}). Inside the script tree, tapscript-specific functions like multi_a() replace legacy multi() because Taproot uses Schnorr signatures with OP_CHECKSIGADD instead of OP_CHECKMULTISIG. For background on Taproot's design, see our Taproot and Schnorr signatures explainer.
Can I use descriptors for watch-only wallets?
Yes. Replace xprvs with xpubs in the descriptor and import it as watch-only. The wallet will track balances and generate addresses but cannot sign transactions. This is the standard pattern for coordinating with hardware signers: the watch-only wallet builds unsigned transactions or PSBTs, and the signing device (which holds the private keys) signs them offline.
This reference is for informational purposes only and does not constitute financial advice. Descriptor syntax and wallet support may change as BIPs are updated. Always verify descriptor compatibility with your specific wallet software before relying on it for fund recovery.
Build with Spark
Integrate bitcoin, Lightning, and stablecoins into your app with a few lines of code.
Read the docs →
