Testnet · LND / lncli + curl
Pay once. Retrieve data twice.
This guide is for developers with an existing LND testnet node. It describes the path from HTTP 402 to the JSON response for BTC RSI(14). Price: 25 testnet sats plus any Lightning routing fees.
Validation status: The underlying flow was successfully tested with LND/lncli in a Docker container: one payment, two authorised requests. This step-by-step version has been checked for syntax but has not yet been run end to end on an independent tester’s installation. The exact client version used in the original test was not recorded.
Requirements
- Linux with Bash, curl and Python 3.
- LND with an unlocked wallet, completed synchronisation and working lncli access to your own node.
- Bitcoin testnet as used in the tested command with
--network=testnet; mainnet, signet and regtest are not this test flow. An invoice prefix alone does not establish a reachable payment route. - Test satoshis as outbound Lightning channel liquidity, and a route to the recipient. An on-chain balance alone is not enough.
Setting up a node, obtaining test satoshis and opening channels are not covered here. If you do not have this setup, contact us before the payment step.
1. Prepare your terminal
Run the blocks in order in the same terminal. The first command opens a separate Bash session. Your LND connection, including certificate and macaroon, must already be configured; add your usual local connection options to the function if needed.
bash
set +x
umask 077
pimp_dir="$(mktemp -d)"
endpoint='https://pimp.andyzyklisch.de/v1/btc/rsi14'
# Local lncli with an existing connection:
pimp_lncli() { lncli --network=testnet "$@"; }
pimp_lncli getinfoIf your lncli runs in Docker, replace the function with this version. litd-buyer is the name of our test container, not a required name for your node.
# Alternative: adapt the container name to your installation.
pimp_lncli() { docker exec -i litd-buyer lncli --network=testnet "$@"; }
pimp_lncli getinfoContinue only if getinfo reports the expected testnet node and synced_to_chain: true . If any step fails, stop and identify the cause. Keep the token and preimage private; do not publish terminal output containing them.
2. Retrieve the payment challenge
This step costs nothing. Expect HTTP 402 and the message that the token and invoice have been saved locally. Do not continue without both.
curl --silent --show-error --connect-timeout 10 --max-time 25 -D "$pimp_dir/headers" -o "$pimp_dir/challenge-body" -w 'HTTP %{http_code}\n' "$endpoint"
python3 - "$pimp_dir" <<'PYTHON'
import pathlib, re, sys
p = pathlib.Path(sys.argv[1])
h = (p / 'headers').read_text()
m = re.search(r'(?im)^www-authenticate:\s*L402\s+macaroon="([^"]+)",\s*invoice="([^"]+)"', h)
if not m:
raise SystemExit('STOP: no matching L402 payment challenge')
if not m[2].startswith('lntb'):
raise SystemExit('STOP: not the expected testnet invoice')
(p / 'macaroon').write_text(m[1])
(p / 'invoice').write_text(m[2])
print('Token and invoice saved locally.')
PYTHON3. Check the invoice
pimp_lncli decodepayreq "$(cat "$pimp_dir/invoice")"Check the output: num_satoshis must be 25 (equivalent to 25,000 millisatoshis). Also check timestamp plus expiry: the invoice must not have expired. Do not pay if the amount or network differs. An expired invoice that has not yet been paid can be replaced by repeating step 2.
4. Pay once
Only this command makes a testnet payment after your confirmation. Check the displayed amount and your client’s fee conditions. Our tested flow used automatic confirmation; here you explicitly confirm it yourself.
pimp_lncli payinvoice "$(cat "$pimp_dir/invoice")"Wait for SUCCEEDED. Privately copy the corresponding preimage for the next step. If the request times out, the connection drops or the status is unclear, do not pay again or request a new invoice: first check the original payment’s status in your LND client. Invoice expiry and the validity of API access are different things.
5. Assemble your access credentials locally
Paste the preimage from that same successful payment into the hidden input. It belongs to the token from step 2. If a STOP message appears, repeat this step with the correct preimage.
read -r -s -p 'Preimage of the successful payment: ' pimp_preimage
printf '\n'
if [[ "$pimp_preimage" =~ ^[0-9a-fA-F]{64}$ ]]; then
printf 'Authorization: L402 %s:%s\n' "$(cat "$pimp_dir/macaroon")" "$pimp_preimage" > "$pimp_dir/auth"
else
printf 'STOP: Preimage must contain 64 hexadecimal characters.\n'
fi
unset pimp_preimage6. Retrieve JSON and reuse your access
curl --silent --show-error --connect-timeout 10 --max-time 25 --header "@$pimp_dir/auth" -o "$pimp_dir/response.json" -w 'HTTP %{http_code}\n' "$endpoint"
python3 -m json.tool "$pimp_dir/response.json"Expected: HTTP 200 and JSON with symbol: BTC, timeframe: 1h, rsi_period: 14 and a numeric rsi. Also check the timestamp and snapshot age; the result is not a trading signal.
Run only this retrieval block a second time. It uses the same local credentials and does not make a Lightning payment. If successful, you receive HTTP 200 again. This tests reuse at the same endpoint; it does not establish long-term validity or access to other endpoints.
If something goes wrong
- No payment route: Check connectivity, outbound channel liquidity and the route. If payment status is unclear, check the original payment first.
- HTTP 402 after a successful payment: The token and preimage must come from the same payment challenge. Check that a valid Authorization header was saved. Do not automatically pay again.
- HTTP 401, 403 or 5xx: Note the status and time and get in touch; these do not establish a need for another payment.
- Browser error: This guide uses curl. A failed browser request alone does not establish an API outage.
- No JSON or stale data: Check the HTTP status, candle timestamp and snapshot age; report anything unexpected.
For feedback, the client version, step, HTTP status and time are sufficient. Please do not share access tokens, preimages, private keys or complete authentication headers. Join the Stacker News discussion ↗
7. Clean up after the test
This block removes the temporary files, including your paid access credentials. If you want to continue using that access, first keep the auth file securely and only locally. Without the token and preimage, you cannot reuse this access. Finally, exit ends the Bash session opened in step 1.
rm -f -- "$pimp_dir/headers" "$pimp_dir/challenge-body" "$pimp_dir/macaroon" "$pimp_dir/invoice" "$pimp_dir/auth" "$pimp_dir/response.json"
rmdir -- "$pimp_dir"
unset pimp_dir endpoint
unset -f pimp_lncli
exit