Developer Guide
Migration Guide
Migrating to Node Backed Payments
20 min
keep your voltage lnd node route payments through the voltage payments api instead of calling lnd directly prerequisites an existing voltage lnd node (running and synced) an enterprise plan (node backed setup requires enterprise) you've read docid 7szaymjg8lkjcmiwjkub (auth changes, concept mapping, status mapping, amount handling) migration steps set up payments environment and wallet credentials handoff (support macaroon) set up webhooks migrate sending migrate receiving implement reconciliation loop parallel run cut over step 1 set up payments environment and wallet follow the docid\ smmi0 ukgfgdchtsztlu5 guide to create a payments environment in the voltage dashboard create a wallet linked to your existing node (check "i want to use a team infrastructure node" and select your node) generate an api key for the environment after this step you'll have three values you need for every api call organization id environment id wallet id and one credential your x api key step 2 credentials handoff voltage needs limited access to your node to manage liquidity on your behalf go to your voltage lightning node dashboard → manage access → macaroon bakery click bake support macaroon this creates a custom permission token that voltage uses for white glove liquidity management this macaroon gives voltage the ability to open/close channels and manage liquidity — you retain full admin access and control for details on macaroon types and security, see the docid\ rlda4beej5rlbld3jm2ql security guide step 3 set up webhooks replace your grpc stream listeners with webhooks 1\ create a publicly accessible https endpoint on your server to receive webhook post requests 2\ register the webhook curl "https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/webhooks" \\ \ request post \\ \ header "content type application/json" \\ \ header "x api key your api key" \\ \ data '{ "id" "b0fc9829 f139 4035 bb14 4a4b6cd58f0e", "organization id" "{organization id}", "environment id" "{environment id}", "url" "https //your domain com/webhook", "name" "migration webhook", "events" \[ { "send" "succeeded" }, { "send" "failed" }, { "receive" "generated" }, { "receive" "completed" }, { "receive" "expired" }, { "receive" "failed" } ] }' 3\ save the shared secret from the response — it's only returned once 4\ implement signature verification in your webhook handler const crypto = require("crypto"); function verifywebhooksignature(payload, signature, timestamp, sharedsecret) { const message = `${payload} ${timestamp}`; const hmac = crypto createhmac("sha256", sharedsecret); hmac update(message); const expectedsignature = hmac digest("base64"); return crypto timingsafeequal( buffer from(expectedsignature), buffer from(signature) ); } read x voltage signature , x voltage timestamp , and x voltage event from the request headers 5\ test the webhook curl "https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/webhooks/{webhook id}/test" \\ \ request post \\ \ header "content type application/json" \\ \ header "x api key your api key" \\ \ data '{ "delivery id" "123e4567 e89b 12d3 a456 426614174002", "payload" { "type" "test", "detail" { "event" "created", "data" "test" } } }' for full webhook documentation, see the guide step 4 migrate sending lightning payment before (lnd rest) curl cacert tls cert \\ \ header "grpc metadata macaroon $(xxd ps u c 1000 admin macaroon)" \\ https //your node voltage cloud 8080/v2/router/send \\ \ request post \\ \ data '{ "payment request" "lntbs1500n1p ", "timeout seconds" 60, "fee limit msat" 1000, "no inflight updates" true }' after (payments api) curl 'https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/payments' \\ \ request post \\ \ header 'x api key your api key' \\ \ header 'content type application/json' \\ \ data '{ "id" "68d00852 8dd8 4c71 94d2 91c84695da78", "wallet id" "{wallet id}", "currency" "btc", "type" "bolt11", "data" { "payment request" "lntbs1500n1p ", "amount" { "currency" "btc", "amount" 150000, "unit" "msats" }, "max fee" { "currency" "btc", "amount" 1000, "unit" "msats" } } }' key differences you generate the id (uuid) — this is your idempotency key amount uses the amount object (msats), not a bare integer max fee replaces fee limit msat — same concept, structured format no timeout seconds — the api manages routing timeouts response is 202; monitor status via get /payments/{payment id} or webhooks on chain payment before (lnd rest) curl cacert tls cert \\ \ header "grpc metadata macaroon $(xxd ps u c 1000 admin macaroon)" \\ https //your node voltage cloud 8080/v1/transactions \\ \ request post \\ \ data '{ "addr" "tb1pzkhtj4ld86g9c49du5yagnncfrm0s489t76vmrwmt2ecxfnf7spsvjte49", "amount" 15000, "sat per vbyte" 10 }' after (payments api) curl 'https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/payments' \\ \ request post \\ \ header 'x api key your api key' \\ \ header 'content type application/json' \\ \ data '{ "id" "2ec1e783 19b4 4c10 8181 66336a6232bd", "wallet id" "{wallet id}", "currency" "btc", "type" "onchain", "data" { "address" "tb1pzkhtj4ld86g9c49du5yagnncfrm0s489t76vmrwmt2ecxfnf7spsvjte49", "amount" { "currency" "btc", "amount" 15000000, "unit" "msats" }, "max fee" { "currency" "btc", "amount" 1000000, "unit" "msats" }, "description" "test payment" } }' key differences addr → data address amount in sats → data amount in msats (15,000 sats = 15,000,000 msats) sat per vbyte → data max fee (max fee budget, not fee rate) step 5 migrate receiving lightning invoice before (lnd rest) curl cacert tls cert \\ \ header "grpc metadata macaroon $(xxd ps u c 1000 admin macaroon)" \\ https //your node voltage cloud 8080/v1/invoices \\ \ request post \\ \ data '{ "value msat" 150000, "memo" "test payment", "expiry" 3600 }' after (payments api) — step 1 create the receive payment curl 'https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/payments' \\ \ request post \\ \ header 'x api key your api key' \\ \ header 'content type application/json' \\ \ data '{ "id" "11ca843c bdaa 44b6 965a 39ac550fcef7", "wallet id" "{wallet id}", "currency" "btc", "payment kind" "bolt11", "amount" { "amount" 150000, "currency" "btc", "unit" "msats" }, "description" "test payment", "expiration" 3600 }' after — step 2 fetch the payment to get the invoice string curl 'https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/payments/11ca843c bdaa 44b6 965a 39ac550fcef7' \\ \ header 'x api key your api key' the data payment request field in the response contains the bolt11 invoice string to give to the payer key differences receiving is a two step flow create (202) → fetch (200 with invoice) send uses type ; receive uses payment kind — same values ( bolt11 , onchain , bip21 ) value msat → amount object memo → description in the request (returned as memo in the response) lnd returns the invoice immediately; payments api returns 202 and you fetch separately on chain receive before (lnd rest) curl cacert tls cert \\ \ header "grpc metadata macaroon $(xxd ps u c 1000 admin macaroon)" \\ https //your node voltage cloud 8080/v1/newaddress?type=taproot pubkey after (payments api) curl 'https //voltageapi com/v1/organizations/{organization id}/environments/{environment id}/payments' \\ \ request post \\ \ header 'x api key your api key' \\ \ header 'content type application/json' \\ \ data '{ "id" "11ca843c bdaa 44b6 965a 39ac550fcef8", "wallet id" "{wallet id}", "currency" "btc", "payment kind" "onchain", "amount" { "amount" 150000000, "currency" "btc", "unit" "msats" }, "description" "on chain deposit" }' fetch the payment to get data address the api tracks confirmations via data receipts step 6 implement reconciliation loop set up a cron job (every 1–5 minutes) as a safety net alongside your webhook handler the full implementation is documented in the guide under "reconciliation loop " in short store a last reconciled at timestamp query get /payments?start date={last reconciled at 10 min}\&end date={now}\&sort key=updated at\&sort order=asc page through all results upsert each payment into your database keyed on payment id advance last reconciled at the overlap window and idempotent upserts make this safe to run as frequently as you want step 7 parallel run run both your existing lnd integration and the new payments api integration side by side verify that the new path works correctly before cutting over verification checklist send a lightning payment via the payments api → status reaches completed receive a lightning payment via the payments api → invoice generated, payment completed send an on chain payment via the payments api → status reaches completed receive an on chain payment via the payments api → address generated, payment completed webhooks fire for all send and receive events reconciliation loop catches all payments (cross reference with webhook delivered payments) wallet balance in payments api matches expected values step 8 cut over once your parallel run is verified disable your old lnd integration — stop calling lnd rest/grpc endpoints for payments remove old code — tls cert loading, macaroon injection, grpc client setup, stream reconnection logic (see "what you can remove" in the ) keep node management access — you still need thunderhub and/or lncli for channel opens and closes liquidity and treasury management node unlocking after restarts or updates seed phrase and scb backup access what you still manage even after migrating to the payments api, the following remain your responsibility on a node backed setup node uptime — your node must stay running and synced channels — open, close, and rebalance as needed (voltage assists via the support macaroon) liquidity / treasury — keep capacity above minimum thresholds per your treasury management strategy with voltage seed phrase + scb backups — maintain secure offline copies on chain deposits — fund your node when voltage notifies you that capacity is below the minimum node unlocking — unlock with your password after every restart or update