---
title: "Send a payment"
description: "How to send sats over Lightning (send swap) or on-chain (cooperative leave) using the prepare then confirm flow."
canonical: https://wavelength.lightning.engineering/guides/send-a-payment/
---

> Docs index: https://wavelength.lightning.engineering/llms.txt

[Wavelength](/)›Guides›Send a payment

# Send a payment

2 min read

Guides

Web SDK

## Send over Lightning

Wavelength uses a send swap to route a Lightning payment from your Ark balance. Call `prepare({ invoice })` from [`useWalletPrepareSend()`](/reference/wavelength-react/#useWalletPrepareSend) to get a fee quote and a single-use `sendIntentId`. Pass the result to `sendPrepared()` from [`useWalletSend()`](/reference/wavelength-react/#useWalletSend) to execute.

The two-step flow lets you show the user the fee before committing. If the user cancels, discard the quote and call nothing. For a one-step path, call `send({ invoice })` from [`useWalletSend()`](/reference/wavelength-react/#useWalletSend) instead.

The quote returned by `prepare()` expires at `expiresAtUnix`. If the user waits too long before confirming, call `prepare()` again to get a fresh quote before calling `sendPrepared()`. Check `feeKnown` before displaying `expectedFeeSat`: when `feeKnown` is `false` the fee is only an estimate, and `warning` may contain additional detail to show the user.

```tsx
import { useWalletPrepareSend, useWalletSend } from '@lightninglabs/wavelength-react';

function SendLightning() {
  const { prepare, prepareData: quote, resetPrepare } = useWalletPrepareSend();
  const { sendPrepared } = useWalletSend();

  const doPrepare = async (bolt11: string) => {
    // prepare returns a fee quote and sendIntentId; prepareData holds it after.
    await prepare({ invoice: bolt11 });
  };

  const confirm = async () => {
    if (!quote) return;
    // sendPrepared dispatches the payment.
    await sendPrepared(quote);
    resetPrepare();
  };

  if (quote) {
    return (
      <div>
        <p>
          Fee: {quote.expectedFeeSat} sats
          {!quote.feeKnown && ' (estimate)'}
        </p>
        {quote.warning && <p role="alert">{quote.warning}</p>}
        <button onClick={confirm}>Confirm payment</button>
        <button onClick={resetPrepare}>Cancel</button>
      </div>
    );
  }

  return <button onClick={() => doPrepare('lnbc...')}>Pay invoice</button>;
}
```

## Send on-chain (cooperative leave)

An on-chain send performs a cooperative leave: the Ark server co-signs a transaction that moves your VTXO value to a plain Bitcoin address. This is faster and cheaper than a unilateral exit because the round happens in one confirmation.

Pass `onchainAddress` and `amountSat` to `send()` from [`useWalletSend()`](/reference/wavelength-react/#useWalletSend). The returned [`SendResult`](/reference/wavelength-core/#SendResult) includes the activity entry and `actualAmountSat`.

```tsx
import { useWalletSend } from '@lightninglabs/wavelength-react';

function SendOnChain() {
  const { send, sendPending } = useWalletSend();

  const handleSend = async (address: string, amountSat: number) => {
    await send({
      onchainAddress: address,
      amountSat,
    });
  };

  return (
    <button onClick={() => handleSend('bc1q...', 50_000)} disabled={sendPending}>
      Send on-chain
    </button>
  );
}
```

To drain the wallet to an address, set `sweepAll` instead of `amountSat`. The daemon snapshots the live VTXO set at prepare time and spends exactly that set, so quote and dispatch cannot disagree about what “all” means.

```tsx
await send({ onchainAddress: address, sweepAll: true });
```

## Handle errors

`send` and `sendPrepared` from [`useWalletSend()`](/reference/wavelength-react/#useWalletSend) throw a [`WavelengthError`](/reference/wavelength-core/#WavelengthError) on failure and set the hook’s `sendError`, which is always `Error | null`. Check `err.code` for a machine-readable reason. Display `err.message` to the user.

Errors thrown during `prepare` (from [`useWalletPrepareSend()`](/reference/wavelength-react/#useWalletPrepareSend)) are safe to retry. Errors thrown during `sendPrepared` may indicate the payment was already sent; check the activity feed before retrying.

```tsx
import { useWalletSend, WavelengthError } from '@lightninglabs/wavelength-react';

function SafeSend() {
  const { send, sendError, resetSend } = useWalletSend();

  const pay = async (bolt11: string) => {
    resetSend();
    try {
      await send({ invoice: bolt11 });
    } catch (err) {
      if (err instanceof WavelengthError) {
        console.error('Payment failed:', err.code, err.message);
      }
    }
  };

  return (
    <>
      <button onClick={() => pay('lnbc...')}>Pay</button>
      {sendError && <p role="alert">{sendError.message}</p>}
    </>
  );
}
```
