Back to Learn

Lightning Network Explained: Channels, Routing, Invoices

How Lightning payments work for developers: channels, HTLCs, source routing, fees, BOLT 11 invoices and the ways payments fail, then the same steps in code.

Updated October 23, 2026View as Markdown
  • Lightning network
  • Bitcoin
  • Payments
  • Invoices
  • Routing

The Lightning Network is a network of payment channels on top of Bitcoin. Two nodes lock bitcoin into a shared on-chain output once, then pay each other as often as they like by exchanging signed updates that never reach the chain. A payment between two parties without a channel of their own crosses several channels, locked by a hash so that nobody in the middle can keep it.

This page covers what you run into when you build on it: channels and liquidity, HTLCs, source routing, fees, BOLT 11 invoices, and the ways a payment fails. The last section does the same steps through ln.bot, which runs the nodes and channels for you.

Why payments move off-chain

Bitcoin adjusts its difficulty so that 2,016 blocks take two weeks, one block every ten minutes on average, and each block has room for a limited number of transactions. Transactions pay fees by their size in bytes, at a rate set by demand for that space. The amount moved doesn't enter into it: sending ₿500 costs as much as sending a house, and in a busy hour the fee can be larger than the payment.

Lightning keeps the chain for final settlement and moves the payments themselves off it. Funds go on-chain once when a channel opens and once when it closes. Everything in between is messages between nodes.

Channels

A channel starts with a funding transaction: bitcoin locked into a 2-of-2 multisignature output, which can only be spent with both parties' signatures. It's the only on-chain step needed to start.

From then on the two nodes keep a running balance by signing new versions of a commitment transaction, one that would pay each side its share if it were broadcast. If Alice and Bob start with ₿500,000 each and Alice pays Bob ₿10,000, they both sign a new state: Alice ₿490,000, Bob ₿510,000. An update is a few messages between two nodes and costs nothing on-chain.

Old states must never be used again, so each update also hands over a revocation secret for the previous one. If Bob later broadcasts a state in which he had more, Alice can use that secret to take the whole channel balance. Closing works two ways: cooperatively, with one transaction both sign, or unilaterally, in which case the side that broadcasts must wait out a delay before spending, which gives the other side time to punish a cheat.

Two consequences matter when you build on top:

  • Capacity is fixed, and payments only move balance from one side to the other. Alice can send at most what is on her side (outbound liquidity) and receive at most what is on Bob's side (inbound liquidity). A channel you funded yourself starts with everything on your side, so it can't receive anything until you have spent some.
  • The chain sees almost nothing: thousands of payments between opening and closing leave two transactions on it.

HTLCs: paying through someone else's channel

If Alice has a channel with Bob and Bob has one with Carol, Alice can pay Carol through Bob without trusting him. The tool is the hash time-locked contract (HTLC).

Carol picks a random 32-byte secret, the preimage, and puts its SHA-256 hash in the invoice she gives Alice:

import { createHash, randomBytes } from "node:crypto";

// The recipient's node, creating an invoice
const preimage = randomBytes(32);
const paymentHash = createHash("sha256").update(preimage).digest("hex");
// paymentHash goes into the invoice;
// the preimage stays secret until the money arrives

// Anyone holding the invoice can check a receipt
function provesPayment(preimageHex: string, paymentHashHex: string): boolean {
  const hash = createHash("sha256").update(Buffer.from(preimageHex, "hex"));
  return hash.digest("hex") === paymentHashHex;
}

console.log(provesPayment(preimage.toString("hex"), paymentHash)); // true
typescript

Alice then offers Bob an HTLC in their channel: Bob gets the amount plus his fee if he shows the preimage before a deadline, and after the deadline the money returns to Alice. Bob offers Carol the same deal, minus his fee, with an earlier deadline. Carol knows the preimage, so she claims from Bob by revealing it; now Bob knows it too and claims from Alice.

The deadlines shrink hop by hop (each channel sets its cltv_expiry_delta, counted in blocks), so a node that has paid downstream always has time left to claim upstream. Nobody in the middle can take the money without the preimage, and if it never appears, every HTLC times out and refunds. The payment completes along the whole route or not at all. The sender ends up holding the preimage, which proves the payment was made: ln.bot returns it on every settled payment as payment.preimage.

Source routing

On the internet, each router picks the next hop. On Lightning, the sender picks the whole route. Nodes gossip channel announcements and fee policies to each other (BOLT 7), so every node keeps a map of the public channel graph: about 16,000 nodes and 32,500 channels on 30 August 2026, by mempool.space's latest count. The sender's node finds a path on that map, then wraps the instructions for each hop in layers of encryption, an onion (BOLT 4). Each hop peels one layer and learns where the payment came from and where to send it next. It can't tell how long the route is or where it sits in it.

