Skip to content

Developer Guide

Swaps

Swaps let a node-backed Voltage wallet send bitcoin on one rail using funds from the other. Your application creates a normal payment and sets flow_preference to swap; Voltage coordinates the Lightning payment and hashlocked on-chain transaction.

See swaps in action with the Voltage Swap demo. The demo runs on Mutinynet and shows both on-chain to Lightning and Lightning to on-chain swaps.

Open the Voltage Swap demo

You can use swaps to:

  • Pay an on-chain Bitcoin address using your wallet’s Lightning liquidity.
  • Pay a Bolt11 invoice using your wallet’s on-chain balance.

Both directions use the same Payments API you use for other sends.

You need:

  • A BTC node-backed wallet with swaps enabled.
  • An API key with access to its organization and environment.
  • Enough balance on the funding rail for the payment and network fees.

We recommend testing first on Mutinynet with low-value funds and a destination controlled by a separate wallet or node.

Send a POST request to:

/v1/organizations/{organization_id}/environments/{environment_id}/payments

Generate the payment ID in your application and retain it before sending the request. Payment creation returns 202 Accepted, and you will use that ID to track the swap.

The following example uses 50,000 sats from the node-backed wallet’s on-chain balance to pay a Bolt11 invoice:

Terminal window
curl --request POST \
--url "https://voltageapi.com/v1/organizations/$ORGANIZATION_ID/environments/$ENVIRONMENT_ID/payments" \
--header "content-type: application/json" \
--header "x-api-key: $VOLTAGE_API_KEY" \
--data '{
"id": "YOUR_PAYMENT_UUID",
"wallet_id": "YOUR_NODE_BACKED_WALLET_ID",
"currency": "btc",
"type": "bolt11",
"flow_preference": "swap",
"data": {
"payment_request": "lntbs...",
"amount": {
"currency": "btc",
"amount": 50000000
},
"max_fee": {
"currency": "btc",
"amount": 2000000
}
}
}'

BTC amounts in this request are expressed in millisatoshis: 50,000 sats is 50000000 millisats. max_fee is a ceiling, not a fee quote.

Voltage publishes the hashlocked on-chain funding transaction and waits for the required confirmation count—at least one confirmation—before paying the Lightning invoice. It then claims the on-chain contract and completes the payment.

To pay an on-chain address from Lightning liquidity, change the payment type and destination data:

{
"id": "YOUR_PAYMENT_UUID",
"wallet_id": "YOUR_NODE_BACKED_WALLET_ID",
"currency": "btc",
"type": "onchain",
"flow_preference": "swap",
"data": {
"address": "tb1p...",
"amount": {
"currency": "btc",
"amount": 50000000
},
"max_fee": {
"currency": "btc",
"amount": 2000000
}
}
}

Voltage pays a private hold invoice from the customer node, funds the hashlocked on-chain output, and claims it to the requested address. The shared payment hash binds the two payment rails together.

Poll the payment using the ID you generated:

Terminal window
curl \
--url "https://voltageapi.com/v1/organizations/$ORGANIZATION_ID/environments/$ENVIRONMENT_ID/payments/YOUR_PAYMENT_UUID" \
--header "x-api-key: $VOLTAGE_API_KEY"

Continue until the top-level status is completed or failed. Immediately after creation, a read can briefly return 404 while the payment reaches the read model; retry it as a processing state.

Swap progress appears in data.outflows entries with type: "swap":

DirectionProgress steps
On-chain → Lightningsource_submitted → target_paid → source_claimed
Lightning → on-chainsource_payment_submitted → target_submitted → target_claimed

Receipts on these outflows provide transaction IDs, payment hashes, amounts, and confirmation details that you can surface in your application. The optional payment_breakdown contains the final principal and network-fee totals when available.

  • Use an external destination when testing. If Voltage finds a matching internal receive, it can complete the payment as a shortcircuit without creating a swap.
  • A completed on-chain-to-Lightning payment means the final claim was broadcast or recovered. Its block confirmation can arrive later.
  • flow_preference: "swap" does not fall back to a direct payment.
  • The contracts include timelocked refund paths, but the current public API does not automatically construct or broadcast refunds. A failure after funds are locked can require help from Voltage support.