Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SDK Documentation Spark
Request API Key

Claiming on-chain deposits

On-chain funds do not have to wait for 3 confirmations. With instant and expedited claims, a deposit can reach the wallet's balance while it is still in the mempool, or a block or two after it confirms. The SDK detects a deposit as soon as it is in the mempool, follows it through its confirmations, and claims it on its own as soon as the cost of doing so fits the fee limits you set.

A claim comes in three speeds. For the two faster ones the Spark Service Provider fronts the funds and charges an instant claim fee for doing so. The standard claim costs an ordinary on-chain fee.

ClaimWhen funds arriveCostClaimed automatically when
InstantAt 0 confirmations, while the deposit is still in the mempool. Offered for some deposits only.Instant claim feeThe instant claim fee fits the deposit's max fee
ExpeditedAt 1 or 2 confirmationsInstant claim feeThe instant claim fee fits the deposit's max fee
StandardAt 3 confirmations on mainnet (1 on regtest)On-chain fee onlyThe on-chain fee fits the deposit's max fee

A deposit's max fee is the configured max claim fee, unless the deposit has been given a max fee of its own. Its own max fee then governs the instant and expedited claims, and the standard claim runs under the larger of the two.

What each speed would cost for a particular deposit can be checked before claiming it, by fetching a fee quote.

If the deposit's max fee is too low for any of the three, the deposit is not claimed automatically and should be claimed manually.

Developer note

The SDK attempts an instant or expedited claim whenever the provider offers one, and takes it when its fee fits the max claim fee. The default max claim fee of 1 sat/vbyte (about 99 sats) is below any instant claim fee, so with the default setting deposits are credited by the standard claim. Raise it to have deposits credited early.

Setting a max claim fee

The max claim fee in the SDK configuration is the most the SDK will pay when it claims a deposit on its own. It takes the form of an absolute amount in sats, a rate in sats/vbyte, or the fastest recommended fee with a leeway, as described on the configuration page.

The max claim fee is one dial for two things. It caps the on-chain fee of a standard claim, and it caps the instant claim fee the provider may take for an instant or expedited claim. The value you choose therefore decides both how much on-chain fee the SDK will pay and whether deposits are claimed early at all.

To make automatic claims more likely, set the max claim fee to the fastest recommended rate at the time of the claim. This can result in higher fees.

Rust
// Create the default config
let mut config = default_config(Network::Mainnet);
config.api_key = Some("<breez api key>".to_string());

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config.max_deposit_claim_fee = Some(MaxFee::NetworkRecommended {
    leeway_sat_per_vbyte: 1,
});
Swift
// Create the default config
var config = defaultConfig(network: Network.mainnet)
config.apiKey = "<breez api key>"

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config.maxDepositClaimFee = MaxFee.networkRecommended(leewaySatPerVbyte: 1)
Kotlin
// Create the default config
val config = defaultConfig(Network.MAINNET)
config.apiKey = "<breez api key>"

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config.maxDepositClaimFee = MaxFee.NetworkRecommended(leewaySatPerVbyte = 1u)
C#
// Create the default config
var config = BreezSdkSparkMethods.DefaultConfig(Network.Mainnet) with
{
    apiKey = "<breez api key>"
};

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config = config with { maxDepositClaimFee = new MaxFee.NetworkRecommended(leewaySatPerVbyte: 1) };
Javascript
// Create the default config
const config = defaultConfig('mainnet')
config.apiKey = '<breez api key>'

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config.maxDepositClaimFee = { type: 'networkRecommended', leewaySatPerVbyte: 1 }
React Native
// Create the default config
const config = defaultConfig(Network.Mainnet)
config.apiKey = '<breez api key>'

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config.maxDepositClaimFee = new MaxFee.NetworkRecommended({ leewaySatPerVbyte: BigInt(1) })
Flutter
// Create the default config
var config = defaultConfig(network: Network.mainnet);
config = config.copyWith(apiKey: "<breez api key>");

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
config = config.copyWith(
    maxDepositClaimFee:
        MaxFee.networkRecommended(leewaySatPerVbyte: BigInt.from(1)));
Python
# Create the default config
config = default_config(network=Network.MAINNET)
config.api_key = "<breez api key>"

# Set the maximum fee to the fastest network recommended fee at the time of claim
# with a leeway of 1 sats/vbyte
config.max_deposit_claim_fee = MaxFee.NETWORK_RECOMMENDED(leeway_sat_per_vbyte=1)
Go
// Create the default config
config := breez_sdk_spark.DefaultConfig(breez_sdk_spark.NetworkMainnet)
apiKey := "<breez api key>"
config.ApiKey = &apiKey

// Set the maximum fee to the fastest network recommended fee at the time of claim
// with a leeway of 1 sats/vbyte
networkRecommendedInterface := breez_sdk_spark.MaxFee(
	breez_sdk_spark.MaxFeeNetworkRecommended{LeewaySatPerVbyte: 1},
)
config.MaxDepositClaimFee = &networkRecommendedInterface

Even with a high max claim fee the SDK might still fail to claim a deposit on its own. When that happens it emits SdkEvent::UnclaimedDepositsSdkEvent.UNCLAIMED_DEPOSITSSdkEvent.unclaimedDepositsSdkEvent.UnclaimedDepositsSdkEvent.UnclaimedDepositsSdkEvent.UnclaimedDepositsSdkEvent.UnclaimedDepositsSdkEventUnclaimedDepositsSdkEvent.UnclaimedDeposits with the deposit's details, and the recommended approach is to claim it manually once the user has accepted the required fee. See Listening to events for how to subscribe.

Giving one deposit its own max fee

A max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee passed to claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit is recorded on that deposit and carried into the SDK's later automatic attempts on it. That is how one deposit is treated differently from the rest without changing the configuration for all of them.

  • Raising it above the instant claim fee lets that single deposit be claimed early. The SDK keeps applying it on later sync passes, so the app does not have to keep calling claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit until the claim lands.
  • Lowering it below the instant claim fee holds that one deposit back from an instant or expedited claim. It waits for the standard claim while the others keep claiming early under the configured max claim fee.

The standard claim runs under whichever is larger, the deposit's own max fee or the configured one. A raised max fee therefore applies to the standard claim too, should the early claim never happen, so raise it to what you are willing to pay for the deposit, not only for the early claim. A lowered one restricts only the early claim.

A few more rules to keep in mind:

  • Calling claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit without a max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee claims under the configured max claim fee and clears any max fee standing on the deposit.
  • The max fee is recorded before the claim is attempted, so it stands whatever the attempt reports. What the attempt reports is covered under Handling claim outcomes.
  • The recorded value is readable as max_claim_feemax_claim_feemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeeMaxClaimFeeMaxClaimFee on each deposit from list_unclaimed_depositslist_unclaimed_depositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositsListUnclaimedDepositsListUnclaimedDeposits, unset while the configured max claim fee applies.
  • Deposits are not part of the synced wallet records, so a max fee set on one device stays on that device. The same deposit seen from another device runs under whatever that device has configured.

Seeing deposits before they confirm

A deposit is visible in the SDK from the moment it reaches the mempool, so an app can show it to the user, or claim it, before the first confirmation. The Spark operators only report a deposit once it has a confirmation, so the SDK also asks its chain service about the deposit addresses it has handed out. A deposit found this way arrives through SdkEvent::NewDepositsSdkEvent.NEW_DEPOSITSSdkEvent.newDepositsSdkEvent.NewDepositsSdkEvent.NewDepositsSdkEvent.NewDepositsSdkEvent.NewDepositsSdkEventNewDepositsSdkEvent.NewDeposits and appears in list_unclaimed_depositslist_unclaimed_depositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositsListUnclaimedDepositsListUnclaimedDeposits with is_matureis_matureisMatureisMatureisMatureisMatureisMatureIsMatureIsMature false, whether or not the SDK goes on to claim it automatically.

Each watched address costs one chain-service request per sync. Requesting a receive address starts a 24-hour window on it, and requesting it again restarts that window, so a wallet that is not expecting an on-chain payment settles at no requests at all. An address that has taken a deposit keeps being watched past its window until that deposit confirms.

Listing unclaimed deposits

