> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction Reports

> Retrieve your tenant transaction reports, understand each field, and reconcile them against your invoice

Use the [Reports API](/reference/reports/get-a-tenants-report) to retrieve your tenant's daily report of card transactions from the network. You can use this report to reconcile user balances and verify collateral liquidations, and the [monthly report](/reference/reports/get-a-tenants-monthly-report) to reconcile your invoice.

<Warning>
  The daily and monthly report endpoints are currently functional only in production. Requests in sandbox do not return report data.
</Warning>

## When Reports Are Available

The settlement cutoff is **12:00 PM UTC** each day. Daily reports are generated between approximately **3:00 PM – 3:30 PM EST** and include all transactions posted since the previous day's cutoff.

For example, a report generated in the afternoon today contains transactions posted from 12:00 PM UTC yesterday through 12:00 PM UTC today.

## What the Report Includes

The report contains `spend` transactions — both **settled** and **not-yet-settled** — plus `late_settlement_adjustment` rows (settlement adjustments that post after the original transaction's billing period). Other transaction types such as fees or collateral deposits are not included.

A transaction that hasn't settled yet appears with `ledgerSettlementAmount` and `interchangeAmount` of `0`. Once it settles, those fields populate and its `transactionDate` moves from the posting date to the settlement date.

## Field Descriptions

<table>
  <thead>
    <tr>
      <th style={{ whiteSpace: 'nowrap' }}>Column</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>transactionId</code></td><td>Transaction ID</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>transactionDate</code></td><td>Settlement-grain timestamp (ISO format) — the ledger book-entry time, falling back to the posting time. Use this column when reconciling <a href="#volume-figures">volume figures</a>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>transactionType</code></td><td><code>spend</code> for card spend, or <code>late\_settlement\_adjustment</code> for a settlement adjustment posted after the original transaction's billing period</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>status</code></td><td>Authorization status</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>merchantName</code></td><td>Merchant name</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>merchantCategoryCode</code></td><td>MCC (defaults to <code>"0000"</code> if empty)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>authorizationAmount</code></td><td>Original authorization amount (in minor units)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>authorizationCurrency</code></td><td>Currency of the authorization</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>settlementAmount</code></td><td>Final settled amount (in minor units)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>settlementCurrency</code></td><td>Currency of the settlement (falls back to auth currency)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>interchangeAmount</code></td><td>Raw interchange fee amount for this transaction, scaled by 10^6 (divide by 1,000,000 to get the actual value). This is the per-transaction interchange only. A separate process aggregates all interchange amounts, deducts Rain's share, adds applicable FX fees, and calculates the total amount owed to the tenant — that aggregated data is not available at the per-transaction level in this report.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>cardId</code></td><td>Card ID</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>cardLast4</code></td><td>Last 4 digits of the card</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>cardholder</code></td><td>Full name (from cardholder user or fallback user)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>isThreeDSecureTransaction</code></td><td>Whether 3DS was used</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>isATMTransaction</code></td><td>Whether the MCC indicates an ATM withdrawal</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>isATMPremiumTransaction</code></td><td>Whether the ATM amount exceeds the tenant's premium threshold</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>visaProductType</code></td><td>Card product type name (from BIN range)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>tokenRequestorId</code></td><td>Raw token requestor ID</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>walletName</code></td><td>Resolved wallet name (<code>ApplePay</code>, <code>GooglePay</code>, <code>SamsungPay</code>, <code>GarminPay</code>, <code>FitbitPay</code>, <code>FidesmoPay</code>, or <code>null</code>)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>isNetworkChipAuthentication</code></td><td>Whether it was a physical chip-authenticated transaction</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>isInternationalTransaction</code></td><td>Whether it was an international transaction</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>foreignExchangeFees</code></td><td>The international fee amount charged when <code>sourceCurrencyCode</code> is not USD. Only one of <code>foreignExchangeFees</code> or <code>crossBorderFees</code> can be non-zero per transaction. Defaults to <code>"0"</code>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>crossBorderFees</code></td><td>The international fee amount charged when <code>merchantCountryCode</code> is not US or PR. Only one of <code>foreignExchangeFees</code> or <code>crossBorderFees</code> can be non-zero per transaction. Defaults to <code>"0"</code>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>merchantCountryCode</code></td><td>The merchant's country code (may be empty if not available)</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>ledgerSettlementAmount</code></td><td>Ledger-net settled volume for this transaction (in minor units) — the amount that actually moved in the ledger. <code>0</code> when no posted ledger entry matches (for example, auth-only rows or dispute reimbursements). Sum this column to reconcile <a href="#volume-figures">invoice volume</a>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>postedAt</code></td><td>Timestamp the transaction posted to the network (ISO format). Equals <code>transactionDate</code> for nearly all rows; the two diverge only when a settlement's ledger entry lands in a different period than the posting (for example, delayed dispute resolutions). Use this column when reconciling <a href="#transaction-counts">transaction counts</a>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>tenantId</code></td><td>The tenant the transaction is stamped to. Use this column when reconciling <a href="#transaction-counts">transaction counts</a>.</td></tr>
    <tr><td style={{ whiteSpace: 'nowrap' }}><code>billingTenantId</code></td><td>The tenant whose invoice this transaction's volume rolls up to. Empty when the volume is not billed to any tenant. Use this column when reconciling <a href="#volume-figures">volume figures</a>.</td></tr>
  </tbody>
</table>

## Daily vs. Monthly Reports

Daily and the [monthly report](/reference/reports/get-a-tenants-monthly-report) are produced the same way: a report includes every row whose `transactionDate` **or** `postedAt` falls in the report's window. The windows differ: a daily report covers a single day (ending at the settlement cutoff above), while the monthly report covers the **full calendar month** — 00:00 UTC on the 1st through 00:00 UTC on the 1st of the next month — to align with your invoice period.

Reports are **not deduplicated by transaction**, so the same transaction can appear more than once. Across daily files it shows up on its posting day (unsettled, `ledgerSettlementAmount` of `0`) and again on its settlement day, because `transactionDate` moves to the settlement date once it settles. Within a single report it can also span more than one row when a settlement straddles a period boundary.

Because the daily and monthly windows use different cutoffs, the monthly report is **not** the sum of that month's daily reports. Reconcile against the monthly report using the aggregations below — `SUM(...)` and `COUNT(DISTINCT transactionId)` both handle repeated rows correctly.

## Reconciling Your Invoice

<Warning>
  Run these queries against the **monthly report** (a single full-month window). Reports are not deduplicated by transaction, so always use `COUNT(DISTINCT transactionId)` for counts and `SUM(...)` for volume — both handle transactions that appear in more than one row.
</Warning>

Your invoice is derived from the same settlement data as this report, so every figure on it can be reproduced from the report rows. Reconciliation splits into two grains — **volume** and **counts** — and each uses a different tenant column and a different date column.

Your report contains every row where either `tenantId` **or** `billingTenantId` matches your tenant, so filter to the column that applies to each grain. The examples below assume the report (CSV or JSON) is loaded into a table named `report`.

### Volume figures

Filter rows to `billingTenantId = <your tenant id>` and `transactionDate` within the invoice period, then sum:

| Invoice figure           | Report calculation            |
| ------------------------ | ----------------------------- |
| `totalTransactionVolume` | `SUM(ledgerSettlementAmount)` |
| `totalInterchange`       | `SUM(interchangeAmount)`      |

```sql theme={null}
SELECT SUM(CAST(ledgerSettlementAmount AS INT64)) AS totalTransactionVolume,
       SUM(CAST(interchangeAmount      AS INT64)) AS totalInterchange
FROM report
WHERE billingTenantId = '<TENANT>'
  AND transactionDate >= '2026-05-01' AND transactionDate < '2026-06-01';
```

Amounts are strings in minor units, so cast before summing. `interchangeAmount` is additionally scaled by 10^6 (divide by 1,000,000 for the dollar value), so `totalInterchange` carries that same scale.

### Transaction counts

Filter rows to `tenantId = <your tenant id>` and `postedAt` within the invoice period, then count distinct transactions:

| Invoice figure      | Counts rows where                      |
| ------------------- | -------------------------------------- |
| `totalTransactions` | `transactionType = 'spend'`            |
| `threeDS`           | `isThreeDSecureTransaction` is `true`  |
| `international`     | `isInternationalTransaction` is `true` |
| `applePay`          | `walletName = 'ApplePay'`              |
| `googlePay`         | `walletName = 'GooglePay'`             |

```sql theme={null}
SELECT COUNT(DISTINCT IF(transactionType = 'spend', transactionId, NULL))   AS totalTransactions,
       COUNT(DISTINCT IF(isThreeDSecureTransaction, transactionId, NULL))   AS threeDS,
       COUNT(DISTINCT IF(isInternationalTransaction, transactionId, NULL))  AS international,
       COUNT(DISTINCT IF(walletName = 'ApplePay', transactionId, NULL))     AS applePay,
       COUNT(DISTINCT IF(walletName = 'GooglePay', transactionId, NULL))    AS googlePay
FROM report
WHERE tenantId = '<TENANT>'
  AND postedAt >= '2026-05-01' AND postedAt < '2026-06-01';
```

Boolean columns are encoded as the strings `true`/`false` in the CSV; compare to `'true'` (a quoted string literal) if your warehouse loads them as text rather than booleans.

### Why volume and counts use different columns

Volume is attributed to the tenant your settlement is **billed** to (`billingTenantId`) on the **settlement-grain** date (`transactionDate`), matching how the invoice rolls up charges. Counts use the **raw tenant stamp** (`tenantId`) on the **posting** date (`postedAt`), matching how the invoice tallies activity. Using the wrong column pair for a grain will not tie out.

A row can have `tenantId` set but `billingTenantId` empty — "settled, but not billed." These rows count toward transaction totals but not toward billed volume.
