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

# Fiat-to-Crypto and Crypto-to-Fiat

> Build on-ramp and off-ramp flows to convert between fiat currencies and cryptocurrencies

## Overview

Grid enables seamless conversion between fiat currencies and cryptocurrencies via the Lightning Network. Use quotes to lock exchange rates and get payment instructions for completing transfers.

**On-ramp (Fiat → Crypto):** User sends fiat → Grid detects payment → Crypto sent to wallet

**Off-ramp (Crypto → Fiat):** Execute quote → Grid processes crypto → Fiat sent to bank

## Prerequisites

* Customer created in Grid
* **On-ramps:** Destination crypto wallet (Spark address) + webhook endpoint
* **Off-ramps:** Internal account with crypto + external bank account registered

## On-ramp: Fiat to crypto

### Create a quote

First, create an external account for the crypto destination:

```bash cURL theme={null}
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
    "currency": "BTC",
    "accountInfo": {
      "accountType": "SPARK_WALLET",
      "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
    }
  }'
```

Then create a quote using the external account ID:

```bash cURL theme={null}
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H 'Content-Type: application/json' \
  -d '{
    "source": {
      "sourceType": "REALTIME_FUNDING",
      "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
      "currency": "USD"
    },
    "destination": {
      "destinationType": "ACCOUNT",
      "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456"
    },
    "lockedCurrencySide": "RECEIVING",
    "lockedCurrencyAmount": 100000,
    "description": "Buy 0.001 BTC"
  }'
```

**Response includes payment instructions:**

```json theme={null}
{
  "id": "Quote:019542f5-b3e7-1d02-0000-000000000006",
  "status": "PENDING",
  "createdAt": "2025-10-03T12:00:00Z",
  "expiresAt": "2025-10-03T12:05:00Z",
  "source": {
    "sourceType": "REALTIME_FUNDING",
    "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
    "currency": "USD"
  },
  "destination": {
    "destinationType": "ACCOUNT",
    "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456"
  },
  "sendingCurrency": { "code": "USD", "name": "United States Dollar", "symbol": "$", "decimals": 2 },
  "receivingCurrency": { "code": "BTC", "name": "Bitcoin", "symbol": "₿", "decimals": 8 },
  "totalSendingAmount": 6500,
  "totalReceivingAmount": 100000,
  "exchangeRate": 15.38,
  "feesIncluded": 200,
  "platformFeesIncluded": 0,
  "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000007",
  "paymentInstructions": [
    {
      "accountOrWalletInfo": {
        "accountType": "USD_ACCOUNT",
        "reference": "UMA-Q12345-REF",
        "accountNumber": "1234567890",
        "routingNumber": "021000021",
        "bankName": "Grid Settlement Bank"
      }
    },
    {
      "accountOrWalletInfo": {
        "accountType": "SOLANA_WALLET",
        "assetType": "USDC",
        "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg"
      }
    }
  ]
}
```

### Display payment instructions

```javascript theme={null}
function displayPaymentInstructions(quote) {
  // EU customers only: an SCA-gated realtime-funding quote comes back
  // PENDING_AUTHORIZATION with paymentInstructions withheld until its
  // scaChallenge is authorized (see "Strong Customer Authentication" below).
  // Authorize first, then display — don't read paymentInstructions while pending.
  if (quote.status === "PENDING_AUTHORIZATION") {
    return { pendingAuthorization: true, scaChallenge: quote.scaChallenge };
  }

  const instructions = quote.paymentInstructions[0];

  return {
    amount: `$${(quote.totalSendingAmount / 100).toFixed(2)}`,
    bankName: instructions.accountOrWalletInfo.bankName,
    accountNumber: instructions.accountOrWalletInfo.accountNumber,
    routingNumber: instructions.accountOrWalletInfo.routingNumber,
    referenceCode: instructions.accountOrWalletInfo.reference, // User must include this
    expiresAt: quote.expiresAt,
    willReceive: `${quote.totalReceivingAmount / 100000000} BTC`,
  };
}
```

### Monitor completion

Grid sends `OUTGOING_PAYMENT.<STATUS>` webhooks as the transaction progresses:

```javascript theme={null}
app.post("/webhooks/grid", async (req, res) => {
  const { type, data } = req.body;

  if (type === "OUTGOING_PAYMENT.COMPLETED") {
    await notifyUser(data.customerId, {
      message: "Your Bitcoin purchase is complete!",
      amount: `${data.receivedAmount.amount / 100000000} BTC`,
    });
  } else if (type === "OUTGOING_PAYMENT.FAILED") {
    await notifyUser(data.customerId, {
      message: `Purchase failed: ${data.failureReason}`,
    });
    // Refund webhook (OUTGOING_PAYMENT.REFUND_*) will follow
  }

  res.status(200).json({ received: true });
});
```