list_unclaimed_depositslist_unclaimed_depositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositsListUnclaimedDepositsListUnclaimedDeposits returns every deposit the SDK is tracking:

  • Pending deposits that have not yet reached the standard claim depth (is_matureis_matureisMatureisMatureisMatureisMatureisMatureIsMatureIsMature is false). These are claimed automatically once they reach the standard claim depth, or sooner if the max claim fee covers an instant or expedited claim.
  • Deposits with enough confirmations whose claim failed, with the specific failure reason.
  • Deposits already claimed whose output the provider has not yet spent.
Rust
let request = ListUnclaimedDepositsRequest {};
let response = sdk.list_unclaimed_deposits(request).await?;

for deposit in response.deposits {
    info!("Unclaimed deposit: {}:{}", deposit.txid, deposit.vout);
    info!("Amount: {} sats", deposit.amount_sats);

    if let Some(claim_error) = &deposit.claim_error {
        match claim_error {
            DepositClaimError::MaxDepositClaimFeeExceeded {
                max_fee,
                required_fee_sats,
                required_fee_rate_sat_per_vbyte,
                ..
            } => {
                info!(
                    "Max claim fee exceeded. Max: {:?}, Required: {} sats or {} sats/vByte",
                    max_fee, required_fee_sats, required_fee_rate_sat_per_vbyte
                );
            }
            DepositClaimError::MissingUtxo { .. } => {
                info!("UTXO not found when claiming deposit");
            }
            DepositClaimError::DepositTooSmall { .. } => {
                info!("Deposit too small to claim");
            }
            DepositClaimError::Generic { message } => {
                info!("Claim failed: {}", message);
            }
        }
    }
}
Swift
let request = ListUnclaimedDepositsRequest()
let response = try await sdk.listUnclaimedDeposits(request: request)

for deposit in response.deposits {
    print("Unclaimed deposit: \(deposit.txid):\(deposit.vout)")
    print("Amount: \(deposit.amountSats) sats")

    if let claimError = deposit.claimError {
        switch claimError {
        case .maxDepositClaimFeeExceeded(
            let tx, let vout, let maxFee, let requiredFeeSats, let requiredFeeRateSatPerVbyte):
            let maxFeeStr: String
            if let maxFee = maxFee {
                switch maxFee {
                case .fixed(let amount):
                    maxFeeStr = "\(amount) sats"
                case .rate(let satPerVbyte):
                    maxFeeStr = "\(satPerVbyte) sats/vByte"
                }
            } else {
                maxFeeStr = "none"
            }
            print(
                "Max claim fee exceeded. Max: \(maxFeeStr), "
                    + "Required: \(requiredFeeSats) sats or "
                    + "\(requiredFeeRateSatPerVbyte) sats/vByte"
            )
        case .missingUtxo(let tx, let vout):
            print("UTXO not found when claiming deposit")
        case .depositTooSmall(let tx, let vout):
            print("Deposit too small to claim")
        case .generic(let message):
            print("Claim failed: \(message)")
        }
    }
}
Kotlin
try {
    val request = ListUnclaimedDepositsRequest
    val response = sdk.listUnclaimedDeposits(request)

    for (deposit in response.deposits) {
        // Log.v("Breez", "Unclaimed deposit: ${deposit.txid}:${deposit.vout}")
        // Log.v("Breez", "Amount: ${deposit.amountSats} sats")

        deposit.claimError?.let { claimError ->
            when (claimError) {
                is DepositClaimError.MaxDepositClaimFeeExceeded -> {
                    val maxFee = claimError.maxFee
                    val maxFeeStr = when (maxFee) {
                        is Fee.Fixed -> "${maxFee.amount} sats"
                        is Fee.Rate -> "${maxFee.satPerVbyte} sats/vByte"
                        null -> "none"
                    }
                    // Log.v("Breez", "Max claim fee exceeded. Max: $maxFeeStr,
                    // Required: ${claimError.requiredFeeSats} sats or
                    // ${claimError.requiredFeeRateSatPerVbyte} sats/vByte")
                }
                is DepositClaimError.MissingUtxo -> {
                    // Log.v("Breez", "UTXO not found when claiming deposit")
                }
                is DepositClaimError.DepositTooSmall -> {
                    // Log.v("Breez", "Deposit too small to claim")
                }
                is DepositClaimError.Generic -> {
                    // Log.v("Breez", "Claim failed: ${claimError.message}")
                }
            }
        }
    }
} catch (e: Exception) {
    // handle error
}
C#
var request = new ListUnclaimedDepositsRequest();
var response = await sdk.ListUnclaimedDeposits(request: request);

foreach (var deposit in response.deposits)
{
    Console.WriteLine($"Unclaimed deposit: {deposit.txid}:{deposit.vout}");
    Console.WriteLine($"Amount: {deposit.amountSats} sats");

    if (deposit.claimError != null)
    {
        if (deposit.claimError is DepositClaimError.MaxDepositClaimFeeExceeded exceeded)
        {
            var maxFeeStr = "none";
            if (exceeded.maxFee != null)
            {
                if (exceeded.maxFee is Fee.Fixed fixedFee)
                {
                    maxFeeStr = $"{fixedFee.amount} sats";
                }
                else if (exceeded.maxFee is Fee.Rate rateFee)
                {
                    maxFeeStr = $"{rateFee.satPerVbyte} sats/vByte";
                }
            }
            Console.WriteLine($"Claim failed: Fee exceeded. Max: {maxFeeStr}, " +
                            $"Required: {exceeded.requiredFeeSats} sats or " +
                            $"{exceeded.requiredFeeRateSatPerVbyte} sats/vByte");
        }
        else if (deposit.claimError is DepositClaimError.MissingUtxo)
        {
            Console.WriteLine("Claim failed: UTXO not found");
        }
        else if (deposit.claimError is DepositClaimError.DepositTooSmall)
        {
            Console.WriteLine("Claim failed: deposit too small to claim");
        }
        else if (deposit.claimError is DepositClaimError.Generic generic)
        {
            Console.WriteLine($"Claim failed: {generic.message}");
        }
    }
}
Javascript
const request: ListUnclaimedDepositsRequest = {}
const response = await sdk.listUnclaimedDeposits(request)

for (const deposit of response.deposits) {
  console.log(`Unclaimed deposit: ${deposit.txid}:${deposit.vout}`)
  console.log(`Amount: ${deposit.amountSats} sats`)

  if (deposit.claimError != null) {
    switch (deposit.claimError.type) {
      case 'maxDepositClaimFeeExceeded': {
        let maxFeeStr = 'none'
        if (deposit.claimError.maxFee != null) {
          if (deposit.claimError.maxFee.type === 'fixed') {
            maxFeeStr = `${deposit.claimError.maxFee.amount} sats`
          } else if (deposit.claimError.maxFee.type === 'rate') {
            maxFeeStr = `${deposit.claimError.maxFee.satPerVbyte} sats/vByte`
          }
        }
        console.log(
          `Max claim fee exceeded. Max: ${maxFeeStr}, ` +
          `Required: ${deposit.claimError.requiredFeeSats} sats or ` +
          `${deposit.claimError.requiredFeeRateSatPerVbyte} sats/vByte`
        )
        break
      }
      case 'missingUtxo':
        console.log('UTXO not found when claiming deposit')
        break
      case 'depositTooSmall':
        console.log('Deposit too small to claim')
        break
      case 'generic':
        console.log(`Claim failed: ${deposit.claimError.message}`)
        break
    }
  }
}
React Native
const request: ListUnclaimedDepositsRequest = {}
const response = await sdk.listUnclaimedDeposits(request)

