Skip to content

Developer Guide

Sending (USD Line of Credit)

This guide explains how to send Lightning Network and on-chain Bitcoin payments from a USD-denominated wallet using the Voltage Payments API. USD wallets leverage a line of credit to convert USD to BTC at the time of payment.

  • A Voltage account with an active USD wallet (credit-backed wallet with an associated USD line of credit)
  • An API key with access to the environment (from the “API Keys” page in your dashboard)
  • The Quotes API enabled, and a valid line_of_credit_id for your USD wallet’s line of credit
  • The target Lightning invoice or Bitcoin address you intend to pay

Sending from a USD wallet involves three steps:

  1. Determine the BTC amount – If paying a Lightning invoice, decode it to extract the payment amount in millisatoshis. For on-chain payments where you already know the amount, skip to Step 2.
  2. Obtain a conversion quote – Use the POST /quotes endpoint to get a quote for converting USD to BTC. This locks in an exchange rate and returns a quote_id.
  3. Create the payment – Call the POST /payments endpoint with your USD wallet, including the quote_id and payment details.

Step 1: Determine the BTC Amount from a Lightning Invoice

Section titled “Step 1: Determine the BTC Amount from a Lightning Invoice”

To create a quote, you need the BTC amount in millisatoshis (msats). When paying a Lightning invoice, the amount is encoded in the invoice string itself and must be extracted before you can request a quote.

Using light-bolt11-decoder:

Terminal window
npm install light-bolt11-decoder
import { decode } from 'light-bolt11-decoder';
const invoice = 'lnbc1500n1p...';
const decoded = decode(invoice);
const amountSection = decoded.sections.find(s => s.name === 'amount');
const amountMsats = amountSection ? Number(amountSection.value) : null;
if (!amountMsats) {
throw new Error('Invoice does not contain an amount');
}
console.log(`Amount: ${amountMsats} msats`);
// Use amountMsats as the "amount" in your quote request (Step 2)

Using bolt11:

Terminal window
pip install bolt11
import bolt11
invoice = "lnbc1500n1p..."
decoded = bolt11.decode(invoice)
amount_msats = decoded.amount_msat
if not amount_msats:
raise ValueError("Invoice does not contain an amount")
print(f"Amount: {amount_msats} msats")
# Use amount_msats as the "amount" in your quote request (Step 2)

Step 2: Get a Quote for USD→BTC Conversion

Section titled “Step 2: Get a Quote for USD→BTC Conversion”

Before creating a payment from a USD wallet, you must get a quote to convert the payment amount into BTC. Use the amount you decoded in Step 1 (or the amount you already know for on-chain payments).

POST https://voltageapi.com/v1/organizations/{organization_id}/environments/{environment_id}/quotes
x-api-key: your-api-key
Content-Type: application/json
Terminal window
curl 'https://voltageapi.com/v1/organizations/{organization_id}/environments/{environment_id}/quotes' \
--request POST \
--header 'x-api-key: your-api-key' \
--header 'Content-Type: application/json' \
--data '{
"id": "87654321-4321-8765-cba9-fed987654321",
"line_of_credit_id": "{line_of_credit_id}",
"network": "mainnet",
"amount": {
"amount": 150000,
"currency": "btc",
"unit": "msats"
},
"to": "usd"
}'
  • id – A client-generated UUID for the quote (for idempotency)
  • line_of_credit_id – Your USD line of credit ID
  • network – The Bitcoin network (“mainnet”, “testnet”, “signet”, or “mutinynet”)
  • amount – The BTC amount you need to send (in msats). When paying a Lightning invoice, this is the amount decoded in Step 1.
  • to – Target currency for conversion (“usd”)

The API will respond with a quote object containing an exchange rate and a quote_id. Use that quote_id in Step 3.

Step 3: Create a Payment from the USD Wallet

Section titled “Step 3: Create a Payment from the USD Wallet”

With a valid quote in hand, create the payment using the POST /payments endpoint.

POST https://voltageapi.com/v1/organizations/{organization_id}/environments/{environment_id}/payments
x-api-key: your-api-key
Content-Type: application/json

To pay a Lightning invoice from a USD wallet, provide the invoice in the payment_request field.

