Skip to main content
This flow starts with a BTC (or USDB) deposit and ends with USDC delivered to Base or Solana. If you want a reusable Bitcoin address that runs this flow automatically on each deposit, use Liquidation Addresses. Common uses:
  • Merchant settlement: accept BTC and deliver USDC to a Base or Solana treasury address.
  • Wallet sell flow: users sell BTC and receive USDC on their preferred chain.
  • Exact-out payments: deliver a precise USDC amount for invoices (Bitcoin L1 only).

Supported Routes

  • BTC (spark) -> USDC (base|solana)
  • BTC (bitcoin|lightning) -> USDC (base|solana)
  • BTC (bitcoin|lightning) -> USDB (spark)
  • USDB (spark) -> USDC (base|solana)
Exact-out payment mode is currently supported only on:
  • BTC (bitcoin) -> USDC (base|solana)
Affiliate fee settlement (appFees or affiliateId) is supported only when:
  • amountMode = exact_in
  • source or destination chain is base or solana
For BTC -> Stablecoin routes, this means app fees apply on USDC destination routes (destinationChain = base|solana) and are not used on BTC -> USDB (spark) routes.

Flow

  1. Create a quote: POST /v1/orchestration/quote.
  2. Initiate the source deposit to depositAddress.
  3. Submit the deposit transaction identifier: POST /v1/orchestration/submit.
  4. Track status via webhooks or GET /v1/orchestration/status?id=....
  5. If status becomes awaiting_approval, resolve with reprice/approve, reprice/reject, or reprice/refund.
Quotes expire 30 minutes after creation.

Example: BTC (Spark) to USDC (Base)

const BASE_URL = 'https://orchestration.flashnet.xyz';

const quote = await fetch(`${BASE_URL}/v1/orchestration/quote`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    'X-Idempotency-Key': `quote:${Date.now()}`,
  },
  body: JSON.stringify({
    sourceChain: 'spark',
    sourceAsset: 'BTC',
    destinationChain: 'base',
    destinationAsset: 'USDC',
    amount: '1000000',
    recipientAddress: '0xYourBaseUsdcAddress',
    slippageBps: 50,
  }),
}).then((r) => r.json());

// Send BTC on Spark to quote.depositAddress for quote.amountIn.
// Capture the Spark transfer id.
const sparkTransferId = await sendSparkBtc({
  toSparkAddress: quote.depositAddress,
  amountSats: quote.amountIn,
});

const submit = await fetch(`${BASE_URL}/v1/orchestration/submit`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    'X-Idempotency-Key': `submit:${quote.quoteId}:${sparkTransferId}`,
  },
  body: JSON.stringify({
    quoteId: quote.quoteId,
    sparkTxHash: sparkTransferId,
    // Optional but recommended. When present, the deposit verifier checks the sender Spark address.
    sourceSparkAddress: 'spark1YourSenderAddress',
  }),
}).then((r) => r.json());

const status = await fetch(
  `${BASE_URL}/v1/orchestration/status?id=${encodeURIComponent(submit.orderId)}`,
).then((r) => r.json());

console.log(status.order.status);

Example: BTC (Bitcoin L1) to USDC (Exact In)

A Bitcoin L1 quote returns a Bitcoin depositAddress. After you broadcast the transaction, submit the txid and the output index (vout) that paid the deposit address. Quote request:
{
  "sourceChain": "bitcoin",
  "sourceAsset": "BTC",
  "destinationChain": "solana",
  "destinationAsset": "USDC",
  "amount": "250000",
  "recipientAddress": "So1...",
  "slippageBps": 50,
  "zeroconfEnabled": true
}
Submit request:
{
  "quoteId": "q_...",
  "bitcoinTxid": "<txid>",
  "bitcoinVout": 0
}

Example: BTC (Bitcoin L1) to USDC (Exact Out Payment Intent)

