This guide covers how to distribute Bitcoin rewards to your customers, including quote creation, execution options, and tracking delivery.
Overview
Distributing Bitcoin rewards involves creating a quote that converts your fiat balance (typically USD) to Bitcoin and sends it to the customer’s wallet. You have flexibility in how you lock amounts, register destinations, and execute transfers.
Basic Flow
- Create a quote - Specify source account, destination, and amount
- Execute the quote - Either immediately or after review
- Monitor completion - Track via webhooks or polling
In this guide, we’ll use the platform’s USD internal account as the funding source for the rewards.
You can find your platform’s internal account by listing all internal accounts for your platform.
curl -X GET "https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Response:
{
"data": [
{
"id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
"balance": {
"amount": 10000,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"fundingPaymentInstructions": [...],
"createdAt": "2025-10-03T12:00:00Z",
"updatedAt": "2025-10-03T12:00:00Z"
}
]
}
Creating a Quote
The core request specifies your platform’s internal account as the source and the customer’s wallet as the destination.
curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"source": {
"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
},
"destination": {
"externalAccountDetails": {
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"currency": "BTC",
"accountInfo": {
"accountType": "SPARK_WALLET",
"address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
}
}
},
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 100,
"immediatelyExecute": true,
"description": "Weekly reward payout"
}'
Response:
{
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020",
"status": "PROCESSING",
"createdAt": "2025-10-03T15:00:00Z",
"expiresAt": "2025-10-03T15:05:00Z",
"source": {
"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
"currency": "USD"
},
"destination": {
"accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456",
"currency": "BTC"
},
"sendingCurrency": {
"code": "USD",
"decimals": 2
},
"receivingCurrency": {
"code": "BTC",
"decimals": 8
},
"totalSendingAmount": 100,
"totalReceivingAmount": 810,
"exchangeRate": 8.1,
"feesIncluded": 5,
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025"
}
Locking Amount: Sending vs. Receiving
When creating a quote, you can choose to either lock the amount you’re sending (fiat) or the amount the customer receives (Bitcoin).
Lock Sending Amount
Use this when you want to send a fixed dollar amount (e.g., $1.00 reward).
{
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 100 // $1.00 in cents
}
The customer receives whatever Bitcoin this amount buys at the current rate.
Lock Receiving Amount
Use this when you want the receiver to receive a specific Bitcoin amount (e.g., 1000 sats).
{
"lockedCurrencySide": "RECEIVING",
"lockedCurrencyAmount": 1000 // 1000 satoshis
}
Your platform account is debited whatever fiat amount is needed to send that Bitcoin amount.
For consistent dollar-value rewards, use SENDING
. For consistent
Bitcoin-value rewards, use RECEIVING
.
Execution Options
Set immediatelyExecute: true
to create and execute the quote in one step. This is ideal for automated reward distribution where you accept the current market rate.
{
"immediatelyExecute": true
}
The quote is created and executed immediately. You receive a transactionId
in the response.
Two-Step Execution (Review Before Sending)
Omit immediatelyExecute
or set it to false
to review the quote before executing.
{
"immediatelyExecute": false // or omit this field
}
The response will be the same as the immediate execution response, but the status will be PENDING
and you’ll have until the quote’s expiresAt
timestamp to execute the quote.
After reviewing the quote’s exchange rate and fees, execute it:
curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Quotes expire after a short time (typically 5 minutes). You must execute
before expiration.
Destination Options
Inline External Account Creation
Use externalAccountDetails
to create the destination wallet on the fly. This is perfect for one-time or infrequent payouts.
{
"destination": {
"externalAccountDetails": {
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"currency": "BTC",
"accountInfo": {
"accountType": "SPARK_WALLET",
"address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
}
}
}
}
The external account is created automatically and returned in the quote response.
Pre-Registered External Account
If you’ve already registered the external account, reference it by ID. This is more efficient for recurring rewards to the same wallets.
First, create the external account:
curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \
-H "Authorization: Basic $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 reference it in /quotes
:
{
"destination": {
"accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456"
}
}
Pre-register external accounts for customers who receive regular rewards. This
avoids duplicate account creation and improves performance.
Tracking Delivery
Webhook Notification
When the Bitcoin transfer completes, you’ll receive a webhook:
{
"transaction": {
"id": "Transaction:019542f5-b3e7-1d02-0000-000000000025",
"status": "COMPLETED",
"type": "OUTGOING",
"sentAmount": {
"amount": 100,
"currency": { "code": "USD" }
},
"receivedAmount": {
"amount": 810,
"currency": { "code": "BTC" }
},
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"settledAt": "2025-10-03T15:01:45Z"
},
"type": "OUTGOING_PAYMENT"
}
Polling Status
Alternatively, poll the quote or transaction endpoint:
curl -X GET "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Or:
curl -X GET "https://api.lightspark.com/grid/2025-10-13/transactions/{transactionId}" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Common Patterns
Fixed Dollar Rewards (e.g., $1.00 per action)
{
"source": { "accountId": "InternalAccount:..." },
"destination": { "externalAccountDetails": { ... } },
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 100,
"immediatelyExecute": true
}
Fixed Satoshi Rewards (e.g., 1000 sats per action)
{
"source": { "accountId": "InternalAccount:..." },
"destination": { "accountId": "ExternalAccount:..." },
"lockedCurrencySide": "RECEIVING",
"lockedCurrencyAmount": 1000,
"immediatelyExecute": true
}
Review Before Sending
{
"source": { "accountId": "InternalAccount:..." },
"destination": { "accountId": "ExternalAccount:..." },
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 5000,
"immediatelyExecute": false
}
Then review the exchange rate and fees, and execute:
curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Best Practices
Use immediatelyExecute: true
for automated, small-dollar rewards where you
accept market rates.
Pre-register external accounts for customers receiving recurring rewards to
improve performance.
Set up webhook handlers to track completion status and update your reward
records.
Ensure your platform’s internal account has sufficient balance before
distributing rewards. Monitor balances via the /platform/internal-accounts
endpoint or account status webhooks.