Terminal window
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": "{usd_wallet_id}",
"currency": "usd",
"quote_id": "12345678-1234-5678-9abc-def012345678",
"type": "bolt11",
"data": {
"payment_request": "lnbc1500n1p...",
"amount": {
"currency": "btc",
"amount": 150000,
"unit": "msats"
},
"max_fee": {
"currency": "btc",
"amount": 1000,
"unit": "msats"
}
}
}'
  • id – A client-generated UUID for the payment (for idempotency)
  • wallet_id – The UUID of your USD wallet
  • currency – “usd” (the wallet’s currency)
  • quote_id – The UUID of the quote obtained in Step 2 (required for USD wallets)
  • type – “bolt11” for a Lightning payment
  • data.payment_request – The BOLT-11 Lightning invoice string (cannot be empty)
  • data.amount – Optional BTC amount object. Required if the invoice has no amount encoded; must match if the invoice has a fixed amount
  • data.max_fee – Optional maximum routing fee in BTC (defaults to 1% of payment or 1,000 msats, whichever is greater)

To send BTC on-chain from a USD wallet, provide the Bitcoin address and amount.

Terminal window
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": "{usd_wallet_id}",
"currency": "usd",
"quote_id": "12345678-1234-5678-9abc-def012345678",
"type": "onchain",
"data": {
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"amount": {
"currency": "btc",
"amount": 15000000,
"unit": "msats"
},
"max_fee": {
"currency": "btc",
"amount": 1000000,
"unit": "msats"
},
"description": "Payment for services"
}
}'
  • type – “onchain” for an on-chain BTC payment
  • data.address – The recipient’s Bitcoin address
  • data.amount – The amount to send in BTC (msats). Example: 15,000,000 msats = 15,000 sats
  • data.max_fee – Optional maximum miner fee in msats
  • data.description – Optional memo for your records

A BIP21 URI can include both an on-chain address and an embedded Lightning invoice. The API will parse the URI and execute the payment via Lightning or on-chain as appropriate.

Terminal window
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": "3e84b6c5-5bbe-4e0f-9fb3-f1198330f6fa",
"wallet_id": "{usd_wallet_id}",
"currency": "usd",
"quote_id": "12345678-1234-5678-9abc-def012345678",
"type": "bip21",
"data": {
"address": "bitcoin:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh?label=Example%20Store&message=Payment%20for%20services&lightning=lnbc1500...qpz",
"payment_request": "lnbc1500n1p...",
"amount": {
"currency": "btc",
"amount": 150000,
"unit": "msats"
},
"max_fee": {
"currency": "btc",
"amount": 1000,
"unit": "msats"
},
"description": "Payment for services"
}
}'
  • type – “bip21” for a unified Bitcoin payment
  • data.address – The BIP21 URI string (required)
  • data.payment_request – Optional Lightning invoice if you have it separately
  • data.amount – Optional BTC amount object (can be omitted if URI contains amount)
  • data.max_fee – Optional max Lightning fee in msats
  • data.description – Optional memo (independent of any invoice memo)

After initiating a payment, monitor its status using:

GET https://voltageapi.com/v1/organizations/{organization_id}/environments/{environment_id}/payments/{payment_id}
Terminal window
curl 'https://voltageapi.com/v1/organizations/{organization_id}/environments/{environment_id}/payments/{payment_id}' \
--header 'x-api-key: your-api-key'
  • sending – Payment is in progress
  • completed – Payment was successful and funds have been delivered
  • failed – Payment failed (check the error field for details)

For completed USD payments, the response includes:

  • data.market_quote – The exchange rate used for USD/BTC conversion
  • data.amount – The amount in USD that was debited from your wallet
  • 200 – Success
  • 400 – Invalid request (missing fields, expired/invalid quote, etc.)
  • 403 – Authentication or permissions error
  • 404 – Resource not found
  • 500 – Server error
  • Missing quote_id – USD wallet payments require a valid quote_id
  • Expired quote – Quotes are time-limited; obtain a new one
  • Invalid invoice – The Lightning invoice is malformed or expired
  • Insufficient balance – Your USD wallet doesn’t have enough funds

When a payment fails, the status will be “failed” and the error object will contain details about the failure reason.