Reports and exports
Pre-built projections for the numbers accountants ask for, and a streaming CSV export for the ones they ask for in bulk. Both tell you how current they are, because a stale report is not wrong — it is old, and only one of those is safe to act on.
On this page
Reports
| Type | Rows |
|---|---|
DEPOSITS | Credited deposits by period and asset — count, total amount. |
PAYOUTS | Settled payouts by period and asset — count, total amount, total fee. |
TREASURY | Positions by tier over time. |
FEES | Chain fees paid, by period and asset. The number to watch when sweep policy changes. |
SUMMARY | All of the above, one row per period. |
const report = await payments.reports.get('PAYOUTS', 'USDT'); report.builtThrough; // "2026-08-29T20:41:00Z" — or null if never built report.rows; // [{ periodBucket, assetCode, count, totalAmountMinor, … }]
Staleness is part of the answer
Always read
builtThrough. Projections are built by a worker loop from a cursor. If that loop stopped an hour ago, the numbers are an hour old and look exactly as authoritative as fresh ones. The field is returned on every report so a dashboard can show its own age — the operator console does, and a report older than its expected interval is worth an alert.A null here means the projection has never been built. That is different from "there was nothing to report", and the distinction matters the first time somebody concludes from an empty report that no payouts happened.
Exports
// Creation is idempotent on the job ID: a retried request does not // start a second export of the same range. const job = await payments.exports.create({ jobId: 'exp_2026_08', reportType: 'PAYOUTS', assetCode: 'USDT', fromTime: '2026-08-01T00:00:00Z', toTime: '2026-09-01T00:00:00Z' }); const status = await payments.exports.get('exp_2026_08'); status.status; // PENDING | RUNNING | COMPLETE | FAILED status.rowCount; // "184203" — a string // Download is a signed GET that streams. // GET /v1/exports/exp_2026_08/download
Why the download streams
The CSV is written to the socket row by row and never assembled in memory. A year of payouts for a busy operator is hundreds of megabytes; buffering that would mean an engine whose memory use is set by its largest customer's largest export.
A stream that fails mid-transfer destroys the connection rather than ending it cleanly. That is deliberate. A truncated CSV that arrives with a clean end looks complete, and a partial export loaded into an accounting system is worse than a failed one — the failure is visible, and the truncation is not.