for (const deposit of response.deposits) {
  console.log(`Unclaimed deposit: ${deposit.txid}:${deposit.vout}`)
  console.log(`Amount: ${deposit.amountSats} sats`)

  if (deposit.claimError != null) {
    if (deposit.claimError?.tag === DepositClaimError_Tags.MaxDepositClaimFeeExceeded) {
      let maxFeeStr = 'none'
      if (deposit.claimError.inner.maxFee != null) {
        if (deposit.claimError.inner.maxFee.tag === Fee_Tags.Fixed) {
          maxFeeStr = `${deposit.claimError.inner.maxFee.inner.amount} sats`
        } else if (deposit.claimError.inner.maxFee.tag === Fee_Tags.Rate) {
          maxFeeStr = `${deposit.claimError.inner.maxFee.inner.satPerVbyte} sats/vByte`
        }
      }
      console.log(
        `Max claim fee exceeded. Max: ${maxFeeStr}, 
        Required: ${deposit.claimError.inner.requiredFeeSats} sats 
        or ${deposit.claimError.inner.requiredFeeRateSatPerVbyte} sats/vByte`
      )
    } else if (deposit.claimError?.tag === DepositClaimError_Tags.MissingUtxo) {
      console.log('UTXO not found when claiming deposit')
    } else if (deposit.claimError?.tag === DepositClaimError_Tags.DepositTooSmall) {
      console.log('Deposit too small to claim')
    } else if (deposit.claimError?.tag === DepositClaimError_Tags.Generic) {
      console.log(`Claim failed: ${deposit.claimError.inner.message}`)
    }
  }
}
Flutter
final request = ListUnclaimedDepositsRequest();
final response = await sdk.listUnclaimedDeposits(request: request);

for (DepositInfo deposit in response.deposits) {
  print("Unclaimed deposit: ${deposit.txid}:${deposit.vout}");
  print("Amount: ${deposit.amountSats} sats");

  final claimError = deposit.claimError;
  if (claimError is DepositClaimError_MaxDepositClaimFeeExceeded) {
    final maxFeeStr = claimError.maxFee != null
        ? (claimError.maxFee is Fee_Fixed
            ? '${(claimError.maxFee as Fee_Fixed).amount} sats'
            : '${(claimError.maxFee as Fee_Rate).satPerVbyte} sats/vByte')
        : 'none';
    print("Max claim fee exceeded. Max: $maxFeeStr, "
        "Required: ${claimError.requiredFeeSats} sats or "
        "${claimError.requiredFeeRateSatPerVbyte} sats/vByte");
  } else if (claimError is DepositClaimError_MissingUtxo) {
    print("UTXO not found when claiming deposit");
  } else if (claimError is DepositClaimError_DepositTooSmall) {
    print("Deposit too small to claim");
  } else if (claimError is DepositClaimError_Generic) {
    print("Claim failed: ${claimError.message}");
  }
}
Python
try:
    request = ListUnclaimedDepositsRequest()
    response = await sdk.list_unclaimed_deposits(request=request)

    for deposit in response.deposits:
        logging.info(f"Unclaimed deposit: {deposit.txid}:{deposit.vout}")
        logging.info(f"Amount: {deposit.amount_sats} sats")

        if deposit.claim_error:
            if isinstance(
                deposit.claim_error, DepositClaimError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDED
            ):
                max_fee_str = "none"
                if deposit.claim_error.max_fee is not None:
                    if isinstance(deposit.claim_error.max_fee, Fee.FIXED):
                        max_fee_str = f"{deposit.claim_error.max_fee.amount} sats"
                    elif isinstance(deposit.claim_error.max_fee, Fee.RATE):
                        max_fee_str = f"{deposit.claim_error.max_fee.sat_per_vbyte} sats/vByte"
                logging.info(
                    f"Claim failed: Fee exceeded. Max: {max_fee_str}, "
                    f"Required: {deposit.claim_error.required_fee_sats} sats "
                    f"or {deposit.claim_error.required_fee_rate_sat_per_vbyte} sats/vByte"
                )
            elif isinstance(deposit.claim_error, DepositClaimError.MISSING_UTXO):
                logging.info("Claim failed: UTXO not found")
            elif isinstance(deposit.claim_error, DepositClaimError.DEPOSIT_TOO_SMALL):
                logging.info("Claim failed: deposit too small to claim")
            elif isinstance(deposit.claim_error, DepositClaimError.GENERIC):
                logging.info(f"Claim failed: {deposit.claim_error.message}")
except Exception as error:
    logging.error(error)
    raise
Go
request := breez_sdk_spark.ListUnclaimedDepositsRequest{}
response, err := sdk.ListUnclaimedDeposits(request)

if err != nil {
	var sdkErr *breez_sdk_spark.SdkError
	if errors.As(err, &sdkErr) {
		// Handle SdkError - can inspect specific variants if needed
		// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
	}
	return err
}

for _, deposit := range response.Deposits {
	log.Printf("Unclaimed Deposit: %v:%v", deposit.Txid, deposit.Vout)
	log.Printf("Amount: %v sats", deposit.AmountSats)

	if claimErr := *deposit.ClaimError; claimErr != nil {
		switch claimErr := claimErr.(type) {
		case breez_sdk_spark.DepositClaimErrorMaxDepositClaimFeeExceeded:
			maxFeeStr := "none"
			if claimErr.MaxFee != nil {
				switch fee := (*claimErr.MaxFee).(type) {
				case breez_sdk_spark.FeeFixed:
					maxFeeStr = fmt.Sprintf("%v sats", fee.Amount)
				case breez_sdk_spark.FeeRate:
					maxFeeStr = fmt.Sprintf("%v sats/vByte", fee.SatPerVbyte)
				}
			}
			log.Printf(
				"Max claim fee exceeded. Max: %v, Required: %v sats or %v sats/vByte",
				maxFeeStr,
				claimErr.RequiredFeeSats,
				claimErr.RequiredFeeRateSatPerVbyte,
			)
		case breez_sdk_spark.DepositClaimErrorMissingUtxo:
			log.Print("UTXO not found when claiming deposit")
		case breez_sdk_spark.DepositClaimErrorDepositTooSmall:
			log.Print("Deposit too small to claim")
		case breez_sdk_spark.DepositClaimErrorGeneric:
			log.Printf("Claim failed: %v", claimErr.Message)
		}
	}
}

Deposits that are already claimed

A deposit taken by an instant or expedited claim carries InstantClaimStatus::SubmittedInstantClaimStatus.SUBMITTEDInstantClaimStatus.submittedInstantClaimStatus.SubmittedInstantClaimStatus.SubmittedInstantClaimStatus.SubmittedInstantClaimStatus.SubmittedInstantClaimStatusSubmittedInstantClaimStatus.Submitted in its instant_claim_statusinstant_claim_statusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusInstantClaimStatusInstantClaimStatus while the claim settles, and InstantClaimStatus::ClaimedInstantClaimStatus.CLAIMEDInstantClaimStatus.claimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatusClaimedInstantClaimStatus.Claimed once the amount is credited. It stays in the list until the provider spends the deposit output, some time after the credit. Treat InstantClaimStatus::ClaimedInstantClaimStatus.CLAIMEDInstantClaimStatus.claimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatusClaimedInstantClaimStatus.Claimed as settled and branch on it rather than showing the deposit as awaiting action. When the SDK claims automatically it emits SdkEvent::ClaimedDepositsSdkEvent.CLAIMED_DEPOSITSSdkEvent.claimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEventClaimedDepositsSdkEvent.ClaimedDeposits at submission, so a deposit can appear both in that event and in this list.

A deposit claimed elsewhere, by another instance sharing the wallet or on another device, reaches InstantClaimStatus::ClaimedInstantClaimStatus.CLAIMEDInstantClaimStatus.claimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatusClaimedInstantClaimStatus.Claimed the next time the SDK tries to claim it and the provider reports it as already claimed. No SdkEvent::ClaimedDepositsSdkEvent.CLAIMED_DEPOSITSSdkEvent.claimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEvent.ClaimedDepositsSdkEventClaimedDepositsSdkEvent.ClaimedDeposits event is emitted, because the claim was not made here. The credit still arrives as a payment, so follow it through list_paymentslist_paymentslistPaymentslistPaymentslistPaymentslistPaymentslistPaymentsListPaymentsListPayments or the payment events.

Instant & expedited claims

A deposit does not have to wait for 3 confirmations. The Spark Service Provider fronts the credited amount as soon as it is willing to carry the risk, which brings a deposit to the balance while it is still in the mempool (an instant claim) or after 1 or 2 confirmations (an expedited claim). Both kinds are reported through the instantinstantinstantinstantinstantinstantinstantInstantInstant quote and instant_claim_statusinstant_claim_statusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusInstantClaimStatusInstantClaimStatus.

