How-to

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.

Export accounting data

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 rows

The SDKs split a long range into windows and join them. Without one, call GET /accounting/export with 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

typeWhat it is
paymentA one-time payment.
billA subscription's bill.
refundA refund; the payment it returns in relatedTxUuid and relatedReference.
organizationFeeA marketplace's commission on a seller's payment (the organization's export only), with the seller's payment in full to reconcile it.
referralFeeWhat 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

The export already holds the on-chain split, as the chain made it.

Do

Reconcile to the smallest unit

grossAmount = platformFee + organizationFee + netAmount, exactly, and the same in USD.
  • Amounts are in the coin paid, exact: 7.5 USDC, not 7500000. currency names it as markets do (USDC, or a chain's own coin: ETH on BASE), and an amount × 10^currencyDecimals is its smallest unit on-chain.
  • Each amount also comes in USD (grossAmountUsd, netAmountUsd…), converted at currencyPriceUsd, the price at the payment's time. A refund is valued at its payment's.
  • A refund's net is what it sent back: netAmount equals its grossAmount, negative. No fee is refunded, so its fee columns are absent in JSON and empty cells in CSV.
  • Rates are percents, not fractions. platformFeePercent holds 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:

networkFeesPaidByWhenIn networkFeesCurrency
payerA payment or a bill: your customer paid them on top. Not your cost.The payment's own currency.
merchantA refund you sent. Book it as your cost.The chain's own coin.
organizationA 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.