The map shows each channel's capacity but not how that capacity is split between the two sides; only the two nodes know. So the sender is guessing. A route can look fine and still fail at a hop whose outbound side is short, which sends back an encrypted error (usually temporary_channel_failure), and the sender tries another route. Wallets also split a payment into parts that take different routes (multi-part payments). The recipient holds the parts until all of them have arrived, then releases the preimage for all of them at once.

Fees

Every channel publishes a fee policy in its gossip: a base fee in millisatoshis plus a proportional fee in parts per million (BOLT 7's fee_base_msat and fee_proportional_millionths). A hop charges on the amount it forwards. The sender pays every hop's fee on top of the amount, and the recipient receives exactly the amount. Each node sets its own policy, so the price depends on the route. On 30 August 2026 the median published policy was 500 msat (₿0.5) plus 100 ppm (mempool.space).

// A hop's fee for forwarding amountMsat (BOLT 7): base plus proportional
function hopFeeMsat(amountMsat: number, baseMsat: number, ppm: number) {
  return baseMsat + Math.floor((amountMsat * ppm) / 1_000_000);
}

// ₿10,000 through two hops that both charge the median policy
const amount = 10_000 * 1000;                     // 10,000,000 msat
const fee2 = hopFeeMsat(amount, 500, 100);        // last hop: 1,500 msat
const fee1 = hopFeeMsat(amount + fee2, 500, 100); // forwards fee2 too: 1,500
console.log(fee1 + fee2);                         // 3,000 msat: ₿3 in fees
typescript

At that policy, ₿100 costs about ₿1 to route over two hops (the base fee dominates) and ₿1,000,000 about ₿201 (the proportional fee does). The sender's node looks for cheap routes, so a payment can cost less than the median suggests, and some channels charge no base fee at all.

The sender sets the ceiling. On ln.bot that's maxFee on payments.create(), counted in ₿ like every amount in the API. It caps the network fee, routing plus a 0.25% network charge, and defaults to 1% of the amount, at least ₿20, so it rarely needs setting; payments.resolve() quotes the fee before you pay, and the settled payment reports what it came to as actualFee. ln.bot adds a service fee to payments that leave ln.bot, 0.25% with a minimum of ₿1, reported as serviceFee. Transfers between two ln.bot wallets carry neither fee.

Invoices: BOLT 11

A BOLT 11 invoice is the recipient's signed payment request, encoded as a bech32 string. The fields worth knowing:

  • The prefix and amount: lnbc means mainnet, and the amount follows with a multiplier, so lnbc10u is 10 micro-bitcoin, ₿1,000. An invoice may leave the amount out and let the payer choose.
  • p, the payment hash from the HTLC section, and s, a payment secret that stops nodes on the route from probing the recipient.
  • d, a description, or h, the hash of a longer one.
  • x, the expiry in seconds after the invoice's timestamp: 3,600 when absent. After it, the payer should not pay and the recipient should not accept.
  • r, route hints for recipients reachable only through private channels.
  • The recipient node's signature, from which the payer recovers the node's public key.
import { LnBot } from "@lnbot/sdk";

const ln = new LnBot({ apiKey: "uk_..." });
const w = ln.wallet("wal_...");

const invoice = await w.invoices.create({ amount: 1000, memo: "Report #42" });
console.log(invoice.bolt11);    // "lnbc10u1p...": 10u is 10 micro-bitcoin, ₿1,000
console.log(invoice.expiresAt); // an hour after creation
console.log(invoice.status);    // "pending", then "settled" or "expired"
typescript

An invoice is for one payment. After it has been paid, the recipient's node may treat a second payment to the same hash as unknown or may accept it (BOLT 4), so paying an invoice twice can cost you twice. For something people can pay again and again, there are Lightning addresses, which mint a fresh invoice for every payment, and BOLT 12 offers: see Lightning address.

How payments fail

A Lightning payment either completes or is unwound, but there are several ways to end up in the second case, and one in which you wait:

  • No route: every path the sender tried came up short somewhere. Large amounts fail more often, since every hop needs the whole amount on its outbound side, and so do recipients with little inbound liquidity. The sender's node retries other routes and splits the payment before it gives up. On ln.bot a payment with no route yet stays pending and is retried until its invoice expires, for 24 hours at most.
  • Fee limit: a fee quote above maxFee, or above the default cap, refuses the payment before it starts, and a route that turns out dearer fails instead of paying more.
  • Expired invoice: the payer refuses it, and the recipient has to issue a new one.
  • Rejected at the end: the recipient's node refuses an HTLC for an unknown payment hash, the wrong amount or a missing payment secret (incorrect_or_unknown_payment_details).
  • Stuck in flight: now and then a hop holds an HTLC without settling or failing it. The payment stays in flight until the HTLC times out, a number of blocks that can add up to hours. Until then the amount is reserved: on ln.bot it shows in the wallet's onHold balance, and the payment's status stays pending or processing.

The rule that follows: never retry by sending a second payment while the first might still settle. Make the request idempotent and wait for a final status.

import { LnBot } from "@lnbot/sdk";

const ln = new LnBot({ apiKey: "uk_..." });
const w = ln.wallet("wal_...");

let payment = await w.payments.create({
  target: "lnbc10u1p...",       // an invoice carries its own amount
  idempotencyKey: "order-1042", // a retry with this key returns this payment
});                             // no maxFee: the default caps the network fee at 1%

while (payment.status === "pending" || payment.status === "processing") {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  payment = await w.payments.get(payment.number);
}

if (payment.status === "failed") {
  console.error(payment.failureReason); // the amount on hold is released
} else {
  console.log(payment.actualFee, payment.preimage); // fee paid, proof of payment
}
typescript

What ln.bot runs for you

ln.bot operates the Lightning nodes, manages the channel liquidity and handles routing. A wallet is a balance on those nodes, so ln.bot holds the bitcoin: treat a wallet as an operational balance for near-term spending, not as savings. A payment between two ln.bot wallets never leaves ln.bot's nodes, so it settles in under a second and costs nothing. Receiving from any Lightning wallet is free; paying out costs 0.25%, minimum ₿1, plus the network fee: another 0.25% and the routing fee.

An account, a wallet, an invoice and a payment in TypeScript:

import { LnBot } from "@lnbot/sdk";

// 1. An account. No auth. Store all three secrets now: they are not shown again.
const account = await new LnBot().register();
// account.primaryKey, account.secondaryKey: "uk_..." user keys
// account.recoveryPassphrase: 12 words

// 2. A wallet, with its own Lightning address
const ln = new LnBot({ apiKey: account.primaryKey });
const { walletId, address } = await ln.wallets.create();
console.log(address); // e.g. "x4k7m2@ln.bot"
const w = ln.wallet(walletId);

// 3. An invoice for ₿1,000 that any Lightning wallet can pay
const invoice = await w.invoices.create({ amount: 1000, memo: "first invoice" });
console.log(invoice.bolt11);

// 4. Pay an invoice (a new wallet holds ₿0, so fund it first)
const payment = await w.payments.create({ target: "lnbc5u1p..." });
console.log(payment.status);
typescript

The same from a terminal:

curl -fsSL https://ln.bot/install.sh | bash
lnbot init                                                # account, wallet, Lightning address
lnbot invoice create --amount 1000 --memo "first invoice" # waits for the payment unless --no-wait
lnbot pay lnbc5u1p...                                     # asks y/N first unless --yes
bash

The SDKs cover TypeScript, Python, Go, Rust and C#, and every wallet also has an MCP server that an agent can use directly. To charge for an API per request over Lightning, see L402; to give each user or agent on a platform its own wallet, see wallet as a service.

FAQ

How fast is a Lightning payment?

A payment routed across the network usually settles in a few seconds, and takes longer when the first routes fail and the sender tries others. Between two wallets on the same provider, such as two ln.bot wallets, it settles in under a second. Once settled it is final: there are no confirmations to wait for.

What does a Lightning payment cost?

Each channel on the route charges a base fee plus a proportional fee, set by its operator. On 30 August 2026 the median published policy was ₿0.5 plus 100 parts per million (mempool.space), so a ₿10,000 payment over two such hops pays about ₿3 in routing. ln.bot adds 0.25% (minimum ₿1) to payments that leave ln.bot; transfers between ln.bot wallets are free.

Do I need to run a Lightning node?

Not to use Lightning. Custodial wallets and APIs such as ln.bot run the nodes, channels and routing and hold the balance for you. Running your own node means managing channels, inbound liquidity and backups yourself.

What happens when a Lightning payment fails?

The HTLCs along the route are cancelled and the amount goes back to the sender; nobody in the middle can keep it. A payment can also stay in flight until a stuck HTLC times out, so wait for a final status before retrying, and use an idempotency key so a retry can't pay twice.

How long is a Lightning invoice valid?

A BOLT 11 invoice carries its own expiry, and when none is set it is one hour. ln.bot invoices last 60 minutes. An invoice is meant to be paid once; for an address that can be paid again and again, use a Lightning address.