What it costs, and when it is offered:

  • The provider charges the instant claim fee for fronting the funds. It is roughly the on-chain cost of the provider's own claim plus a percentage of the deposit, so it grows with the deposit.
  • A claim at 0 confirmations is offered for some deposits only. Whether a deposit is fronted at all, and at which depth, is decided by the provider per deposit. It is not a setting you control.
  • The SDK re-attempts the early claim as confirmations arrive, so a deposit the provider will not yet front in the mempool is often claimed expedited a block or two later.

The SDK claims early on its own whenever the provider offers an early claim and its fee fits the max claim fee, so the max claim fee decides whether deposits are credited early. To credit a single deposit early instead, call claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit with a higher max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee, as described under Giving one deposit its own max fee.

Claiming a deposit manually

When a deposit is not claimed automatically because the max claim fee is too low, claim it with claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit and a higher max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee. The recommended approach is to show the user the required fee and ask for approval before claiming.

Claiming a deposit that already has a claim returns SdkError::DepositClaimInProgressSdkError.DEPOSIT_CLAIM_IN_PROGRESSSdkError.depositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkErrorDepositClaimInProgressSdkError.DepositClaimInProgress. That covers a claim still running, from a background attempt or another call, and one that has already credited the deposit and is waiting for the provider to spend the output. Neither is a failure to show the user, and neither needs anything from you. Check instant_claim_statusinstant_claim_statusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusInstantClaimStatusInstantClaimStatus to tell them apart.

Rust
if let Some(DepositClaimError::MaxDepositClaimFeeExceeded {
    required_fee_sats, ..
}) = &deposit.claim_error
{
    // Show UI to user with the required fee and get approval
    let user_approved = true; // Replace with actual user approval logic

    if user_approved {
        let request = ClaimDepositRequest {
            txid: deposit.txid.clone(),
            vout: deposit.vout,
            max_fee: Some(MaxFee::Fixed {
                amount: *required_fee_sats,
            }),
        };
        sdk.claim_deposit(request).await?;
    }
}
Swift
if case .maxDepositClaimFeeExceeded(_, _, _, let requiredFeeSats, _) = deposit.claimError {
    // Show UI to user with the required fee and get approval
    let userApproved = true  // Replace with actual user approval logic

    if userApproved {
        let claimRequest = ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: MaxFee.fixed(amount: requiredFeeSats)
        )
        try await sdk.claimDeposit(request: claimRequest)
    }
}
Kotlin
try {
    val claimError = deposit.claimError
    if (claimError is DepositClaimError.MaxDepositClaimFeeExceeded) {
        val requiredFee = claimError.requiredFeeSats

        // Show UI to user with the required fee and get approval
        val userApproved = true // Replace with actual user approval logic

        if (userApproved) {
            val claimRequest = ClaimDepositRequest(
                txid = deposit.txid,
                vout = deposit.vout,
                maxFee = MaxFee.Fixed(requiredFee)
            )
            sdk.claimDeposit(claimRequest)
        }
    }
} catch (e: Exception) {
    // handle error
}
C#
if (deposit.claimError is DepositClaimError.MaxDepositClaimFeeExceeded exceeded)
{
    var requiredFee = exceeded.requiredFeeSats;

    // Show UI to user with the required fee and get approval
    var userApproved = true; // Replace with actual user approval logic

    if (userApproved)
    {
        var claimRequest = new ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: new MaxFee.Fixed(amount: requiredFee)
        );
        await sdk.ClaimDeposit(request: claimRequest);
    }
}
Javascript
if (deposit.claimError?.type === 'maxDepositClaimFeeExceeded') {
  const requiredFee = deposit.claimError.requiredFeeSats

  // Show UI to user with the required fee and get approval
  const userApproved = true // Replace with actual user approval logic

  if (userApproved) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: { type: 'fixed', amount: requiredFee }
    }
    await sdk.claimDeposit(claimRequest)
  }
}
React Native
if (deposit.claimError?.tag === DepositClaimError_Tags.MaxDepositClaimFeeExceeded) {
  const requiredFee = deposit.claimError.inner.requiredFeeSats

  // Show UI to user with the required fee and get approval
  const userApproved = true // Replace with actual user approval logic

  if (userApproved) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: new MaxFee.Fixed({ amount: requiredFee })
    }
    await sdk.claimDeposit(claimRequest)
  }
}
Flutter
final claimError = deposit.claimError;
if (claimError is DepositClaimError_MaxDepositClaimFeeExceeded) {
  final requiredFee = claimError.requiredFeeSats;

  // Show UI to user with the required fee and get approval
  bool userApproved = true; // Replace with actual user approval logic

  if (userApproved) {
    final claimRequest = ClaimDepositRequest(
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee.fixed(amount: requiredFee),
    );
    await sdk.claimDeposit(request: claimRequest);
  }
}
Python
try:
    if isinstance(
        deposit.claim_error, DepositClaimError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDED
    ):
        required_fee = deposit.claim_error.required_fee_sats

        # Show UI to user with the required fee and get approval
        user_approved = True  # Replace with actual user approval logic

        if user_approved:
            claim_request = ClaimDepositRequest(
                txid=deposit.txid,
                vout=deposit.vout,
                max_fee=Fee.FIXED(amount=required_fee),
            )
            await sdk.claim_deposit(request=claim_request)
except Exception as error:
    logging.error(error)
    raise
Go
if claimErr := *deposit.ClaimError; claimErr != nil {
	if exceeded, ok := claimErr.(breez_sdk_spark.DepositClaimErrorMaxDepositClaimFeeExceeded); ok {
		requiredFee := exceeded.RequiredFeeSats

		// Show UI to user with the required fee and get approval
		userApproved := true // Replace with actual user approval logic

		if userApproved {
			maxFee := breez_sdk_spark.MaxFee(breez_sdk_spark.MaxFeeFixed{Amount: requiredFee})
			claimRequest := breez_sdk_spark.ClaimDepositRequest{
				Txid:   deposit.Txid,
				Vout:   deposit.Vout,
				MaxFee: &maxFee,
			}
			_, err := sdk.ClaimDeposit(claimRequest)
			if err != nil {
				var sdkErr *breez_sdk_spark.SdkError
				if errors.As(err, &sdkErr) {
					// Handle SdkError - can inspect specific variants if needed
					// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
				}
				return err
			}
		}
	}
}

Writing your own claim logic

For advanced use cases you may want to write your own claim logic instead of relying on the SDK's automatic process. This gives you complete control over when and how deposits are claimed.

To disable automatic claims, unset the max claim fee. Then use the methods on this page to claim deposits manually according to your business logic. Common scenarios include:

  • Dynamic fee adjustment: Adjust claiming fees based on market conditions or priority
  • Conditional claiming: Only claim deposits that meet certain criteria (amount thresholds, time windows, etc.)
  • Integration with external systems: Coordinate claims with other business processes

The recommended fees API is useful for determining appropriate fee levels for claiming deposits. For example, you can claim a deposit only if the required fee rate is less than the fastest recommended fee (or any other).

