> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a monthly statement

> Read what an account held at the start of a period, every ledger entry that moved it, and what it held at the end. The three reconcile.

A statement answers one question about one account and one asset: what did it hold, what moved, and what does it hold now. Conduit reads it from the ledger when you ask. Nothing is stored, so a statement for a past month reads the same today as it will next year.

<Note>
  **Test this flow in sandbox.** Simulate a deposit and a payout on the same
  account, wait for the day to close, then read the statement. See [deposit
  simulation](/sandbox/deposits) and [withdrawals](/sandbox/withdrawals).
</Note>

```bash theme={null}
curl -G https://api.conduit.financial/v2/customers/cus_.../virtual-accounts/vac_.../statement \
  -H "x-api-key: YOUR_API_KEY" \
  -d from=2026-08-01 \
  -d to=2026-08-31
```

A crypto wallet holds several assets, so its statement names the one you want:

```bash theme={null}
curl -G https://api.conduit.financial/v2/customers/cus_.../wallets/wlt_.../statement \
  -H "x-api-key: YOUR_API_KEY" \
  -d asset=USDC \
  -d chain=ethereum \
  -d from=2026-08-01 \
  -d to=2026-08-31
```

Give `chain` as the chain the wallet is on. A wallet lives on one chain. Any other chain returns `422 WALLET_CHAIN_MISMATCH` instead of a statement for an account the wallet does not hold.

## The period

`from` and `to` are UTC days, and both are included. The period starts at `from` 00:00:00.000Z and ends at the instant `to` ends, which is the next day's 00:00:00.000Z. An entry posted at that exact instant belongs to the next period. So the period `2026-08-01` to `2026-08-31` and the period `2026-09-01` to `2026-09-30` never count the same entry twice and never lose one between them.

Three rules, each a `400 VALIDATION_ERROR`:

* `to` must be a day that has ended. Today is refused because it has not ended.
* `from` must be `to` or earlier.
* The period holds at most 92 days.

## What reconciles

Per bucket, over the whole period:

```
opening + Σ credits − Σ debits = closing
```

`opening` is what the account held the instant the period began. `closing` is what it held the instant the period ended. Both carry the same three buckets as the `balances` on the account resource:

| Bucket      | What it holds                                                    |
| ----------- | ---------------------------------------------------------------- |
| `available` | Money the customer can spend now                                 |
| `pending`   | Money that has arrived or been reserved and is not spendable yet |
| `frozen`    | Money held under a compliance or operational hold                |

A virtual account's reserved and incoming claims are both `pending`, so a payout reservation and an unsettled arrival appear in the same bucket.

## Reading a line

A line is one posted ledger entry, not one transaction. A single deposit writes several: the arrival raises `pending`, and the approval raises `available` and releases the `pending` hold. Lines come in the order the ledger posted them, oldest first.

| Field             | What it says                                                                |
| ----------------- | --------------------------------------------------------------------------- |
| `at`              | When the entry posted                                                       |
| `bucket`          | Which balance bucket it moved                                               |
| `direction`       | `credit` raised the bucket, `debit` lowered it. `amount` is always positive |
| `amount`          | The money that moved, in the account's asset                                |
| `kind`            | `principal`, `fee`, or `adjustment`                                         |
| `transactionId`   | The transaction the entry belongs to                                        |
| `transactionType` | That transaction's type, such as `deposit` or `withdrawal`                  |

`kind` separates the money a transaction moves from what Conduit charges to move it. An `adjustment` is a correction that belongs to no transaction, so it carries no transaction fields.

Two kinds of fee entry read as `principal`. A fee entry posted before statements were released carries no kind of its own. The entry that reverses a fee carries none either, because a reversal carries no entry-level detail. Both are rare, and neither changes what reconciles.

`transactionId` and `transactionType` are absent when the entry belongs to no transaction you can read, such as a movement Conduit makes on its own books.

## Paging a long period

One request answers one page of lines. Follow `meta.nextCursor` until it is `null`:

```bash theme={null}
curl -G https://api.conduit.financial/v2/customers/cus_.../virtual-accounts/vac_.../statement \
  -H "x-api-key: YOUR_API_KEY" \
  -d from=2026-08-01 \
  -d to=2026-08-31 \
  -d cursor=eyJpZCI6...
```

A cursor belongs to the request that returned it. Send it back with the same account, asset and period, or the call returns `400 INVALID_CURSOR`.

`opening`, `closing` and `period` cover the whole period on every page, not only the page you are holding. The sum reconciles once you have every page, never on one page of several. Collect the lines, then check.

The invariant holds by construction, and the check is yours to run, over one page or many. Sum the lines you collected and compare them to `opening` and `closing`. A statement that does not add up means a problem in the ledger behind it, so do not reconcile around it — contact support with the account, the period and the figures you got.

<Note>
  A month of one busy account is a few pages. Ask for the month you want, not
  the year. The period holds at most 92 days, and a statement is easiest to
  reconcile one month at a time.
</Note>