See the [Transaction Lifecycle](https://agents-docs.lightspark.com/platform-overview/core-concepts/transaction-lifecycle.md) guide for all status transitions and refund handling.

## Off-ramp: Crypto to fiat

### Create and execute a quote

```javascript theme={null}
// 1. Create quote
const quote = await fetch("https://api.lightspark.com/grid/2025-10-13/quotes", {
  method: "POST",
  body: JSON.stringify({
    source: {
      sourceType: "ACCOUNT",
      accountId: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
    },
    destination: {
      destinationType: "ACCOUNT",
      accountId: "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
    },
    lockedCurrencySide: "SENDING",
    lockedCurrencyAmount: 100000, // 0.001 BTC
    description: "Sell 0.001 BTC",
  }),
}).then((r) => r.json());

// 2. Execute quote
const result = await fetch(
  `https://api.lightspark.com/grid/2025-10-13/quotes/${quote.id}/execute`,
  {
    method: "POST",
    headers: { Authorization: `Basic ${credentials}` },
  }
).then((r) => r.json());
```

### Track completion

```javascript theme={null}
app.post("/webhooks/grid", async (req, res) => {
  const { type, data } = req.body;

  if (type === "OUTGOING_PAYMENT.COMPLETED") {
    await notifyUser(data.customerId, {
      message: "Your USD withdrawal is complete!",
      amount: `$${data.receivedAmount.amount / 100}`,
    });
  } else if (type === "OUTGOING_PAYMENT.FAILED") {
    await notifyUser(data.customerId, {
      message: `Withdrawal failed: ${data.failureReason}`,
    });
  }

  res.status(200).json({ received: true });
});
```

## Immediate execution

For instant on-ramps (e.g., reward payouts), use `immediatelyExecute: true` with an internal account source:

```bash cURL theme={null}
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H 'Content-Type: application/json' \
  -d '{
    "source": {
      "sourceType": "ACCOUNT",
      "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
    },
    "destination": {
      "destinationType": "ACCOUNT",
      "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456"
    },
    "lockedCurrencySide": "RECEIVING",
    "lockedCurrencyAmount": 100000,
    "immediatelyExecute": true
  }'
```

> [!NOTE]
>
> `immediatelyExecute` can only be used with sources that are either internal accounts or external accounts with direct pull functionality (e.g., ACH pull).

## Strong Customer Authentication (EU customers)

Customers in SCA-regulated regions (in practice the EU: EUR / USDC) must confirm
money movements with Strong Customer Authentication, and a conversion out of
EUR / USDC is gated on its swap leg. When it applies, the quote comes back
`PENDING_AUTHORIZATION` carrying an `scaChallenge` that you authorize before the
conversion is released; for every other customer nothing changes. See
[Per-transaction authorization](https://agents-docs.lightspark.com/platform-overview/sca/per-transaction-authorization.md)
for the full walkthrough.

## Best practices

> [accordion] Handle quote expiration
>
> ```javascript theme={null}
> async function refreshQuoteIfNeeded(quote, quoteRequest) {
>   const expiresAt = new Date(quote.expiresAt);
>   const now = new Date();
>
>   if (expiresAt - now < 60000) {
>     // Less than 1 minute left
>     return await createNewQuote(quoteRequest);
>   }
>
>   return quote;
> }
> ```

> [accordion] Settlement times by account type
>
> ```javascript theme={null}
> const settlementTimes = {
>   USD_ACCOUNT: "1-3 business days (ACH)",
>   EUR_ACCOUNT: "Seconds to 1 business day (SEPA Instant / SEPA)",
>   BRL_ACCOUNT: "Instant (PIX)",
>   SWIFT_ACCOUNT: "1-3 business days",
>   SPARK_WALLET: "Instant",
> };
> ```

> [accordion] Handle failed transactions
>
> ```javascript theme={null}
> // quoteRequest is the request your integration sent to create the original
> // quote: a transaction does not carry the parameters it was quoted from.
> async function handleFailedTransaction(transaction, quoteRequest) {
>   if (transaction.status !== "FAILED") return;
>
>   await notifyUser(transaction.customerId, {
>     message: "Transaction failed",
>     reason: transaction.failureReason,
>     action: "retry",
>   });
>
>   if (
>     transaction.failureReason === "QUOTE_EXPIRED" ||
>     transaction.failureReason === "QUOTE_RATE_UNAVAILABLE"
>   ) {
>     await createNewQuote(quoteRequest);
>   }
> }
> ```

## Next steps

![](https://agents-docs.lightspark.com/images/icons/wallet1.svg)

Self-custody wallets

Send crypto to user-controlled wallets

[Open](https://agents-docs.lightspark.com/ramps/conversion-flows/self-custody-wallets.md)

![](https://agents-docs.lightspark.com/images/icons/bell.svg)

Webhooks

Monitor transaction status

[Open](https://agents-docs.lightspark.com/ramps/platform-tools/webhooks.md)

![](https://agents-docs.lightspark.com/images/icons/code.svg)

API Reference

Complete API documentation

[Open](https://agents-docs.lightspark.com/api-reference)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.