Rust
if let Some(DepositClaimError::MaxDepositClaimFeeExceeded {
    required_fee_rate_sat_per_vbyte,
    ..
}) = &deposit.claim_error
{
    let recommended_fees = sdk.recommended_fees().await?;

    if *required_fee_rate_sat_per_vbyte <= recommended_fees.fastest_fee {
        let request = ClaimDepositRequest {
            txid: deposit.txid.clone(),
            vout: deposit.vout,
            max_fee: Some(MaxFee::Rate {
                sat_per_vbyte: *required_fee_rate_sat_per_vbyte,
            }),
        };
        sdk.claim_deposit(request).await?;
    }
}
Swift
if case .maxDepositClaimFeeExceeded(_, _, _, _, let requiredFeeRateSatPerVbyte) =
    deposit.claimError
{
    let recommendedFees = try await sdk.recommendedFees()

    if requiredFeeRateSatPerVbyte <= recommendedFees.fastestFee {
        let claimRequest = ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: MaxFee.rate(satPerVbyte: requiredFeeRateSatPerVbyte)
        )
        try await sdk.claimDeposit(request: claimRequest)
    }
}
Kotlin
try {
    val claimError = deposit.claimError
    if (claimError is DepositClaimError.MaxDepositClaimFeeExceeded) {
        val requiredFeeRate = claimError.requiredFeeRateSatPerVbyte

        val recommendedFees = sdk.recommendedFees()

        if (requiredFeeRate <= recommendedFees.fastestFee) {
            val claimRequest = ClaimDepositRequest(
                txid = deposit.txid,
                vout = deposit.vout,
                maxFee = MaxFee.Rate(requiredFeeRate)
            )
            sdk.claimDeposit(claimRequest)
        }
    }
} catch (e: Exception) {
    // handle error
}
C#
if (deposit.claimError is DepositClaimError.MaxDepositClaimFeeExceeded exceeded)
{
    var requiredFeeRate = exceeded.requiredFeeRateSatPerVbyte;

    var recommendedFees = await sdk.RecommendedFees();

    if (requiredFeeRate <= recommendedFees.fastestFee)
    {
        var claimRequest = new ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: new MaxFee.Rate(satPerVbyte: requiredFeeRate)
        );
        await sdk.ClaimDeposit(request: claimRequest);
    }
}
Javascript
if (deposit.claimError?.type === 'maxDepositClaimFeeExceeded') {
  const requiredFeeRate = deposit.claimError.requiredFeeRateSatPerVbyte

  const recommendedFees = await sdk.recommendedFees()

  if (requiredFeeRate <= recommendedFees.fastestFee) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: { type: 'rate', satPerVbyte: requiredFeeRate }
    }
    await sdk.claimDeposit(claimRequest)
  }
}
React Native
if (deposit.claimError?.tag === DepositClaimError_Tags.MaxDepositClaimFeeExceeded) {
  const requiredFeeRate = deposit.claimError.inner.requiredFeeRateSatPerVbyte

  const recommendedFees = await sdk.recommendedFees()

  if (requiredFeeRate <= recommendedFees.fastestFee) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: new MaxFee.Rate({ satPerVbyte: requiredFeeRate })
    }
    await sdk.claimDeposit(claimRequest)
  }
}
Flutter
final claimError = deposit.claimError;
if (claimError is DepositClaimError_MaxDepositClaimFeeExceeded) {
  final requiredFeeRate = claimError.requiredFeeRateSatPerVbyte;

  final recommendedFees = await sdk.recommendedFees();

  if (requiredFeeRate <= recommendedFees.fastestFee) {
    final claimRequest = ClaimDepositRequest(
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee.rate(satPerVbyte: requiredFeeRate),
    );
    await sdk.claimDeposit(request: claimRequest);
  }
}
Python
try:
    if isinstance(
        deposit.claim_error, DepositClaimError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDED
    ):
        required_fee_rate = deposit.claim_error.required_fee_rate_sat_per_vbyte

        recommended_fees = await sdk.recommended_fees()

        if required_fee_rate <= recommended_fees.fastest_fee:
            claim_request = ClaimDepositRequest(
                txid=deposit.txid,
                vout=deposit.vout,
                max_fee=MaxFee.RATE(sat_per_vbyte=required_fee_rate),
            )
            await sdk.claim_deposit(request=claim_request)
except Exception as error:
    logging.error(error)
    raise
Go
if claimErr := *deposit.ClaimError; claimErr != nil {
	if exceeded, ok := claimErr.(breez_sdk_spark.DepositClaimErrorMaxDepositClaimFeeExceeded); ok {
		requiredFeeRate := exceeded.RequiredFeeRateSatPerVbyte

		recommendedFees, err := sdk.RecommendedFees()
		if err != nil {
			return err
		}

		if requiredFeeRate <= recommendedFees.FastestFee {
			maxFee := breez_sdk_spark.MaxFee(breez_sdk_spark.MaxFeeRate{SatPerVbyte: requiredFeeRate})
			claimRequest := breez_sdk_spark.ClaimDepositRequest{
				Txid:   deposit.Txid,
				Vout:   deposit.Vout,
				MaxFee: &maxFee,
			}
			_, err := sdk.ClaimDeposit(claimRequest)
			if err != nil {
				var sdkErr *breez_sdk_spark.SdkError
				if errors.As(err, &sdkErr) {
					// Handle SdkError - can inspect specific variants if needed
					// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
				}
				return err
			}
		}
	}
}

Pricing a claim with fee quotes

fetch_claim_deposit_quotefetch_claim_deposit_quotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuoteFetchClaimDepositQuoteFetchClaimDepositQuote prices both ways of claiming a deposit, so an app can offer the choice rather than deciding for the user. It returns the deposit's current confirmationsconfirmationsconfirmationsconfirmationsconfirmationsconfirmationsconfirmationsConfirmationsConfirmations alongside two quotes: instantinstantinstantinstantinstantinstantinstantInstantInstant for the instant or expedited claim, and maturematurematurematurematurematurematureMatureMature for the standard claim. Each quote carries the fee and confirmations_requiredconfirmations_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequired.

Reading the quotes:

  • confirmations_requiredconfirmations_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequired is the depth the deposit becomes claimable at, not a count of blocks still to wait. Subtract the deposit's current confirmations to get the wait: an early claim claimable at 1 confirmation, on a deposit with 0, is available a block from now.
  • On the instantinstantinstantinstantinstantinstantinstantInstantInstant quote, confirmations_requiredconfirmations_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequired also tells instant from expedited: 0 is an instant claim, 1 or 2 is an expedited one.
  • The instantinstantinstantinstantinstantinstantinstantInstantInstant quote is absent when the provider will not front this particular deposit. It is also absent when claiming early would not actually be earlier: once the deposit has reached the standard claim depth, or when the provider would only credit at that same depth, waiting is both cheaper and no slower, so there is no choice left to offer. It is absent, too, when the provider could not be reached for a quote, which is the one case worth retrying. An absent quote means no early claim is offered for this deposit right now, not that early claiming is unavailable.
  • The instantinstantinstantinstantinstantinstantinstantInstantInstant quote is priced whether or not the configured max claim fee would allow it, so the fee it shows is the provider's price rather than what the configured limit permits.
  • The maturematurematurematurematurematurematureMatureMature quote is always present, but may be flagged is_estimateis_estimateisEstimateisEstimateisEstimateisEstimateisEstimateIsEstimateIsEstimate when the provider will not quote a deposit this early. The fee is then derived from current on-chain fees and the final one may differ.

Acting on the instantinstantinstantinstantinstantinstantinstantInstantInstant quote yourself means passing a max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee to claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit of at least the quoted fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats. With a lower one the call returns ClaimDepositOutcome::DeferredClaimDepositOutcome.DEFERREDClaimDepositOutcome.deferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcomeDeferredClaimDepositOutcome.Deferred with ClaimDeferredReason::MaxFeeExceededClaimDeferredReason.MAX_FEE_EXCEEDEDClaimDeferredReason.maxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReasonMaxFeeExceededClaimDeferredReason.MaxFeeExceeded, carrying what the early claim would have cost, and the deposit waits for the standard claim unless the max fee is raised (see Handling claim outcomes).

What to offer follows from the quote and the configured max claim fee. Check the middle column first: where the SDK claims by itself, a dialog defaulting to the standard claim shows the user one outcome and delivers another.

Quote stateWhat the SDK will doWhat to present
No instantinstantinstantinstantinstantinstantinstantInstantInstant quoteStandard claimThe standard claim only
instantinstantinstantinstantinstantinstantinstantInstantInstant quote, depth not yet reachedClaim early once the depth arrives, if the fee fits the max claim feeThe standard claim, with the early claim shown as available in N blocks
instantinstantinstantinstantinstantinstantinstantInstantInstant quote at a reachable depth, fee above the max claim feeWait for the standard claimBoth, the early claim requiring an explicit higher max fee
instantinstantinstantinstantinstantinstantinstantInstantInstant quote at a reachable depth, fee within the max claim feeClaim early by itselfDefault to the early claim, or offer no choice
Rust
let quote = sdk
    .fetch_claim_deposit_quote(FetchClaimDepositQuoteRequest {
        txid: deposit.txid.clone(),
        vout: deposit.vout,
    })
    .await?;

// The standard claim, and how many blocks away it is.
let blocks_to_wait = quote
    .mature
    .confirmations_required
    .saturating_sub(quote.confirmations);
info!(
    "Wait {} blocks and pay {} sats",
    blocks_to_wait, quote.mature.fee_sats
);