Use exact-out when the destination must receive a precise USDC amount. Quote request:
{
  "sourceChain": "bitcoin",
  "sourceAsset": "BTC",
  "destinationChain": "base",
  "destinationAsset": "USDC",
  "amount": "100000000",
  "amountMode": "exact_out",
  "recipientAddress": "0xYourBaseUsdcAddress",
  "refundAddress": "bc1qyourrefundaddress...",
  "slippageBps": 0
}
Quote response adds:
  • targetAmountOut: requested destination amount
  • requiredAmountIn: minimum BTC input required
  • maxAcceptedAmountIn: highest BTC input auto-accepted
  • inputBufferBps: currently 2 when slippageBps=0, else 0
Deposit guidance:
  • Send requiredAmountIn for the cleanest path.
  • Keep actual input at or below maxAcceptedAmountIn.
  • If actual input is outside bounds, or target output cannot be met at current market conditions, the order moves to awaiting_approval.
Approval resolution endpoints:
  • POST /v1/orchestration/reprice/approve
  • POST /v1/orchestration/reprice/reject
  • POST /v1/orchestration/reprice/refund
reprice/refund moves the order to refunding, then terminal refunded after the refund is broadcast and recorded.

Example: BTC (Spark) to USDC (Solana) with Affiliate Fees

{
  "sourceChain": "spark",
  "sourceAsset": "BTC",
  "destinationChain": "solana",
  "destinationAsset": "USDC",
  "amount": "100000",
  "recipientAddress": "So1RecipientAddress...",
  "appFees": [
    {
      "recipient": "So1AffiliateOne...",
      "fee": 100
    },
    {
      "recipient": "So1AffiliateTwo...",
      "fee": 50
    }
  ]
}

Example: BTC (Spark) to USDC (Solana) with Affiliate Registry

{
  "sourceChain": "spark",
  "sourceAsset": "BTC",
  "destinationChain": "solana",
  "destinationAsset": "USDC",
  "amount": "100000",
  "recipientAddress": "So1RecipientAddress...",
  "affiliateId": "swapkit"
}
Quote response fields for affiliate-fee routes:
  • feeAmount: platform fee amount
  • totalFeeAmount: platform + app fees
  • appFeeAmount: sum of all app fee amounts
  • appFees: per-recipient fee breakdown in destination-chain smallest units (USDC)
Settlement behavior:
  • Affiliate fees are paid on the Base/Solana USDC destination payout side.
  • App fee transfers and recipient payout transfers are recorded separately in feePayouts.entries.
  • feePayouts.entries[*].affiliateId is present when the quote used affiliateId.
  • Multi-leg Bitcoin flows can include full, instant, and holdback payout legs.
  • No affiliate fee is deducted from BTC or Spark balances.
  • Status and webhooks include feePlan and feePayouts as payout legs are recorded.

ZeroConf Behavior (Bitcoin L1)

When zeroconfEnabled is true and the deployment is configured for ZeroConf, exact-in quotes can be accepted before confirmations. If ZeroConf cannot be used, or if you set zeroconfEnabled to false, the engine waits for 3 confirmations before proceeding. For some deposits, ZeroConf may process an instant leg and hold back the remainder until confirmations. The same order id continues and the holdback leg is delivered after confirmations. If the holdback remainder is below minimum swap size (dust), it is not swapped. Exact-out (amountMode=exact_out) on Bitcoin L1 uses confirmation-based processing and does not use ZeroConf.

Example: BTC (Lightning) to USDC

A Lightning quote returns a BOLT11 invoice in depositAddress and a lightningReceiveRequestId. Pay the invoice, then submit the receive request id. Quote response fields to pay:
  • depositAddress: BOLT11 invoice
  • amountIn: sats to pay
  • lightningReceiveRequestId: identifier used by submit
Submit request:
{
  "quoteId": "q_...",
  "lightningReceiveRequestId": "<id>"
}

Next Steps