The BOLT 11 invoice format has proven popular but has several limitations:
hfield was a boutique extraction of the
pwas often mishandled, and amounts in pico-bitcoin are harder than the modern satoshi-based counting.
payment_secretdesigned to prevent probing by other nodes in the path was only useful if the invoice remained private between the payer and payee.
Here we use "user" as shorthand for the individual user's lightning node and "merchant" as the shorthand for the node of someone who is selling or has sold something.
There are two basic payment flows supported by BOLT 12:
The general user-pays-merchant flow is:
The merchant-pays-user flow (e.g. ATM or refund):
Note that the normal lightning "proof of payment" can only demonstrate that an
invoice was paid (by showing the preimage of the
payment_hash), not who paid
it. The merchant can claim an invoice was paid, and once revealed, anyone can
claim they paid the invoice, too.
Providing a key in invoice_request allows the payer to prove that they were the one to request the invoice. In addition, the Merkle construction of the BOLT 12 invoice signature allows the user to reveal invoice fields in case of a dispute selectively.
Each of the forms documented here are in TLV format.
The supported ASCII encoding is the human-readable prefix, followed by a
1, followed by a bech32-style data string of the TLVs in order,
optionally interspersed with
+ (for indicating additional data is to
come). There is no checksum, unlike bech32m.
Readers of a bolt12 string:
+followed by zero or more whitespace characters between two bech32 characters:
The use of bech32 is arbitrary but already exists in the bitcoin world. We currently omit the six-character trailing checksum: QR codes have their own checksums anyway, and errors don't result in loss of funds, simply an invalid offer (or inability to parse).
The use of
+ (which is ignored) allows use over limited
text fields like Twitter:
lno1xxxxxxxx+ yyyyyyyyyyyy+ zzzzz
All signatures are created as per
and tagged as recommended there. Thus we define H(
tag) || SHA256(
msg), and SIG(
as the signature of H(
Each form is signed using one or more signature TLV elements: TLV
types 240 through 1000 (inclusive). For these,
the tag is "lightning" ||
msg is the
Merkle-root; "lightning" is the literal 9-byte ASCII string,
messagename is the name of the TLV stream being signed (i.e. "invoice_request" or "invoice") and the
fieldname is the TLV field containing the
signature (e.g. "signature").
The formulation of the Merkle tree is similar to that proposed in BIP-341, with each TLV leaf paired with a nonce leaf to avoid revealing adjacent nodes in proofs.
The Merkle tree's leaves are, in TLV-ascending order for each tlv:
The Merkle tree inner nodes are H("LnBranch", lesser-SHA256||greater-SHA256); this ordering means proofs are more compact since left/right is inherently determined.
If there is not exactly a power of 2 leaves, then the tree depth will be uneven, with the deepest tree on the lowest-order leaves.
e.g. consider the encoding of an
signature with TLVs TLV1, TLV2, and TLV3 (of types 1, 2 and 3 respectively):
L1=H("LnLeaf",TLV1) L1nonce=H("LnNonce"||TLV1,1) L2=H("LnLeaf",TLV2) L2nonce=H("LnNonce"||TLV1,2) L3=H("LnLeaf",TLV3) L3nonce=H("LnNonce"||TLV1,3) Assume L1 < L1nonce, L2 > L2nonce and L3 > L3nonce. L1 L1nonce L2 L2nonce L3 L3nonce \ / \ / \ / v v v v v v L1A=H("LnBranch",L1||L1nonce) L2A=H("LnBranch",L2nonce||L2) L3A=H("LnBranch",L3nonce||L3) Assume L1A < L2A: L1A L2A L3A=H("LnBranch",L3nonce||L3) \ / | v v v L1A2A=H("LnBranch",L1A||L2A) L3A=H("LnBranch",L3nonce||L3) Assume L1A2A > L3A: L1A2A=H("LnBranch",L1A||L2A) L3A \ / v v Root=H("LnBranch",L3A||L1A2A) Signature = SIG("lightninginvoicesignature", Root, nodekey)
Offers are a precursor to an invoice_request: readers will request an invoice (or multiple) based on the offer. An offer can be much longer-lived than a particular invoice, so it has some different characteristics; in particular the amount can be in a non-lightning currency. It's also designed for compactness to fit inside a QR code easily.
Note that the non-signature TLV elements get mirrored into invoice_request and invoice messages, so they each have specific and distinct TLV ranges.
The human-readable prefix for offers is
A writer of an offer:
offer_node_idto the node's public key to request the invoice from.
offer_descriptionto a complete description of the purpose of the payment.
offer_chainsthe offer is valid for.
offer_chains, implying that bitcoin is only chain.
offer_amountis required for successful payment:
offer_amountto the amount expected (per item).
offer_amountis that of all entries in
amountin multiples of the minimum lightning-payable unit (e.g. milli-satoshis for bitcoin).
iso4217as an ISO 4712 three-letter code.
offer_amountin the currency unit adjusted by the ISO 4712 exponent (e.g. USD cents).
offer_metadatafor its own use.
featuresto the bitmap of bolt12 features.
seconds_from_epochto the number of seconds after midnight 1 January 1970, UTC that invoice_request should not be attempted.
offer_pathscontaining one or more paths to the node from publicly reachable nodes.
A reader of an offer:
offer_featurescontains unknown odd bits that are non-zero:
offer_featurescontains unknown even bits that are non-zero:
offer_chainsis not set:
offer_descriptionis not set:
offer_node_idis not set:
offer_amountto provide the user with a cost estimate:
offer_currencyfield if set
The entire offer is reflected in the invoice_request, both for
completeness (so all information will be returned in the invoice), and
so that the offer node can be stateless. This makes
particularly useful, since it can contain an authentication cookie to
validate the other fields.
A signature is unnecessary, and makes for a longer string (potentially limiting QR code use on low-end cameras); if the offer has an error, no invoice will be given since the request includes all the non-signature fields.
offer_quantity_max is allowed to be 1, which seems useless, but
useful in a system which bases it on available stock. It would be
painful to have to special-case the "only one left" offer generation.
Invoice Requests are a request for an invoice; the human-readable prefix for
invoice requests is
There are two similar-looking uses for invoice requests, which are almost identical from a workflow perspective, but are quite different from a user's point of view.
One is a response to an offer; this contains the
all other offer details, and is generally received over an onion
message: if it's valid and refers to a known offer, the response is
generally to reply with an
invoice using the
reply_path field of
the onion message.
The second case is publishing an
invoice_request without an offer,
such as via QR code. It contains no
offer_node_id (using the
invreq_payer_id instead, as it in the one paying), and the
other offer fields are filled by the creator of the
forming a kind of offer-to-send-money.
invreq_metadata is numbered 0 (not in the
80-159 range for other invreq fields) as this is the first
TLV element, which ensures payer-provided entropy is used in hashing
for Signature Calculation.
invreq_chainto one of
offer_chainsunless that chain is bitcoin, in which case it MAY omit
invreq_chainit MUST set it to bitcoin.
sigas detailed in Signature Calculation using the
offer_amountis not present:
msatas greater or equal to amount expected by
offer_amount(and, if present,
invreq_payer_idto a transient public key.
invreq_quantityto greater than zero.
invreq_quantityless than or equal to
offer_issueras it would for an offer.
invreq_payer_idas it would set
offer_node_idfor an offer.
invreq_chainthe offer is valid for.
invreq_metadatato an unpredictable series of bytes.
msatin multiples of the minimum lightning-payable unit (e.g. milli-satoshis for bitcoin) for
invreq_chain(or for bitcoin, if there is no
featuresto the bitmap of features.
invreq_metadataare not present.
invreq_featurescontains unknown odd bits that are non-zero:
invreq_featurescontains unknown even bits that are non-zero:
signatureis not correct as detailed in Signature Calculation using the
offer_node_idis present (response to an offer):
invreq_quantityis zero, OR greater than
offer_currencyis not the
invreq_chaincurrency, convert to the
invreq_quantityis present, multiply by
msatis less than the expected amount.
msatgreatly exceeds the expected amount.
offer_node_id, not a response to our offer):
invreq_amountis not present.
offer_currency) for informational display to user.
offer_pathsif present, otherwise MUST use
invreq_payer_idas the node id to send to.
invreq_chainis not present:
chainis not a supported chain.
invreq_metadata might typically contain information about the derivation of the
invreq_payer_id. This should not leak any information (such as using a simple
BIP-32 derivation path); a valid system might be for a node to maintain a base
payer key and encode a 128-bit tweak here. The payer_id would be derived by
tweaking the base key with SHA256(payer_base_pubkey || tweak). It's also
the first entry (if present), ensuring an unpredictable nonce for hashing.
invreq_payer_note allows you to compliment, taunt, or otherwise engrave
graffiti into the invoice for all to see.
Users can give a tip (or obscure the amount sent) by specifying an
invreq_amount in their invoice request, even though the offer specifies an
offer_amount. The recipient will only accept this if
the invoice request amount exceeds the amount it's expecting (i.e. its
offer_amount after any currency conversion, multiplied by
invoice_requests are currently required to
explicitly state the
invreq_amount in the chain currency,
offer_currency are redundant (but may be
informative for the payer to know how the sender claims
invreq_amount was derived).
Invoices are a payment request, and when the payment is made, it can be combined with the invoice to form a cryptographic receipt.
The recipient sends an
invoice in response to an
The 'MPP support' invoice feature indicates that the payer MUST (16) or MAY (17) use multiple part payments to pay the invoice.
Some implementations may not support MPP (e.g. for small payments), or may (due to capacity limits on a single channel) require it.
A writer of an invoice:
invoice_created_atto the number of seconds since Midnight 1 January 1970, UTC when the invoice was created.
invoice_amountto the minimum amount it will accept, in units of the minimal lightning-payable unit (e.g. milli-satoshis for bitcoin) for
invoice_request(including unknown fields).
invoice_amountto the expected amount.
invreq_chainas it would for an invoice_request.
offer_descriptionas it would for an offer.
invoice_payment_hashto the SHA256 hash of the
payment_preimagethat will be given in return for payment.
invoice_node_idto a valid public key.
sigto the signature using
invoice_node_idas described in Signature Calculation.
seconds_from_creationto the number of seconds after
invoice_created_atthat payment of this invoice should not be attempted.
invoice_fallbacksin order of most-preferred to least-preferred if it has a preference.
versionas a valid witness version and
addressas a valid witness program
invoice_pathscontaining one or more paths to the node.
invoice_pathsin order of most-preferred to least-preferred if it has a preference.
invoice_blindedpaywith exactly one
paths, in order.
allowed_features(or empty, if no
offer_node_idis present, and
invreq_payer_idis identical to a previous
A reader of an invoice:
invoice_amountis not present.
invoice_created_atis not present.
invoice_payment_hashis not present.
invoice_node_idis not present.
invoice_featurescontains unknown odd bits that are non-zero:
invoice_featurescontains unknown even bits that are non-zero:
invoice_pathsis not present or is empty.
invoice_blindedpayis not present.
invoice_blindedpaydoes not contain exactly one
featureshas any unknown even bits set.
offer_node_idis present (invoice_request for an offer):
invoice_node_idis not equal to
invoice_node_idis correct, out-of-band.
signatureis not a valid signature using
invoice_node_idas described in Signature Calculation.
invoice_pathsover later ones if it has no other reason for preference.
invoice_featurescontains the MPP/compulsory bit:
invoice_featurescontains the MPP/optional bit:
msatis not within the amount range authorized.
versionis greater than 16.
addressis less than 2 or greater than 40 bytes.
addressdoes not meet known requirements for the given
Because the messaging layer is unreliable, it's quite possible to
receive multiple requests for the same offer. As it's the caller's
responsibility not to reuse
the writer doesn't have to check all the fields are duplicates before
simply returning a previous invoice. Note that such caching is optional,
and should be carefully limited when e.g. currency conversion is involved,
or if the invoice has expired.
The invoice duplicates fields rather than committing to the previous invreq. This flattened format simplifies storage at some space cost, as the payer need only remember the invoice for any refunds or proof.
The reader of the invoice cannot trust the invoice correctly reflects the invreq fields, hence the requirements to check that they are correct, although allowance is made for simply sending an unrequested invoice directly.
Note that the recipient of the invoice can determine the expected amount from either the offer it received, or the invreq it sent, so often already has authorization for the expected amount.
invoice_relative_expiry of 7200 seconds, which is generally a
sufficient time for payment, even if new channels need to be opened.
Blinded paths provide an equivalent to
payment_metadata used in BOLT 11.
invreq_payer_id is public, we force the use of blinding paths to keep these features.
If the recipient does not care about the added privacy offered by blinded paths, they can create a path of length 1 with only themselves.
Rather than provide detailed per-hop-payinfo for each hop in a blinded path, we aggregate the fees and CLTV deltas. This avoids trivially revealing any distinguishing non-uniformity which may distinguish the path.
In the case of an invoice where there was no offer (just an invoice request), the payer needs to ensure that the invoice is from the intended payment recipient. This is the basis for the suggestion to confirm the invoice_node_id for this case.
Raw invoices (not based on an invoice_request) are generally not supported, though an implementation is allowed to support them, and we may define the behavior in future.
Informative errors can be returned in an onion message
field (via the onion
reply_path) for either
A writer of an invoice_error:
errorto an explanatory string.
erroneous_fieldto a specific field number in the
invoice_requestwhich had a problem.
suggested_valueto a valid field for that
A reader of an invoice_error: FIXME!
Usually an error message is sufficient for diagnostics, however future enhancements may make automated handling useful.
In particular, we could allow non-offer-response
invreq_amount in future and use offer fields to
indicate alternate currencies. ("I will send you 10c!"). Then the
sender of the invoice would have to guess how many msat that was,
and could use the
invoice_error to indicate if the recipient disagreed
with the conversion so the sender can send a new invoice.
invoice_requestis another offer, perhaps with a signature from the original
invreq_refund_forto support proofs.
invoice_replacefor requesting replacement of a (stuck-payment) invoice with a new one.
invoice_requestwith alternate currencies?
offer_quantity_unitto indicate stepping for quantity (e.g. 100 grams).