// An instant or expedited claim, when the provider offers one.
if let Some(instant) = &quote.instant {
    let blocks_to_wait = instant
        .confirmations_required
        .saturating_sub(quote.confirmations);
    info!(
        "Or wait {} blocks and pay {} sats",
        blocks_to_wait, instant.fee_sats
    );
}
Swift
let request = FetchClaimDepositQuoteRequest(txid: deposit.txid, vout: deposit.vout)
let quote = try await sdk.fetchClaimDepositQuote(request: request)

// The standard claim, and how many blocks away it is.
var blocksToWait: UInt32 = 0
if quote.mature.confirmationsRequired > quote.confirmations {
    blocksToWait = quote.mature.confirmationsRequired - quote.confirmations
}
print("Wait \(blocksToWait) blocks and pay \(quote.mature.feeSats) sats")

// An instant or expedited claim, when the provider offers one.
if let instant = quote.instant {
    var instantBlocks: UInt32 = 0
    if instant.confirmationsRequired > quote.confirmations {
        instantBlocks = instant.confirmationsRequired - quote.confirmations
    }
    print("Or wait \(instantBlocks) blocks and pay \(instant.feeSats) sats")
}
Kotlin
try {
    val quote = sdk.fetchClaimDepositQuote(
        FetchClaimDepositQuoteRequest(
            txid = deposit.txid,
            vout = deposit.vout
        )
    )

    // UInt subtraction wraps, so clamp the wait at zero.
    fun blocksToWait(required: UInt) =
        if (required > quote.confirmations) required - quote.confirmations else 0u

    // The standard claim, and how many blocks away it is.
    val matureWait = blocksToWait(quote.mature.confirmationsRequired)
    // Log.v("Breez", "Wait $matureWait blocks and pay ${quote.mature.feeSats} sats")

    // An instant or expedited claim, when the provider offers one.
    quote.instant?.let { instant ->
        val instantWait = blocksToWait(instant.confirmationsRequired)
        // Log.v("Breez", "Or wait $instantWait blocks and pay ${instant.feeSats} sats")
    }
} catch (e: Exception) {
    // handle error
}
C#
var request = new FetchClaimDepositQuoteRequest(
    txid: deposit.txid,
    vout: deposit.vout
);
var quote = await sdk.FetchClaimDepositQuote(request: request);

// The standard claim, and how many blocks away it is.
var blocksToWait = quote.mature.confirmationsRequired > quote.confirmations
    ? quote.mature.confirmationsRequired - quote.confirmations
    : 0U;
Console.WriteLine($"Wait {blocksToWait} blocks and pay {quote.mature.feeSats} sats");

// An instant or expedited claim, when the provider offers one.
if (quote.instant is ClaimDepositQuote instant)
{
    var instantBlocksToWait = instant.confirmationsRequired > quote.confirmations
        ? instant.confirmationsRequired - quote.confirmations
        : 0U;
    Console.WriteLine($"Or wait {instantBlocksToWait} blocks and " +
                      $"pay {instant.feeSats} sats");
}
Javascript
const quote = await sdk.fetchClaimDepositQuote({
  txid: deposit.txid,
  vout: deposit.vout
})

// The standard claim, and how many blocks away it is.
const blocksToWait = Math.max(
  0,
  quote.mature.confirmationsRequired - quote.confirmations
)
console.log(`Wait ${blocksToWait} blocks and pay ${quote.mature.feeSats} sats`)

// An instant or expedited claim, when the provider offers one.
if (quote.instant != null) {
  const instantBlocksToWait = Math.max(
    0,
    quote.instant.confirmationsRequired - quote.confirmations
  )
  console.log(
    `Or wait ${instantBlocksToWait} blocks and ` +
    `pay ${quote.instant.feeSats} sats`
  )
}
React Native
const quote = await sdk.fetchClaimDepositQuote({
  txid: deposit.txid,
  vout: deposit.vout
})

// The standard claim, and how many blocks away it is.
const blocksToWait = Math.max(0, quote.mature.confirmationsRequired - quote.confirmations)
console.log(`Wait ${blocksToWait} blocks and pay ${quote.mature.feeSats} sats`)

// An instant or expedited claim, when the provider offers one.
const instant = quote.instant
if (instant != null) {
  const instantBlocks = Math.max(0, instant.confirmationsRequired - quote.confirmations)
  console.log(`Or wait ${instantBlocks} blocks and pay ${instant.feeSats} sats`)
}
Flutter
final request = FetchClaimDepositQuoteRequest(
  txid: deposit.txid,
  vout: deposit.vout,
);
final quote = await sdk.fetchClaimDepositQuote(request: request);

// The standard claim, and how many blocks away it is.
final confirmations = quote.confirmations;
final matureBlocks = quote.mature.confirmationsRequired > confirmations
    ? quote.mature.confirmationsRequired - confirmations
    : 0;
print("Wait $matureBlocks blocks and pay ${quote.mature.feeSats} sats");

// An instant or expedited claim, when the provider offers one.
final instant = quote.instant;
if (instant != null) {
  final instantBlocks = instant.confirmationsRequired > confirmations
      ? instant.confirmationsRequired - confirmations
      : 0;
  print("Or wait $instantBlocks blocks and pay ${instant.feeSats} sats");
}
Python
try:
    request = FetchClaimDepositQuoteRequest(txid=deposit.txid, vout=deposit.vout)
    quote = await sdk.fetch_claim_deposit_quote(request=request)

    # The standard claim, and how many blocks away it is.
    blocks_to_wait = max(
        0, quote.mature.confirmations_required - quote.confirmations
    )
    logging.info(
        f"Wait {blocks_to_wait} blocks and pay {quote.mature.fee_sats} sats"
    )

    # An instant or expedited claim, when the provider offers one.
    if quote.instant is not None:
        blocks_to_wait = max(
            0, quote.instant.confirmations_required - quote.confirmations
        )
        logging.info(
            f"Or wait {blocks_to_wait} blocks and pay {quote.instant.fee_sats} sats"
        )
except Exception as error:
    logging.error(error)
    raise
Go
quote, err := sdk.FetchClaimDepositQuote(breez_sdk_spark.FetchClaimDepositQuoteRequest{
	Txid: deposit.Txid,
	Vout: deposit.Vout,
})
if err != nil {
	return err
}

// The standard claim, and how many blocks away it is.
blocksToWait := uint32(0)
if quote.Mature.ConfirmationsRequired > quote.Confirmations {
	blocksToWait = quote.Mature.ConfirmationsRequired - quote.Confirmations
}
log.Printf("Wait %v blocks and pay %v sats", blocksToWait, quote.Mature.FeeSats)

// An instant or expedited claim, when the provider offers one.
if quote.Instant != nil {
	instantBlocks := uint32(0)
	if quote.Instant.ConfirmationsRequired > quote.Confirmations {
		instantBlocks = quote.Instant.ConfirmationsRequired - quote.Confirmations
	}
	log.Printf("Or wait %v blocks and pay %v sats", instantBlocks, quote.Instant.FeeSats)
}

Handling claim outcomes

claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit reports what it did as outcomeoutcomeoutcomeoutcomeoutcomeoutcomeoutcomeOutcomeOutcome, which is worth handling in full:

  • ClaimDepositOutcome::SettledClaimDepositOutcome.SETTLEDClaimDepositOutcome.settledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcomeSettledClaimDepositOutcome.Settled: a standard claim that settled, carrying the payment it produced.
  • ClaimDepositOutcome::SubmittedClaimDepositOutcome.SUBMITTEDClaimDepositOutcome.submittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcomeSubmittedClaimDepositOutcome.Submitted: an instant or expedited claim is settling asynchronously. Watch for the payment via list_paymentslist_paymentslistPaymentslistPaymentslistPaymentslistPaymentslistPaymentsListPaymentsListPayments or the payment events.
  • ClaimDepositOutcome::DeferredClaimDepositOutcome.DEFERREDClaimDepositOutcome.deferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcomeDeferredClaimDepositOutcome.Deferred: nothing was claimed yet, and no further call is needed.

Which outcome occurs follows from the deposit's depth and the max fee rather than from anything you ask for. A max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee below what an early claim costs returns ClaimDepositOutcome::DeferredClaimDepositOutcome.DEFERREDClaimDepositOutcome.deferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcomeDeferredClaimDepositOutcome.Deferred rather than failing. A deposit that has already reached the standard claim depth and whose claim exceeds the max fee is a different matter and returns SdkError::MaxDepositClaimFeeExceededSdkError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDEDSdkError.maxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkErrorMaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceeded, because nothing will claim it until the max fee rises or on-chain fees fall.

