---
title: "List customer aging balances"
method: GET
path: "/v1/aging/customers"
tags: ["Aging"]
---

# List customer aging balances

`GET /v1/aging/customers`

## Request

- Base URL: `https://api.getfinero.com/functions/v1/api`
- URL: `https://api.getfinero.com/functions/v1/api/v1/aging/customers`
- Auth: HTTP bearer

Current installment-level aging in the workspace timezone, read from the PAYMENT SCHEDULE rather than the invoice header. A row counts when the installment is live, still owes money, is not closed in the source, and has a due date; removed and retired records never count. Only an invoice the ERP has FINALISED counts: a draft, an invoice awaiting approval, or a voided or deleted document is not a receivable, whatever amount it carries. `collectible` on each row says whether Finero can act on it. Balances are the maintained ERP/Finero reconciled figures, aged by due date: today or future, 1–30, 31–60, 61–90, or over 90 days. Absent by design: an installment with NO due date, which cannot be aged, and a CREDIT balance, which Finero never allocates against a debt. No historical reconstruction or FX. Totals cover all matching rows, per currency. Summary aggregates into `totals` and carries NEITHER `data` nor `pagination`: it is not a list. Customer and installment lists carry both. Use customer_key and bucket for drilldown. Cursors require unchanged query and report revision; restart on invalid_cursor. Invoice-summary due filters select whole invoices, not installment aging.

Available to **admin and readonly** API keys (no side effects).

## Query parameters

- `currency` string: Currency code, or UNKNOWN for missing currency. Omit for separate totals per currency; never sum currencies.
- `search` string: Literal, case-insensitive customer name or number search across the full report.
- `customer_key` string: Stable key returned by customer aging: customer UUID, or unassigned:invoice UUID. Never group customers by name.
- `bucket` 'current' | 'days_1_30' | 'days_31_60' | 'days_61_90' | 'over_90': Installment bucket: current includes today; 1–30, 31–60, 61–90, over 90 are disjoint. The report holds positive balances only: an installment with no due date, or a credit balance, is not in it at all.
- `overdue_only` boolean: Only positive balances with invoice or installment due dates before the workspace's current business day.
- `sort` 'overdue_desc' | 'outstanding_desc' | 'customer_asc': Customer ordering, default overdue_desc. Money ranks WITHIN a currency, never across two: rows group by currency, largest first inside each — filter by currency to rank one book. Breakdown always orders by due date, invoice and installment; unknown dates last.
- `limit` integer: Rows per page, default 50, maximum 100. Totals cover all matching rows.
- `cursor` string: Return next_cursor unchanged, with identical filters and limit. Do not parse or build one — the format may change. Restart without it on invalid_cursor.

## Response `200`

Success

- object
  - `report_date` string, date, required: Current business day in workspace timezone; not a historical balance date.
  - `timezone` string, required
  - `generated_at` string, date-time, required
  - `balance_basis` 'finero_reconciled_current', required: Current maintained invoice or installment balance after existing ERP/Finero reconciliation. Includes debt Finero cannot currently collect, whatever blocks it: `collectible` on each row says whether it can be acted on. Always positive: a credit balance is not reported here, and Finero never nets one against a debt. No FX or historical reconstruction.
  - `revision` string, required: Content hash of the rows behind this answer. It changes whenever they do, in every mode, so it is usable as a change token; a cursor also carries it, which is how a paged walk detects that the report moved underneath it.
  - `currencies` string[], required: The workspace's currencies, not this response's — a filter never removes one. Empty totals beside a full list means nothing matched, not that nothing is owed.
  - `data_updated_at` string, date-time, nullable, required: Latest source record change, not proof that the ERP is fully synchronized.
  - `totals` object[], required
    - `currency` string, required
    - `outstanding` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `overdue` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `current` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_1_30` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_31_60` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_61_90` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `over_90` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
  - `data` object[], required
    - `currency` string, required
    - `outstanding` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `overdue` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `current` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_1_30` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_31_60` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `days_61_90` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `over_90` string, required: Exact decimal amount in major currency units. Outstanding sums the positive balances the report holds; credits are not among them.
    - `customer_key` string, required
    - `customer_id` string, nullable, required
    - `customer_name` string, required
    - `customer_number` string, nullable, required: The ERP's account key for the customer: Oracle's business account number; for Xero, the contact's ContactID (a UUID, an identifier rather than an account number to show a person). Null for a customer created in Finero.
    - `invoice_count` integer, required: Invoices behind this row: those with an installment still owing a dated positive balance, after this report's filters. One collected in full is not among them — which is why `summarize_invoices.by_customer.invoice_count` can be HIGHER for the same customer: it counts invoices carrying money, owed or collected.
    - `installment_count` integer, required: How many installments are behind this row — the same population `invoice_count` counts, one row deep. Not the size of an invoice's schedule: `invoice_installment_count` on the breakdown is that, and counts paid siblings too.
  - `pagination` object, required
    - `total_count` integer, required: How many rows match the filters, ignoring paging — customers and currencies in customer mode, invoice or installment targets in the breakdown. Not a count of invoices.
    - `limit` integer, required
    - `next_cursor` string, nullable, required

## Other responses

- `400`: `invalid_cursor` — The cursor is malformed, was issued for a different query, or (aging) the report changed. Restart without cursor, keeping the filters identical. `validation_failed` — The request failed schema validation. See error.details for field messages.
- `401`: `invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.
- `403`: `feature_unavailable` — The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key's permission does not allow this operation (readonly keys cannot call admin-only operations).
- `404`: `not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.
- `429`: `rate_limit_exceeded` — Too many requests. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.
- `500`: `internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/getfinero/apis/finero-public-api.md) · [All operations](https://skmtc.dev/getfinero/apis/finero-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getfinero/apis/finero-public-api/revisions/3d456e222775?raw)
