Glossary / Lightning & layer 2
BOLT12 offer
Also known as offer.
- What is a BOLT12 offer?
- A BOLT12 offer is a reusable Lightning payment code beginning lno1 that any number of payers can turn into their own fresh invoice by asking the issuer over the Lightning network itself.
An offer is a request to be paid that survives being paid, so one printed code serves every customer. Offers carry no signature and no checksum, and their fields occupy TLV types 2 through 22, deliberately short enough for a low-end phone camera. If you are receiving bitcoin, that is the difference between a code you print once and the invoice it mints, which expires 7200 seconds later.
How it works
A BOLT12 offer is a stream of type-length-value records written with the human readable prefix lno, a separator 1, and then bech32 style characters carrying those records in order.
There is no six character checksum on the end. The specification drops it on the grounds that QR codes carry their own checksums, and that a corrupted offer produces an unparseable string rather than a lost payment. Long offers may be broken up with a plus sign, optionally followed by whitespace, so they survive being pasted into a text field with a character limit, and a reader must strip the plus and the whitespace before decoding. A writer must use all lowercase or all uppercase, and should choose uppercase for QR codes.
The fields themselves are numbered. offer_chains is 2, offer_metadata 4, offer_currency 6, offer_amount 8, offer_description 10, offer_features 12, offer_absolute_expiry 14, offer_paths 16, offer_issuer 18, offer_quantity_max 20 and offer_issuer_id 22. A writer must stay inside types 1 to 79, with a second range from 1000000000 to 1999999999 set aside for experiments. Nothing in that list is a signature, and that is deliberate: signing would lengthen the string for no benefit, because every non-signature field gets copied into the request that follows, so a tampered offer simply produces no invoice.
Paying then takes two more messages. Your wallet sends an invoice_request, prefix lnr, over an onion message, copying every offer field including ones it does not understand and signing the result with a BIP340 signature over a Merkle root. The issuer replies with a signed invoice. The Merkle construction pairs each field's leaf with a nonce leaf, which is what lets a payer later reveal one field of a disputed invoice without exposing its neighbours, something a BOLT11 signature over the whole string cannot do.
Two of those three messages run backwards for refunds and cash machines. A merchant publishes an invoice_request naming an amount it wants to send, you reply with an invoice, and the merchant confirms the invoice_node_id before paying.
Why this matters when you buy bitcoin
An offer changes how you publish a way to be paid, and changes nothing at all about what being paid costs you.
Take the Phoenix review on this site. ACINQ publishes a fee table rather than leaving you to discover it: sending over Lightning costs 0.4% plus 4 satoshis, receiving is free when you already have inbound liquidity, and receiving when the wallet has to go on chain to make room costs 1% plus mining fees, with a new channel at 1,000 satoshis. Handing out an offer instead of an invoice does not touch any of those numbers. It removes the step where you mint a fresh code per payer, not the step where somebody pays for the liquidity.
The privacy gain is real but narrow. A Lightning address routes every request for an invoice through a web server on somebody's domain, and that operator sees the amounts and the timing. An offer routes the request over Lightning as an onion message, and offer_paths lets a node whose channels are all private stay reachable without publishing a node id at all. What that does not buy you is privacy from your own provider: our Phoenix review quotes ACINQ's FAQ saying in as many words that it knows the final destination and amount of payments.
The practical warning is about pasting. A withdrawal box on an exchange asks for a Lightning invoice, and an lno1 string is not one. Strike, one of the 63 exchange records here, is Lightning native and adds no fee of its own on transfers across more than 100 countries, and that is exactly the sort of flow where two formats get confused. None of the 41 wallet reviews or 63 exchange records on this site currently documents offer support as a feature, so treat it as something to confirm in your own wallet's release notes rather than assume.
A hat stall's offer, and the invoice it becomes
A stall selling hats for 25 dollars writes an offer with offer_currency set to USD and offer_amount set to 2500.
That is the specification's rule for a currency other than bitcoin: a three letter ISO 4217 code, and an amount in the currency unit adjusted by the ISO 4217 exponent, which for dollars means cents. Setting offer_amount at all makes offer_description mandatory, because a payer being charged for something specific has to know what it was. If three hats are left, offer_quantity_max is 3, and if the stall has no fixed limit it is 0. A value of 1 is explicitly allowed, awkward as it looks, so a stock system never needs a special case for the last item. offer_issuer might read hats@example.com Hat Stall, since the spec asks for a domain first and any friendly text after a space.
Your wallet reads that, quotes you 25 dollars, and sends an invoice_request carrying invreq_quantity of 2 for two hats, an invreq_payer_id, and optionally an invreq_payer_note. Back comes an invoice with a payment hash, a creation timestamp, at least one blinded path, and a fee quote per path covering base fee, proportional fee, CLTV delta and the smallest and largest HTLC it will carry. Two rules protect you here. Your wallet must reject the invoice if any field in the ranges 0 to 159 fails to match what it sent, and it must warn you if the millisatoshi amount differs significantly from the dollar estimate the offer gave. Unless the issuer sets invoice_relative_expiry, the invoice must be rejected 7200 seconds after it was created.
BOLT12 offer vs BOLT11 invoice
A BOLT12 offer is a request for a payment request; a BOLT11 invoice is the payment request.
The BOLT12 text opens with eight complaints about its predecessor, and the ones a payer feels are these. A BOLT11 invoice is signed as a whole, so proving one field means revealing all of them. Its human readable amounts, in the spec's judgement, proved fraught: the p multiplier was often mishandled, and pico-bitcoin is harder to count in than satoshis. Its payment_secret only stopped probing while the invoice stayed private between payer and payee, which a published invoice is not. And, in the spec's own words, invoices must be given per user and are actively dangerous if two payment attempts are made for the same user. An offer answers all four by not being an invoice at all.
BOLT12 offer vs Lightning address
A BOLT12 offer is a self contained string; a Lightning address is a name that somebody's server resolves for you.
Both are reusable, which is why they get muddled. The address depends on a domain, a certificate and an operator who keeps the service running, and it stops working the day any of the three does. The offer depends on both nodes speaking onion messages and on nothing else, which is a smaller failure surface, though one you have to confirm your own wallet supports rather than assume. The address wins on being sayable out loud. A long lno1 string is a QR code, not something you read into a microphone.