# Balances and statements

Check what's in your accounts and reconcile every movement against your own records. Reconciliation means matching each line of Multigate's statement to a payout, inflow or conversion you recorded, so every amount is accounted for.

## Before you start

- Your API key in `MG_API_KEY` (see the [Quickstart](../01-get-started/03-quickstart.md)).
- The account to read. Every endpoint on this page takes an **account number**: the wallet's own number or any of its virtual account numbers (`fundingDetails[].accountNumber`), or a linked account number. A customer's accounts work the same way. See [IDs and numbers you'll use](02-accounts.md#ids-and-numbers-youll-use).
- Your own records of payouts (with their `uniqueRequestId`), inflows (with their `transactionId`) and conversions (with their `id`).

## Balances

| You want | Use |
|---|---|
| One account's balance | [Get an account balance](api:getAccountBalance) |
| One account with its balance and details | [Get an account](api:getAccount) |
| The total in one currency across your accounts (or a customer's), the aggregated balance view | [Get balances across accounts](api:getConsolidatedBalances) |
| Which currencies you hold | [List currencies you hold](api:listAccountCurrencies) |

```bash
curl "$MG_BASE_URL/client-service/api/v1/accounts/NGN/balance?accountNumber=9912345678" \
  -H "mp-api-key: $MG_API_KEY"
```

```json
{
  "message": "balance retrieved successfully",
  "accountBalance": { "accountNumber": "1029384756", "availableBalance": "1250000.5", "bookBalance": "1250000.5" }
}
```

- `availableBalance` is what you can spend now; money reserved for transfers in progress is already taken off. Transfers check it against amount **plus fee**.
- Balances are decimal strings without fixed decimals (`"1250000"`, `"1250000.5"`). Parse them as decimals, never as floating-point numbers.
- You can pass the wallet number or any of its virtual account numbers; `accountNumber` in the response is always the wallet's own number.
- The account must be `active`: anything else returns `400` "Account is inactive".
- If the ledger or, for a **linked account**, your bank can't be reached, the balance comes back as `"0"` instead of an error. Don't treat `"0"` as final: retry.
- In [Get balances across accounts](api:getConsolidatedBalances), SWIFT linked accounts always count as `0`, and `totalBalance` is a JSON number: round it to the currency's decimals.

## Statements

| You want | Use | Includes |
|---|---|---|
| A statement with opening, closing and running balances | [Get an account statement](api:getAccountStatement) | `openingBalance`, `closingBalance`, and `accountBalance` after every line |
| Transactions with sender and receiver details and status | [Get account transactions](api:getTransactionHistory) | Counterparty names, banks and account numbers, status, exact time |
| A file for your finance team | [Download a statement](api:downloadAccountStatement) | Excel, with a balance column |

```bash
curl "$MG_BASE_URL/client-service/api/v1/statement/clientStatement/9912345678?startDate=2026-10-01&endDate=2026-10-10&pageSize=500" \
  -H "mp-api-key: $MG_API_KEY"
```

- Dates are `YYYY-MM-DD` UTC days, and the end date is **inclusive**. Always pass a period; without one you get the account's whole history.
- A virtual account number returns its **wallet's** statement.
- Each line has `id` (the Multigate reference, for example `IN-…` for an inflow), `direction` (`credit` or `debit`), `amount`, `transactionType` (`Inflow`, `Transfer`, `Trade`, `Fee`, `Manual` or `Refund`), `narration`, `customReference` (your `uniqueRequestId` for payouts and their fees; the bank's reference for inflows) and `accountBalance` (the balance after that line). See [What each statement line is](#what-each-statement-line-is).
- Lines are newest first.
- Statement lines carry the **date only**. For exact times, use [Get account transactions](api:getTransactionHistory).
- `closingBalance` is reliable on the last page. Fetch a day in one page (a large `pageSize`) for reconciliation.
- A period with no transactions returns `200` with an empty list for wallets.

:::info[Empty isn't always empty]
**Get account transactions** returns `200` with an empty list, not an error, when transactions can't be loaded or when a query parameter is invalid or unknown (for example a `pageSize` above 1000). If a busy account suddenly shows nothing, check your parameters and retry before concluding there were no transactions.
:::

### Fields differ between endpoints

| | Get an account statement | Get account transactions | Download a statement |
|---|---|---|---|
| Direction | `credit` / `debit` | `transactionCategory`: `CR` / `DR` | `CR` / `DR` |
| Amount | Number; the amount that moved on this account | Decimal string; the transaction amount | The transaction amount |
| Your `uniqueRequestId` | `customReference` | Not included (search it with `transactionId`) | UniqueRequestId column |
| Time | Date only | Full timestamp | Date and time (Lagos time) |

### Linked accounts

| Statement | Local linked account | SWIFT linked account |
|---|---|---|
| Get an account statement | Live from your bank; opening and closing balances are `0`; `404` if the bank returns no lines | Not available (`404`) |
| Get account transactions | Live from your bank; no paging or filters | One row per MT940 statement received |
| Download a statement | Yes | Yes |

Without dates, local linked accounts cover **today only**.

## Daily reconciliation

```mermaid
flowchart LR
    a[Your records<br/>payouts, inflows, conversions] --> c{Match each line}
    b[Multigate statement<br/>for yesterday] --> c
    c -->|match| ok[Reconciled]
    c -->|only in statement| inv[Investigate<br/>missed webhook?]
    c -->|only in your records| inv2[Investigate<br/>failed or pending?]
```

### What each statement line is

| `transactionType` | `direction` | `id` | `customReference` | Match it to |
|---|---|---|---|---|
| `Inflow` | `credit` | The inflow's `transactionId` (`IN-…`) | The bank's reference (the webhook's `paymentReference`) | The `account` / `inflow` webhook with the same `transactionId`. |
| `Transfer` | `debit` | The payout's `transactionId` (`PMT-…`) | Your `uniqueRequestId` | Your payout. |
| `Fee` | `debit` | `FEE-` followed by the payout's reference without its prefix (`FEE-261010-…`) | The same `uniqueRequestId` as the payout | The payout's fee (`charge`). |
| `Transfer` or `Fee` | `credit` | `RVSL-` followed by the original line's `id` | The original `uniqueRequestId` | A failed payout or fee being reversed: the money is back in the account. |
| `Trade` | `debit` or `credit` | On the credited account: the conversion's `id` (`FX-…`) | Empty | Your conversion. |
| `Refund`, `Manual` | Either | Multigate's reference | Varies | A refund you made, or an adjustment by Multigate. Ask support if you don't recognise one. |

The amount on a payout line is the amount sent; the fee is its own line. If Multigate has set up a dedicated fee account for you, or fees are charged monthly by agreement, `Fee` lines appear there or later instead.

### Steps

1. Each morning, pull yesterday's statement for each wallet: `startDate` and `endDate` both set to yesterday (UTC), and a `pageSize` large enough for the whole day, so `closingBalance` is reliable.
2. Match each line with the table above.
3. Check that `openingBalance` plus credits minus debits equals `closingBalance`, and that `closingBalance` equals the balance you expect.
4. Investigate anything unmatched:
   - **Only in the statement:** usually a webhook you didn't process. Record it now.
   - **Only in your records:** a payout still `processing`, or one that failed and was reversed. [Get account transactions](api:getTransactionHistory) shows each transaction's `transactionStatus`.

## Next

<CardGroup cols={2}>
<Card title="Collections" icon="lucide-arrow-down-to-line" href="03-collections.md">
What happens when money arrives in a virtual account.
</Card>
<Card title="Accounts" icon="lucide-wallet" href="02-accounts.md">
Open wallets, virtual accounts and linked accounts.
</Card>
</CardGroup>
