Export for accounting
Every payment, bill, refund and fee of a period, in one CSV or JSON file that reconciles exactly, its network fees apart.
accounting-export.sh
# Every payment, bill, refund and fee between two dates. One request covers a limited window (a# longer one answers 400); format=csv answers CSV.curl "https://api.qbitflow.app/v2/accounting/export?from=2026-01-01&to=2026-03-31&format=json" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: { items: AccountingEvent[], hasMore: false } (format=csv: CSV text)// Every payment, bill, refund and fee between two dates (both included). Any range: the SDK// requests it in windows of at most 92 days, both included (here 4), and concatenates them.const rows = await client. accounting. exportJsonRange( '2026-01-01', '2026-12-31');for (const row of rows.slice(0, 5)) { console.log(row.type, row.txUuid, row.txTimeUtc, row.grossAmountUsd, row.productName);} // The same as CSV text, its header line once.const csv = await client. accounting. exportCsvRange( '2026-01-01', '2026-12-31');await writeFile('qbitflow-2026.csv', csv, { mode: 0o600 });# The API exports at most 92 days at a time (both included): the *_range methods split any# range into windows, request them in order and join the results (the CSV header once).events = client. accounting. export_json_range( "2026-01-01", "2026-12-31")for event in events: print(event.type, event.tx_uuid, event.tx_time_utc.date(), event.gross_amount_usd) csv_text = client. accounting. export_csv_range( "2026-01-01", "2026-12-31")with open("qbitflow-2026.csv", "w", encoding="utf-8") as file: file.write(csv_text)// The API exports at most 92 days at a time (both included): the …Range methods split any// range into windows, request them in order and join the results (the CSV header once).events, err := client. Accounting. ExportJSONRange( ctx, "2026-01-01", "2026-12-31")if err != nil { log.Fatal(err)}for _, e := range events { fmt.Println(e.Type, e.TxUUID, e. TxTimeUTC. Format( time. DateOnly), e.GrossAmountUSD, e.ProductName)} csv, err := client. Accounting. ExportCSVRange( ctx, "2026-01-01", "2026-12-31")if err != nil { log.Fatal(err)}if err := os. WriteFile( "qbitflow-2026.csv", []byte(csv), 0o600); err != nil { log.Fatal(err)}// The API exports at most 92 days at a time (both included): the range helpers split any range// into windows, request them in order and join the results (the CSV header once).$events = $client->accounting->exportJsonRange( '2026-01-01', '2026-12-31');foreach ($events as $event) { echo "{$event->type} {$event->txUuid} {$event->txTimeUtc->format('Y-m-d')} {$event->grossAmountUsd} USD {$event->productName}\n";} $csv = $client->accounting->exportCsvRange( '2026-01-01', '2026-12-31'); // one header line, then the rowsThe SDKs split a long range into windows and join them. Without one, call
GET /accounting/from and to (YYYY-MM-DD, at most
92 days apart) and format (csv or json). The dashboard exports the same file
from Payments.
The days are the transactions', in UTC, both included: an event is in the export of the day its
block was mined (txTimeUtc), so back-to-back exports never miss or repeat one. Rows come in the
same order every time: by txTimeUtc, then type, then txUuid.
One row per event
type | What it is |
|---|---|
payment | A one-time payment. |
bill | A subscription's bill. |
refund | A refund; the payment it returns in relatedTxUuid and relatedReference. |
organizationFee | A marketplace's commission on a seller's payment (the organization's export only), with the seller's payment in full to reconcile it. |
referralFee | What your referral earned on another organization's payment: when, on which chain, how much, and nothing about that payment. |
The amounts reconcile exactly
Avoid
Recompute the split from the rates
Do
Reconcile to the smallest unit
grossAmount = platformFee + organizationFee + netAmount, exactly, and the same in USD.- Amounts are in the coin paid, exact:
7.5USDC, not7500000.currencynames it as markets do (USDC, or a chain's own coin:ETHonBASE), and an amount × 10^currencyDecimalsis its smallest unit on-chain. - Each amount also comes in USD (
grossAmountUsd,netAmountUsd…), converted atcurrencyPriceUsd, the price at the payment's time. A refund is valued at its payment's. - A refund's net is what it sent back:
netAmountequals itsgrossAmount, negative. No fee is refunded, so its fee columns are absent in JSON and empty cells in CSV. - Rates are percents, not fractions.
platformFeePercentholds the rate QBitFlow took on that payment as the number before its % sign, in JSON and CSV alike. - Every row links its transaction (
txHash,explorerUrl) and its customer's receipt (receiptUrl).
Network fees, apart from the net
The transaction's network fees have their own columns, right after netAmount, and are never in
it. networkFeesPaidBy says whose wallet paid them:
networkFeesPaidBy | When | In networkFeesCurrency |
|---|---|---|
payer | A payment or a bill: your customer paid them on top. Not your cost. | The payment's own currency. |
merchant | A refund you sent. Book it as your cost. | The chain's own coin. |
organization | A refund of a payment the organization held for its seller, sent from its wallet. In the seller's export. | The chain's own coin. |
networkFees is in networkFeesCurrency's units, networkFeesUsd at
networkFeesCurrencyPriceUsd. An old refund recorded before its fees were has those cells empty,
never 0.
CSV, safely
- The header row is always there, even for an empty period (JSON:
{ "items": [], "hasMore": false }). - Read a column by its header name, not its position.
- A cell starting with
=,+,-or@gets a leading quote: a spreadsheet shows it, never runs it. Numbers stay numbers. - An optional field without a value is an empty cell in CSV and absent from JSON, never
0.