A deposit worth too little to claim returns SdkError::DepositTooSmallSdkError.DEPOSIT_TOO_SMALLSdkError.depositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkErrorDepositTooSmallSdkError.DepositTooSmall, from both claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit and fetch_claim_deposit_quotefetch_claim_deposit_quotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuoteFetchClaimDepositQuoteFetchClaimDepositQuote, and automatic claims record it as DepositClaimError::DepositTooSmallDepositClaimError.DEPOSIT_TOO_SMALLDepositClaimError.depositTooSmallDepositClaimError.DepositTooSmallDepositClaimError.DepositTooSmallDepositClaimError.DepositTooSmallDepositClaimError.DepositTooSmallDepositClaimErrorDepositTooSmallDepositClaimError.DepositTooSmall in claim_errorclaim_errorclaimErrorclaimErrorclaimErrorclaimErrorclaimErrorClaimErrorClaimError. This happens when the amount left after the claim fee would be below the dust limit. No max fee changes this, but the deposit can become claimable once on-chain fees fall.

Whether a deferred deposit actually waits for the standard claim depends on its reasonreasonreasonreasonreasonreasonreasonReasonReason. The SDK re-attempts an early claim as the deposit gains confirmations, so a claim declined at a depth the provider will not yet front is often claimed early a block or two later.

  • ClaimDeferredReason::NoEarlyClaimAvailableClaimDeferredReason.NO_EARLY_CLAIM_AVAILABLEClaimDeferredReason.noEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReasonNoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailable usually clears with the next confirmation.
  • ClaimDeferredReason::MaxFeeExceededClaimDeferredReason.MAX_FEE_EXCEEDEDClaimDeferredReason.maxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReasonMaxFeeExceededClaimDeferredReason.MaxFeeExceeded does not clear on its own. The deposit waits for the standard claim unless the max fee is raised.
  • ClaimDeferredReason::ProviderDeclinedClaimDeferredReason.PROVIDER_DECLINEDClaimDeferredReason.providerDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReasonProviderDeclinedClaimDeferredReason.ProviderDeclined means the provider refused or could not be reached, which another confirmation does not address.

Only the first is a wait you can put a time on, so showing a user "claimed in about 30 minutes" for the others would be wrong.

Refunding deposits

When a deposit cannot be claimed you can refund it to an external Bitcoin address. This creates a transaction that sends the amount, minus transaction fees, to the destination address.

A deposit that has already been claimed is not a candidate: its instant_claim_statusinstant_claim_statusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusInstantClaimStatusInstantClaimStatus is InstantClaimStatus::ClaimedInstantClaimStatus.CLAIMEDInstantClaimStatus.claimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatus.ClaimedInstantClaimStatusClaimedInstantClaimStatus.Claimed, so check that before offering a refund.

The recommended fees API is useful for choosing a fee for the refund transaction.

A deposit can only be refunded once it has enough confirmations. Calling refund_depositrefund_depositrefundDepositrefundDepositrefundDepositrefundDepositrefundDepositRefundDepositRefundDeposit earlier fails, reporting the deposit as unknown while it is unconfirmed and as having too few confirmations for a block or so after that. Nothing is signed or stored when this happens, so retry after a few more blocks.

Rust
let txid = "your_deposit_txid".to_string();
let vout = 0;
let destination_address = "bc1qexample...".to_string(); // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
let recommended_fees = sdk.recommended_fees().await?;
let fee = Fee::Rate {
    sat_per_vbyte: recommended_fees.half_hour_fee,
};
// or using a fixed amount
//let fee = Fee::Fixed { amount: 500 };
//

let request = RefundDepositRequest {
    txid,
    vout,
    destination_address,
    fee,
};

let response = sdk.refund_deposit(request).await?;
info!("Refund transaction created:");
info!("Transaction ID: {}", response.tx_id);
info!("Transaction hex: {}", response.tx_hex);
Swift
let txid = "your_deposit_txid"
let vout: UInt32 = 0
let destinationAddress = "bc1qexample..."  // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
let recommendedFees = try await sdk.recommendedFees()
let fee = Fee.rate(satPerVbyte: recommendedFees.halfHourFee)
// or using a fixed amount
//let fee = Fee.fixed(amount: 500) // 500 sats
//

let request = RefundDepositRequest(
    txid: txid,
    vout: vout,
    destinationAddress: destinationAddress,
    fee: fee
)

let response = try await sdk.refundDeposit(request: request)
print("Refund transaction created:")
print("Transaction ID: \(response.txId)")
print("Transaction hex: \(response.txHex)")
Kotlin
try {
    val txid = "your_deposit_txid"
    val vout = 0u
    val destinationAddress = "bc1qexample..." // Your Bitcoin address

    // Set the fee for the refund transaction using the half-hour feerate
    val recommendedFees = sdk.recommendedFees()
    val fee = Fee.Rate(recommendedFees.halfHourFee)
    // or using a fixed amount
    //val fee = Fee.Fixed(500u)
    //

    val request = RefundDepositRequest(
        txid = txid,
        vout = vout,
        destinationAddress = destinationAddress,
        fee = fee
    )

    val response = sdk.refundDeposit(request)
    // Log.v("Breez", "Refund transaction created:")
    // Log.v("Breez", "Transaction ID: ${response.txId}")
    // Log.v("Breez", "Transaction hex: ${response.txHex}")
} catch (e: Exception) {
    // handle error
}
C#
var txid = "your_deposit_txid";
var vout = 0U;
var destinationAddress = "bc1qexample...";  // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
var recommendedFees = await sdk.RecommendedFees();
var fee = new Fee.Rate(satPerVbyte: recommendedFees.halfHourFee);
// or using a fixed amount
//var fee = new Fee.Fixed(amount: 500);
//

var request = new RefundDepositRequest(
    txid: txid,
    vout: vout,
    destinationAddress: destinationAddress,
    fee: fee
);

var response = await sdk.RefundDeposit(request: request);
Console.WriteLine("Refund transaction created:");
Console.WriteLine($"Transaction ID: {response.txId}");
Console.WriteLine($"Transaction hex: {response.txHex}");
Javascript
const txid = 'your_deposit_txid'
const vout = 0
const destinationAddress = 'bc1qexample...' // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
const recommendedFees = await sdk.recommendedFees()
const fee: Fee = { type: 'rate', satPerVbyte: recommendedFees.halfHourFee }
// or using a fixed amount
// const fee: Fee = { type: 'fixed', amount: 500 }
//

const request: RefundDepositRequest = {
  txid,
  vout,
  destinationAddress,
  fee
}

const response = await sdk.refundDeposit(request)
console.log('Refund transaction created:')
console.log('Transaction ID:', response.txId)
console.log('Transaction hex:', response.txHex)
React Native
const txid = 'your_deposit_txid'
const vout = 0
const destinationAddress = 'bc1qexample...' // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
const recommendedFees = await sdk.recommendedFees()
const fee = new Fee.Rate({ satPerVbyte: recommendedFees.halfHourFee })
// or using a fixed amount
// const fee = new Fee.Fixed({ amount: BigInt(500) })
//

const request: RefundDepositRequest = {
  txid,
  vout,
  destinationAddress,
  fee
}

const response = await sdk.refundDeposit(request)
console.log('Refund transaction created:')
console.log('Transaction ID:', response.txId)
console.log('Transaction hex:', response.txHex)
Flutter
String txid = "your_deposit_txid";
int vout = 0;
String destinationAddress = "bc1qexample..."; // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
final recommendedFees = await sdk.recommendedFees();
Fee fee = Fee.rate(satPerVbyte: recommendedFees.halfHourFee);
// or using a fixed amount
//Fee fee = Fee.fixed(amount: BigInt.from(500));
//

final request = RefundDepositRequest(
  txid: txid,
  vout: vout,
  destinationAddress: destinationAddress,
  fee: fee,
);

