> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flashnet.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Offramp

> Pay a Cash App username or Lightning address from a deposited asset

`POST /v1/orchestration/offramp` quotes an exact-in payout of BTC over Lightning to a Cash App username or a Lightning address. Funding the deposit address creates an order. Track it with the quote and status APIs.

`amount` is an integer string in the source asset's smallest units. `estimatedOut` is sats. The destination is `lightning:BTC`, and delivery is variable, so the quote does not lock a dollar amount.

<Note>
  To receive dollars, the Cash App user opens the Bitcoin section and turns on "Receive Lightning as dollars". Orchestra does not check that setting. `completed` means the Lightning payment was delivered. It does not confirm a Cash App dollar credit, the dollar amount, or Cash App's conversion fee. Other Lightning addresses receive BTC according to that wallet.
</Note>

## Prerequisites

* An Orchestra account. Create one in the [dashboard](https://orchestra.flashnet.xyz/dashboard). Flashnet reviews new accounts before enabling API access.
* A server key (`fn_...`) from **API keys** in the dashboard. Keep it on your backend. For browsers and apps, use a scoped client key (`fnp_...`); see [Authentication](/api/authentication).

## Flow

<Steps>
  <Step title="Create the quote">
    Send 50 USDC on Solana to `$username`:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://orchestration.flashnet.xyz/v1/orchestration/offramp \
        -H "Authorization: Bearer SERVER_KEY" \
        -H "X-Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d '{
          "sourceChain": "solana",
          "sourceAsset": "USDC",
          "amount": "50000000",
          "recipientAddress": "$username",
          "refundAddress": "YOUR_SOURCE_CHAIN_REFUND_ADDRESS"
        }'
      ```

      ```typescript TypeScript theme={null}
      const res = await fetch("https://orchestration.flashnet.xyz/v1/orchestration/offramp", {
        method: "POST",
        headers: {
          Authorization: "Bearer SERVER_KEY",
          "X-Idempotency-Key": crypto.randomUUID(),
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          sourceChain: "solana",
          sourceAsset: "USDC",
          amount: "50000000",
          recipientAddress: "$username",
          refundAddress: "YOUR_SOURCE_CHAIN_REFUND_ADDRESS",
        }),
      });
      const quote = await res.json();
      ```
    </CodeGroup>

    <Info>
      Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.

      `YOUR_SOURCE_CHAIN_REFUND_ADDRESS` is an address on the source chain. `$username` is the Cash App recipient.
    </Info>

    The response matches [Quotes](/orchestra/quotes): `quoteId`, `depositAddress`, `amountIn`, `estimatedOut`, fees, and `expiresAt`. `amountMode` is `exact_in`. The price lasts 2 minutes.
  </Step>

  <Step title="Fund the deposit address">
    Send `amountIn` of the source asset to `depositAddress` before `expiresAt`. Include `depositMemo` when the quote returns one. Orchestra detects the deposit and creates the order. If detection misses it, [submit the transaction](/orchestra/quotes#when-to-call-submit).
  </Step>

  <Step title="Track the order">
    Poll `GET /v1/orchestration/order?quoteId=QUOTE_ID` with a server key until `order` is non-null, then follow [Status](/orchestra/status). Client keys poll `/status?quoteId=QUOTE_ID` with the returned `readToken`. That token does not authorize an order-id SSE stream.
  </Step>
</Steps>

## Recipient

`alice` and `$alice` become `alice@cash.app`. The name, without `$`, is 1 to 20 letters or digits and must include a letter. Case is preserved. `alice@cash.app` and `alice@example.com` are kept; the domain needs a dot. Invoices and URLs are rejected, including BOLT11, BOLT12, and `https://cash.app/$alice`.

The quote checks that syntax and does not look up the wallet. Orchestra fetches the LNURL pay request at delivery, after the sats amount is final. A missing account, a rejected callback, or an amount outside that wallet's limits can fail after funding. Recovery uses `refundAddress`. See [Status](/orchestra/status).

## Request

`refundAddress` is required. Omit `refundChain` to refund on the source chain. `refundChain` may be `bitcoin` or `lightning`; a Lightning refund is an amountless BOLT11 invoice. TON, BNB, Tron, XRP, Litecoin, and Zcash refund on the source chain only.

The source asset must list `lightning:BTC` in `route.to` on `GET /v2/orchestration/routes`. Amount bounds are on [Routes and limits](/orchestra/routes-and-limits). Client keys need `orders:quote`, and the call uses the quote rate limit.

Optional fields match `/quote`: `refundChain`, `slippageBps`, `appFees`, `affiliateId`, and `affiliateIds`. Unknown fields are rejected, including `destinationChain`, `destinationAsset`, `amountMode`, and `deliveryMode`. The same idempotency key and body return the original quote. A different body returns `409 idempotency_conflict`.

Request and response fields: [Create offramp quote](/api-reference/orchestration/create-offramp-quote).
