Integrate Plannt.
Plannt is an HTTP-native access control layer built on the L402 protocol (bLIP-26) and Bitcoin Lightning. Every protected request returns a 402 challenge with a Lightning invoice. Payment becomes the credential. This page is enough to be in production by the end of an afternoon.
Overview
Plannt implements the L402 protocol (also known as LSAT) over Bitcoin Lightning. Verification is pure cryptography. The protocol is stateless. No database is required for access decisions.
An L402 server responds to unauthenticated requests with HTTP 402 Payment Required,
a BOLT11 Lightning invoice, and a macaroon (a bearer token scoped to the invoice's
payment hash). The client pays the invoice over Lightning, receives the payment
preimage as cryptographic proof of settlement, and retries the request with
Authorization: L402 macaroon:preimage. The server verifies the proof by
computing HMAC(root_key, payment_hash) == macaroon_id. If the equation
holds, the request proceeds.
The wire-protocol scheme name is historically LSAT and modernly
L402. The current Plannt server accepts both header prefixes. New
integrations should send L402.
Quick start with curl.
Three commands, three responses. End-to-end in the terminal you already have open.
$Step 1. Request the protected endpoint.
$ curl -i https://api.plannt.com/v1/demo
→Step 2. Receive the 402 challenge.
HTTP/1.1 402 Payment Required content-type: application/json { "error": "Payment Required", "invoice": "lnbc210n1p...", "authorization_format": "L402 <macaroon>:<preimage>" }
⚡Step 3. Pay the invoice. Any Lightning wallet works.
Alby, Phoenix, Mutiny, a self-custodied LND node, or any WebLN-compatible browser wallet. The payment returns a preimage, a 32-byte hex string. Keep it.
$Step 4. Retry with proof of payment.
$ curl -i https://api.plannt.com/v1/demo \ -H "Authorization: L402 <macaroon>:<preimage>" HTTP/1.1 200 OK { "result": "verified", ... }
The protocol in five steps.
The full L402 request lifecycle, what each side computes, and which artifact moves on the wire.
- Request. The agent sends a normal HTTP request to a protected endpoint with no authentication header.
- Challenge. The server generates a BOLT11 Lightning invoice via its LND node and a macaroon bound to that invoice's payment hash. It returns
402 Payment Requiredwith both artifacts in the response body. - Settle. The agent pays the invoice over Lightning. The wallet returns the payment preimage as proof of settlement. The preimage is the 32-byte hex value
rsuch thatSHA-256(r) == payment_hash. - Retry. The agent re-sends the original request with header
Authorization: L402 macaroon:preimage. - Verify. The server validates the preimage against the macaroon by recomputing
HMAC(root_key, payment_hash) == macaroon_id. On match, the request proceeds and the token is marked spent. Single-use, by construction.
A preimage cannot be forged. The 32-byte value is generated by the Lightning node when the invoice is paid, and the only way to obtain it is to settle the invoice. Possession of the preimage is cryptographic proof of payment, period.
Authorization header format.
The retry request must carry exactly one header:
Authorization: L402 <macaroon>:<preimage>
| Field | Encoding | Source |
|---|---|---|
macaroon |
base64, URL-safe | Returned by the server in the 402 challenge's authorization_format string. |
preimage |
64-char hex (32 bytes) | Returned by your Lightning wallet on successful payment of the invoice. |
Single-use. Spent on verification. Retransmitting the same token will return 401 Invalid token.
Endpoint reference.
Base URL: https://api.plannt.com. Four endpoints live today.
{ prompt: string }, returns GPT-4 inference response.
250sats
Pricing
Prices are in sats per request. Plannt does not negotiate, batch, or discount. Each invoice is independent. There is no subscription, no top-up balance, and no carry-over credit.
Client integration.
Any HTTP client that can read a 402 response can integrate with Plannt. Two canonical examples below. A Plannt CLI and Plannt MCP server ship May 2026 (see roadmap).
EXAMPLE / agent-node.js
// Plannt L402 Agent Example. Node.js. async function payLightningInvoice(invoice) { // Connect to your Lightning node and pay the invoice. // Returns the payment preimage as a hex string. throw new Error('Implement with your Lightning wallet'); } async function callPlanntAPI(endpoint, options = {}) { const url = `https://api.plannt.com${endpoint}`; const challenge = await fetch(url, options); if (challenge.status !== 402) return challenge.json(); const { invoice, authorization_format } = await challenge.json(); const macaroon = authorization_format.split(' ')[1].split(':')[0]; const preimage = await payLightningInvoice(invoice); return fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `L402 ${macaroon}:${preimage}` } }).then(r => r.json()); } callPlanntAPI('/v1/data').then(console.log).catch(console.error);
EXAMPLE / agent-python.py
# Plannt L402 Agent Example. Python. import requests def pay_lightning_invoice(invoice: str) -> str: raise NotImplementedError("Implement with your Lightning wallet") def call_plannt_api(endpoint: str, method: str = "GET", body: dict = None) -> dict: url = f"https://api.plannt.com{endpoint}" headers = {"Content-Type": "application/json"} if body else {} response = requests.request(method, url, headers=headers, json=body) if response.status_code != 402: return response.json() challenge = response.json() macaroon = challenge["authorization_format"].split(" ")[1].split(":")[0] preimage = pay_lightning_invoice(challenge["invoice"]) return requests.request(method, url, headers={**headers, "Authorization": f"L402 {macaroon}:{preimage}"}, json=body).json() if __name__ == "__main__": print(call_plannt_api("/v1/data"))
Compatible Lightning wallets.
Any wallet that pays BOLT11 invoices and exposes the resulting preimage to application code will integrate. The list below is what the Plannt team uses in production.
| Wallet | Type | Notes |
|---|---|---|
| Alby | browser extension + hosted API | Fastest path for agents running in a browser context. WebLN-compatible. |
| Phoenix | mobile, self-custodial | iOS / Android. Good for human-paid calls during prototyping. |
| LND | self-hosted node | Production deployments. Pair with ln-service on Node.js. |
| WebLN | browser provider API | Standard browser wallet interface. Detection: window.webln. |
Self-hosting Plannt.
Plannt is open source and designed to run on your own infrastructure with your own Lightning node. Settlement is direct from agent to your node. No platform sits in the middle.
Requirements
- Node.js 18 or newer
- LND node with funded channels and inbound liquidity
- Fly.io account (or any Node.js host)
Environment variables
| Variable | Description | Required |
|---|---|---|
LND_MACAROON |
Admin macaroon, base64-encoded | yes |
LND_CERT |
TLS certificate, base64-encoded | yes |
LND_SOCKET |
gRPC endpoint, host:port |
yes |
OPENAI_KEY |
OpenAI API key | /v1/generate only |
Convert credentials
$ base64 -i admin.macaroon | tr -d '\n' # → LND_MACAROON $ base64 -i tls.cert | tr -d '\n' # → LND_CERT
Deploy to Fly.io
$ git clone https://github.com/HashRails/plannt-api $ cd plannt-api && npm install $ fly secrets set \ LND_MACAROON="..." \ LND_CERT="..." \ LND_SOCKET="yournode.voltageapp.io:10009" \ --app your-app-name $ fly deploy
Verify deployment
$ curl -i https://your-app.fly.dev/v1/demo # Returns: HTTP 402 with a BOLT11 invoice. You are live.
Lightning node setup.
The fastest path to a node in production is Voltage.cloud. Self-managed LND on your own infrastructure works equally well for operators who want it.
Voltage.cloud (recommended)
- Create an account at voltage.cloud.
- Spin up a new LND node.
- Fund the node with Bitcoin and open channels.
- Export the admin macaroon, the TLS certificate, and the gRPC socket.
- Set them as
LND_MACAROON,LND_CERT, andLND_SOCKETenvironment variables.
Minimum liquidity
500,000 sats total. The node needs both inbound and outbound capacity. Inbound to receive Plannt payments. Outbound for normal Lightning routing health.
Credential conversion
$ base64 -i admin.macaroon | tr -d '\n' # → LND_MACAROON $ base64 -i tls.cert | tr -d '\n' # → LND_CERT # LND_SOCKET = yournode.voltageapp.io:10009
Sustained low inbound liquidity will cause the server to fail to generate
invoices and return 503 instead of 402. Monitor channel
balance and open new inbound channels before traffic outgrows capacity.
Security properties.
Five properties of the L402 design. Each is a consequence of how the protocol is constructed, not a feature bolted on top.
- Credentials are ephemeral. A token exists from the moment the invoice is paid until the moment it verifies. That window is typically under a second.
- Tokens are single-use. Verification marks the token spent. A second request with the same token returns
401 Invalid token. - Every request is pre-paid. Spend is capped at the cost of a single call. There is no billing cycle to run wild inside.
- Verification is stateless. The server holds a root key and recomputes
HMAC(root_key, payment_hash) == macaroon_idon demand. No database lookup. Horizontally scalable by construction. - No user data is stored. The server never learns who paid. It only learns that the payment hash settled. The agent is anonymous to the API operator unless it chooses otherwise.
Threat model
The protocol is not a guarantee against the API operator. A malicious operator can still serve bad data or refuse to release the response after verification. The L402 guarantees are about the credential layer:
- A leaked macaroon is useless without a paid preimage.
- A leaked preimage cannot be replayed once spent.
- A leaked root key is catastrophic. Rotate root keys with the same discipline you rotate any HMAC secret.