final response = await sdk.refundDeposit(request: request);
print("Refund transaction created:");
print("Transaction ID: ${response.txId}");
print("Transaction hex: ${response.txHex}");
Python
try:
    txid = "your_deposit_txid"
    vout = 0
    destination_address = "bc1qexample..."  # Your Bitcoin address

    # Set the fee for the refund transaction using the half-hour feerate
    recommended_fees = await sdk.recommended_fees()
    fee = Fee.RATE(sat_per_vbyte=recommended_fees.half_hour_fee)
    # or using a fixed amount
    #fee = Fee.FIXED(amount=500)
    #

    request = RefundDepositRequest(
        txid=txid, vout=vout, destination_address=destination_address, fee=fee
    )

    response = await sdk.refund_deposit(request=request)
    logging.info("Refund transaction created:")
    logging.info(f"Transaction ID: {response.tx_id}")
    logging.info(f"Transaction hex: {response.tx_hex}")
except Exception as error:
    logging.error(error)
    raise
Go
txid := "<your_deposit_txid>"
vout := uint32(0)
destinationAddress := "bc1qexample..." // Your Bitcoin address

// Set the fee for the refund transaction using the half-hour feerate
recommendedFees, err := sdk.RecommendedFees()
if err != nil {
	return err
}
fee := breez_sdk_spark.Fee(breez_sdk_spark.FeeRate{SatPerVbyte: recommendedFees.HalfHourFee})
// or using a fixed amount
//fee := breez_sdk_spark.Fee(breez_sdk_spark.FeeFixed{Amount: 500})
//

request := breez_sdk_spark.RefundDepositRequest{
	Txid:               txid,
	Vout:               vout,
	DestinationAddress: destinationAddress,
	Fee:                fee,
}
response, err := sdk.RefundDeposit(request)

if err != nil {
	var sdkErr *breez_sdk_spark.SdkError
	if errors.As(err, &sdkErr) {
		// Handle SdkError - can inspect specific variants if needed
		// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
	}
	return err
}

log.Print("Refund transaction created:")
log.Printf("Transaction ID: %v", response.TxId)
log.Printf("Transaction hex: %v", response.TxHex)

Developer note

The total fee must cover at least 1 sat/vB of the refund transaction so it can be relayed by the Bitcoin network. The exact minimum depends on the size of the transaction, which varies with the destination address type: around 99 sats to a native segwit address and 111 sats to a taproot one. If the fee is lower, the refund request is rejected and the error states the required minimum.

Tracking a refund

refund_staterefund_staterefundStaterefundStaterefundStaterefundStaterefundStateRefundStateRefundState on DepositInfoDepositInfoDepositInfoDepositInfoDepositInfoDepositInfoDepositInfoDepositInfoDepositInfo reports how far the refund has got:

  • RefundState::BroadcastPendingRefundState.BROADCAST_PENDINGRefundState.broadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundStateBroadcastPendingRefundState.BroadcastPending: the refund is signed and stored but has not been seen on the network. The SDK rebroadcasts it on every sync until the deposit is spent, so a refund that failed to send because of a temporary network problem recovers on its own.
  • RefundState::BroadcastRefundState.BROADCASTRefundState.broadcastRefundState.BroadcastRefundState.BroadcastRefundState.BroadcastRefundState.BroadcastRefundStateBroadcastRefundState.Broadcast: the network has accepted the refund and it is waiting to confirm. The deposit disappears from list_unclaimed_depositslist_unclaimed_depositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositslistUnclaimedDepositsListUnclaimedDepositsListUnclaimedDeposits once it does.

A refund created near the 1 sat/vB minimum can stay at RefundState::BroadcastPendingRefundState.BROADCAST_PENDINGRefundState.broadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundState.BroadcastPendingRefundStateBroadcastPendingRefundState.BroadcastPending indefinitely if the network's minimum relay fee later rises above what it pays. Rebroadcasting cannot fix this, because the network keeps refusing the same transaction. Read last_errorlast_errorlastErrorlastErrorlastErrorlastErrorlastErrorLastErrorLastError for the reason the network gave, then call refund_depositrefund_depositrefundDepositrefundDepositrefundDepositrefundDepositrefundDepositRefundDepositRefundDeposit again at a higher fee to replace it.

Replacing a refund that is already on the network costs more than the original fee, because the replacement also pays to relay its own size. When the fee offered is too low, the call is rejected and the error states the minimum required.

Get Bitcoin fee estimates for different confirmation targets to help determine appropriate fee levels for claiming or refunding deposits.

Rust
let response = sdk.recommended_fees().await?;
info!("Fastest fee: {} sats/vByte", response.fastest_fee);
info!("Half-hour fee: {} sats/vByte", response.half_hour_fee);
info!("Hour fee: {} sats/vByte", response.hour_fee);
info!("Economy fee: {} sats/vByte", response.economy_fee);
info!("Minimum fee: {} sats/vByte", response.minimum_fee);
Swift
let response = try await sdk.recommendedFees()
print("Fastest fee: \(response.fastestFee) sats/vByte")
print("Half-hour fee: \(response.halfHourFee) sats/vByte")
print("Hour fee: \(response.hourFee) sats/vByte")
print("Economy fee: \(response.economyFee) sats/vByte")
print("Minimum fee: \(response.minimumFee) sats/vByte")
Kotlin
val response = sdk.recommendedFees()
println("Fastest fee: ${response.fastestFee} sats/vByte")
println("Half-hour fee: ${response.halfHourFee} sats/vByte")
println("Hour fee: ${response.hourFee} sats/vByte")
println("Economy fee: ${response.economyFee} sats/vByte")
println("Minimum fee: ${response.minimumFee} sats/vByte")
C#
var response = await sdk.RecommendedFees();
    Console.WriteLine($"Fastest fee: {response.fastestFee} sats/vByte");
    Console.WriteLine($"Half-hour fee: {response.halfHourFee} sats/vByte");
    Console.WriteLine($"Hour fee: {response.hourFee} sats/vByte");
    Console.WriteLine($"Economy fee: {response.economyFee} sats/vByte");
    Console.WriteLine($"Minimum fee: {response.minimumFee} sats/vByte");
}
Javascript
const response = await sdk.recommendedFees()
console.log('Fastest fee:', response.fastestFee, 'sats/vByte')
console.log('Half-hour fee:', response.halfHourFee, 'sats/vByte')
console.log('Hour fee:', response.hourFee, 'sats/vByte')
console.log('Economy fee:', response.economyFee, 'sats/vByte')
console.log('Minimum fee:', response.minimumFee, 'sats/vByte')
React Native
const response = await sdk.recommendedFees()
console.log('Fastest fee:', response.fastestFee, 'sats/vByte')
console.log('Half-hour fee:', response.halfHourFee, 'sats/vByte')
console.log('Hour fee:', response.hourFee, 'sats/vByte')
console.log('Economy fee:', response.economyFee, 'sats/vByte')
console.log('Minimum fee:', response.minimumFee, 'sats/vByte')
Flutter
final response = await sdk.recommendedFees();
print("Fastest fee: ${response.fastestFee} sats/vByte");
print("Half-hour fee: ${response.halfHourFee} sats/vByte");
print("Hour fee: ${response.hourFee} sats/vByte");
print("Economy fee: ${response.economyFee} sats/vByte");
print("Minimum fee: ${response.minimumFee} sats/vByte");
Python
response = await sdk.recommended_fees()
logging.info(f"Fastest fee: {response.fastest_fee} sats/vByte")
logging.info(f"Half-hour fee: {response.half_hour_fee} sats/vByte")
logging.info(f"Hour fee: {response.hour_fee} sats/vByte")
logging.info(f"Economy fee: {response.economy_fee} sats/vByte")
logging.info(f"Minimum fee: {response.minimum_fee} sats/vByte")
Go
response, err := sdk.RecommendedFees()
if err != nil {
	var sdkErr *breez_sdk_spark.SdkError
	if errors.As(err, &sdkErr) {
		// Handle SdkError - can inspect specific variants if needed
		// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
	}
	return err
}
log.Printf("Fastest fee: %v sats/vByte", response.FastestFee)
log.Printf("Half-hour fee: %v sats/vByte", response.HalfHourFee)
log.Printf("Hour fee: %v sats/vByte", response.HourFee)
log.Printf("Economy fee: %v sats/vByte", response.EconomyFee)
log.Printf("Minimum fee: %v sats/vByte", response.MinimumFee)