# 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 the Demo

See swaps in action with the [Voltage Swap demo](https://voltage-swaps.netlify.app/). The demo runs on Mutinynet and shows both on-chain to Lightning and Lightning to on-chain swaps.

[![](https://voltage-swaps.netlify.app/swaps-demo.png)](https://voltage-swaps.netlify.app/)

[Open the Voltage Swap demo](https://voltage-swaps.netlify.app/)

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.

## Before you begin

:::note
**Node-backed wallets only:** Swaps are designed for BTC wallets backed by your Lightning node. Credit-backed wallets already support both Lightning and on-chain payments natively from the same wallet, so they do not need swaps and cannot set `flow_preference: "swap"`.
:::

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.

## Create a swap

Send a `POST` request to:

```text
/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.

### On-chain to Lightning

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

```bash
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.

### Lightning to on-chain

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

```json
{
  "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.

## Track the payment

Poll the payment using the ID you generated:

```bash
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"`:

| Direction | Progress steps |
| --- | --- |
| On-chain → Lightning | `source_submitted` → `target_paid` → `source_claimed` |
| Lightning → on-chain | `source_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.

## Testing notes

- 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.
