---
title: "List cashbook entries"
method: GET
path: "/v1/cashbook-entries"
tags: ["CashBook"]
---

# List cashbook entries

`GET /v1/cashbook-entries`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/cashbook-entries`
- Auth: HTTP bearer

Returns a paginated, filtered, ordered window of cashbook
entries for the authenticated user's company.

The response envelope extends the standard `ListEnvelope`
with two cashbook-specific fields:

- `opening_balance` — the running-balance anchor at the start
  of the `from` window. Zero when no `from` is set (the
  running balance simply accumulates from zero on the first
  row).
- `total_items` — the full filtered count, so clients can
  render "n of N" without a follow-up request.

Page rows carry `running_balance` already computed in
`order_direction` from `opening_balance`. Do not re-sort
client-side without recomputing.

## Query parameters

- `tab` 'tutto' | 'entrate' | 'uscite'
- `financial_account_id` string
- `preset` 'rel30' | 'rel90' | 'rel365' | 'year' | 'Q1' | 'Q2' | 'Q3' | 'Q4' | 'month' | 'custom' | 'all'
- `from` string, date
- `to` string, date
- `q` string
- `limit` integer
- `starting_after` string
- `order_direction` 'asc' | 'desc'

## Response `200`

Page of cashbook entries.

- CashBookListEnvelope: Extends `ListEnvelope` with the two fields the cashbook needs to be self-consistent: `opening_balance` (the running-balance anchor) and `total_items` (the full filtered count). The envelope's `{object, data, has_more, url}` shape stays wire-compatible with the generic list parser; the extra fields are inert to clients that don't know about them.
  - `object` 'list', required: Polymorphic discriminator.
  - `data` CashBookEntry[], required: Page of items. Each item carries its own `object` field.
    - `object` 'cashbook_entry', required
    - `id` string, required: Underlying payment UUID. Opaque to clients.
    - `payment_type` 'income' | 'expense', required: Side of the ledger. Clients branch on this to decide which side-specific fields (invoice_/worksheet_ vs expense_, customer_name vs supplier_name) are populated.
    - `amount` number, double, required: Signed payment amount in the workshop's currency. Float64 matches the entity layer; the cashbook computes a running balance, so integer cents would force clients to redo the math.
    - `payment_date` string, date-time, required: When the payment was recorded. RFC 3339 UTC.
    - `financial_account_id` string, nullable: Account this payment hit. Nullable — legacy entries may predate the financial-accounts feature.
    - `financial_account_name` string, required: Denormalized account name. Empty string for legacy entries with no account assignment.
    - `customer_name` string: Income-side counterparty. Omitted from the JSON entirely on expense rows (no empty key).
    - `supplier_name` string: Expense-side counterparty. Omitted from the JSON entirely on income rows.
    - `invoice_id` string, nullable: Source invoice UUID (income rows).
    - `invoice_seq_number` integer, nullable: Sequence number of the linked issued invoice, if any. Set on income rows backed by an issued invoice.
    - `invoice_issued_at` string, date-time, nullable
    - `worksheet_id` string, nullable: Source worksheet UUID, set on income rows backed by a worksheet whose invoice is still a draft.
    - `worksheet_seq_number` integer, nullable: Sequence number of the linked worksheet, set on income rows backed by a worksheet whose invoice is still a draft.
    - `worksheet_created_at` string, date-time, nullable
    - `receipt_id` string, nullable: Source receipt (scontrino) UUID (income rows).
    - `receipt_seq_number` integer, nullable: Sequence number of the linked standalone scontrino.
    - `receipt_issued_at` string, date-time, nullable
    - `expense_id` string, nullable: Source expense UUID (expense rows).
    - `expense_seq_number` integer, nullable: Sequence number of the linked supplier expense.
    - `expense_created_at` string, date-time, nullable
    - `manual_entry_id` string, nullable: Set on a manual prima nota movement — a row typed straight into the ledger with no source document, so every other `*_id` above is null. Its value is the id used to edit or delete the movement on the web app.
    - `manual_description` string: Free-text label of a manual movement, shown in place of the document reference other rows carry. Empty on every other row.
    - `manual_payment_method` string: Payment method code (MP01, MP05, …) of a manual movement. Empty on every other row.
    - `running_balance` number, double, required: Cumulative balance after applying this row, starting from `opening_balance` on the list envelope. Computed by the server in `order_direction` — do not re-sort client-side without recomputing.
  - `has_more` boolean, required: True if more results exist beyond this page. Use this for the "load more" UI — `data.length` alone is unreliable because a full page may still be the last page.
  - `next_starting_after` string: Opaque cursor for the next page. Present (and non-empty) when `has_more` is `true`; omitted otherwise. Pass the value verbatim as `?starting_after=` on the follow-up request. Clients MUST NOT parse it — today it encodes an offset, tomorrow it may encode a `(created_at, id)` tuple without a wire-visible change.
  - `url` string, required: The request path that produced this list.
  - `opening_balance` number, double, required: Running-balance anchor at the start of the `from` window. Zero when `from` is unset.
  - `total_items` integer, required: Full filtered count, ignoring pagination.
  - `date_filter` DateFilter: The effective date range the server applied to a date-scoped list request, echoed on the list envelope so clients can render the active filter without re-deriving it. A request that sends no `from`/`to`/`preset` gets the default window, the last 12 months (`rel365`); nothing is carried over between requests. Relative presets are resolved here to concrete dates and roll forward over time.
    - `object` 'date_filter', required
    - `preset` 'rel30' | 'rel90' | 'rel365' | 'year' | 'Q1' | 'Q2' | 'Q3' | 'Q4' | 'month' | 'custom' | 'all', required: Effective selection label. Relative (`rel30`/`rel90`/`rel365`) and absolute (`year`/`Q1`-`Q4`/`month`/`custom`) carry `from`/`to`; `all` omits them.
    - `from` string, date: Inclusive lower bound (YYYY-MM-DD). Omitted when `preset` is `all`.
    - `to` string, date: Inclusive upper bound (YYYY-MM-DD). Omitted when `preset` is `all`.

## Other responses

- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `415`: Content-Type is not `application/json`, or Accept rejects JSON.
- `422`: A request field is missing or invalid (fast-fail).
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
