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

# Overview

The Financials API is the full read/write surface of the financials
subsystem: invoices and their lifecycle, payments, per-event reserves, two
composition endpoints (event import, payee merge), balance and reserve-update
reads, configuration discovery, and a read-state consistency check/repair
pair, plus the atomic policy-invoice batch. Every write is a recorded action on
an append-only journal — the API is the action catalog with HTTP methods on it.

<Note>
  This page documents the contract of the **current financials surface**.
  Companies are enabled on it progressively; a company not yet enabled gets
  `404` from these endpoints (they behave as if they do not exist yet) and is
  served by the [deprecated legacy endpoints](#deprecated-legacy-endpoints)
  below until its cutover. The policy-invoice batch is the exception: its
  separate `policy-financials` rollout gate returns `403` when disabled, never a
  flag-derived `404`.
</Note>

## Design rules

These rules hold across the whole surface — each operation's page restates
only what is specific to it.

### Idempotency: client-supplied `actionId`

Every write takes a client-minted `actionId` (a uuid) that becomes the
recorded action's journal id. Retrying the **identical** request replays the
original outcome — server-minted values (invoice id and number, the created
marks' payment ids) included — instead of applying twice; duplicate
resolution runs before every other check, so a replay succeeds even with a
stale `If-Match`. Reusing an id with a **different** action or payload is
`409 ACTION_ID_REUSED`. A payment gesture becomes one action per per-line
mark: the `actionId` anchors the FIRST mark's action, sibling marks mint their
own ids in the same transaction, and a replay reconstructs the full set.

Compound endpoints follow the same rule per client-authored action: the
[event import](/api-reference/financials/import-event-financials) takes one
`actionId` per create and per reserve set, commits atomically, and
short-circuits a retry when your ids are already journaled. The
[payee merge](/api-reference/financials/merge-payee) takes no `actionId`s at
all — it is idempotent by convergence. The
[policy invoice batch](/api-reference/financials/update-policy-invoices) also
takes no `actionId`: its per-invoice watermarks protect the input snapshot and
the whole policy scope is serialized in one database transaction.

### Authorship: the optional `author` label

Every write except the policy-invoice batch accepts an optional `author`
(1–255 characters) — a display label for the source system's author, e.g. the
person acting in your system. The batch body is exactly its detached explicit
plan and has no `author` field. Where the app shows who recorded a change, a
labeled row reads as your label; an unlabeled row reads as the built-in
"External API" actor. Rows written through this API are always visibly marked
as API-written regardless of the label — `author` is display attribution,
never an identity: it cannot impersonate an in-app user, and the recorded
acting principal is always the API surface itself. On an idempotent replay the
original record — original label included — is returned and the retried
`author` is ignored.

### Concurrency: the `If-Match` watermark

Every invoice row carries `headJournalId` — the id of the last action that
touched it. Every single-invoice write after creation must send it as
`If-Match`. A stale value is `409 IF_MATCH_CONFLICT` with the invoice's
**current** row in the body (`error.currentInvoice`): re-read, reconcile,
retry with its `headJournalId`. A missing or malformed header is `400`.
Reserve writes and the compositions are exempt — they serialize on scope
locks instead.

### Stable error codes (`422`)

A write whose payload is well-formed but rejected by the books fails with
`422` and a **stable machine-readable code** (`LIVE_PAYMENTS`,
`AMOUNT_EXCEEDS_BALANCE_DUE`, `INVALID_AMOUNT`, `INVOICE_VOIDED`,
`LINE_ITEMS_IMMUTABLE`, `UNKNOWN_ID`, …— the full 18-value enum is on
every write's 422 schema). Codes are contract; messages may be reworded.
Preconditions **fail closed**: a rejected write leaves no journal record, no
ledger change, no read-state change.

Malformed payloads (unknown fields, both `links.event` and `links.policy`,
both or neither payment mode) are schema-level `400`s. Unknown path
resources (invoice, event) are `404`. A valid API key naming another company
in the URL is `403`.

### Writes return refreshed read state

Every write's response is the refreshed read state, not an acknowledgement:
invoice writes return `{journalIds, invoice, payments}` (plus top-level
`paymentIds` and `createdPayments` — the created mark rows — when recording
a payment); reserve writes return the scope's new `expectedTotalCents`.
`journalIds` carries every journal id the write emitted — a payment's sibling
marks and companions included, e.g. the automatic reserve unwind composed
when a pre-horizon eroding payment is removed. There is **one
invoice representation**: reads and write responses share the same invoice
shape.

### Amounts and dates

All amounts are **integer cents**. On line items, direction never rides the
sign: `amountCents` is the magnitude a user would type, and a negative
amount is a credit within the item's own frame — it posts opposite the line
item type's expected direction (e.g. a reversal or refund); the posting rule
always comes from the type, never the sign.

The invoice's document scalars are **oriented net-cash figures**:
`totalAmountCents` sums the line amounts oriented by their types' directions
(payable +, receivable −) — the net cash the document commits to move;
`amountPaidCents` is the oriented sum of live payment marks — net cash moved
so far; `balanceDueCents` is their difference — net cash remaining, positive
\= out, negative = in. A document whose oriented due is negative is an open
credit (money in), and the due may move **non-monotonically** as
opposite-direction lines settle.

A payment is a **per-line settlement mark**, signed in its line's frame, and
settles that line's remaining **toward zero** — per line, never as a
document scalar; an invoice is `paid` when every line is settled, never when
a scalar hits zero. The record-payment request names its marks in that same
line frame, or asks for the whole balance due (see
[Record Payment](/api-reference/financials/record-payment)). A reserve
scope's remaining reserve is likewise a **signed balance** — the identity
Reserves + Paid = Expected Total is its only law, and the remaining may read
negative (over-paid against a standing estimate).

Balance reads return raw
signed cents in the ledger convention with each account's `role` and
provenance `direction` (and each category's `expectedDirection`) embedded;
the carrying side derives from role × direction (assets/expenses-side
accounts carry debit, liabilities/income-side carry credit), so clients
orient displays themselves. Dates are ISO `YYYY-MM-DD`; timestamps are ISO
8601\.

### Cursor pagination

List reads are cursor-paginated: pass the previous page's `nextCursor` back
as `cursor` until it is `null`. Cursors are opaque — echo them verbatim.

## API Endpoints

### Invoice reads

* **List Invoices** (`GET /financials/invoices`) — global and entity-scoped
  lists in one endpoint: filter by status, category, linked
  event/policy/payee, invoice number, and date ranges; cursor-paginated.
  Deleted invoices are hidden.
* **Get Invoice** (`GET /financials/invoices/{invoiceId}`) — the whole
  current document with its live payments and `headJournalId`. Returns
  deleted invoices too (a delete's confirmable terminal state).

### Invoice writes

* **Create Invoice** (`POST /financials/invoices`) — the server mints the id
  and invoice number; links (event **or** policy, plus payee) are optional
  and can attach later.
* **Update Invoice** (`PUT /financials/invoices/{invoiceId}`) — once an
  invoice is POSTED its line items and category are **immutable** (echo the
  stored set verbatim; any deviation is `422 LINE_ITEMS_IMMUTABLE`, at any
  payment count): the mutable remainder is dates, memo, and `fieldData`, and
  corrections are void-and-recreate. Drafts still replace their document
  wholesale until finalize. Links excluded either way (they change via the
  endpoints below).
* **Delete Invoice** (`DELETE /financials/invoices/{invoiceId}`) — terminal;
  live payments are swept in the same action.
* **Re-link Invoice** (`POST …/relink`) — attach/detach/move the event or
  policy link; zero ledger rows; requires no live payments.
* **Change Invoice Payee** (`POST …/payee`) — set/clear/change the payee;
  requires no live payments.
* **Record Payment** (`POST …/payments`) — either `payBalanceDue: true`
  (settle every open line, both directions) or explicit per-line
  `allocations`, exactly one per request; returns the created mark rows;
  `erodeReserves` steers reserve erosion on reserved-category invoices.
* **Remove Payment** (`DELETE …/payments/{paymentId}`) — exact negation; the
  horizon rule's companion reserve unwind composes automatically.
* **Void Invoice** (`POST …/void`) / **Restore Invoice** (`POST …/restore`) —
  write the charges down to zero and back; both have defined zero-row no-ops
  (re-void, restore of a non-voided invoice).
* **Approve Invoice** (`POST …/approve`) / **Unapprove Invoice**
  (`POST …/unapprove`) — flip the document's approval state; zero ledger rows.
  Money may only post against an approved invoice (`paid ⇒ approved`), so
  approve before recording a payment — or let **Record Payment** approve for
  you (below). Both need `company.payment:approve`, which is also the whole
  authority: the company's in-app approval rules never apply to API callers.
  Re-approving keeps the ORIGINAL approval; unapproving a paid or
  partially-paid invoice is `422 UNAPPROVE_PAID`.

<Note>
  **Approval and payments.** Recording a payment on an UNAPPROVED invoice
  approves it automatically when the calling key holds
  `company.payment:approve` (in the same transaction, before the payment
  marks); a key without the permission gets `422 NOT_APPROVED` instead. The
  auto-approval shows up as `approved: true` on the response's invoice row and
  is deliberately absent from `journalIds`, so an idempotent `actionId` replay
  returns a byte-identical body.
</Note>

### Bill review

A bill review checks one invoice against the company's own bill review rules and
reports what it thinks is wrong with it. It is **asynchronous**: starting one
queues background work and returns immediately, and the verdict arrives on the
read.

* **Start Invoice Bill Review** (`POST …/bill-review`) — `202` with the
  `runId` to poll. No request body, no `actionId`, no `If-Match`: the rules are
  the company's own (snapshotted when the run is created) and the invoice
  version is pinned from its current head, so there is nothing to send, and a
  review records a verdict beside the invoice rather than journaling an action
  against it. Re-posting while a review of this invoice is under way creates
  nothing and returns `outcome: in_flight` with the run already running; once
  that run is terminal, the next call mints a fresh one. A company at its
  background-work capacity gets a `429` and nothing is queued.
* **Get Invoice Bill Review** (`GET …/bill-review`) — the latest run, its
  verdict and its findings. Poll until `status` is `succeeded` or `failed`;
  `passed` is `null` until then, and stays `null` forever on a `failed` run, so
  read `error` there rather than reading an empty `findings` list as a pass.
  `billReview` is `null` when the invoice has never been reviewed.

<Note>
  **Findings can be stale, and you check that yourself.**
  `invoiceHeadJournalId` is the invoice version the run judged, pinned when the
  review was started; a review is never re-run because the document changed. If
  it differs from the invoice's current `headJournalId`, the findings describe an
  earlier version — start a fresh review. Dismissals (`dismissedAt`) are stamps
  an operator applies in the app, not deletes, so dismissed findings are still
  returned; filter on the field if you only want outstanding ones.
</Note>

### Reserves

* **Set Reserves** (`PUT /financials/events/{eventId}/reserves/{categoryId}`) —
  absolute "set expected total to X" per `(event, reserved category)` scope;
  previous total and delta are recorded server-side. X is unconstrained — a
  set below the scope's paid-to-date is legal and leaves the remaining
  reserve negative.
* **Reset Reserve History** (`POST …/reserves/{categoryId}/history-reset`) —
  zero the scope's remaining expectation and mark the reserve-update feed's
  horizon; idempotent-shaped, legal on deprecated categories.

### Compositions

* **Import Event Financials** (`POST /financials/events/{eventId}/import`) —
  wholesale refresh in one transaction: sweep the event's current invoices,
  create the new slate, set expected totals. Importing replaces, it does not
  append.
* **Merge Payee** (`POST /financials/payees/{payeeId}/merge`) — repoint every
  linked invoice at another payee, removing and re-recording payments so
  attribution follows; idempotent by convergence.
* **Update Policy Invoices**
  (`POST /financials/policies/{policyId}/invoices/batch`) — the only document
  write door for policy-linked invoices. Submit the complete explicit
  keep/void/create plan; every void cites the invoice's `headJournalId`, and
  kept plus created invoices must conserve the policy's current pricing
  contract. The batch is atomic and never infers dates, payees, or schedules.

### Balances and feeds

* **Get Balances** (`GET /financials/balances`) — company-wide per-category
  rollups with per-account detail, raw signed cents.
* **Get Entity Balances** (`GET /financials/entities/{entityType}/{entityId}/balances`) —
  one entity's slice in the same shape (`event`, `policy`, or `payee`); also
  the read for a scope's current expected total before setting reserves.
* **List Reserve Updates** (`GET /financials/events/{eventId}/reserve-updates`) —
  an event's reserve-update feed: user updates, automatic eroding-payment
  rows, and history-reset markers.

### Configuration discovery

* **List Transaction Categories** (`GET /financials/config/categories`) —
  read-only discovery of the category and line item type ids write payloads
  cite (`includeDeprecated=true` to resolve old references). Categories and
  line item types are configured in the app — this surface has **no
  configuration writes**.

### Validation

* **Check Financials Read-State Consistency** (`GET /financials/validation/check`) —
  read-only: verify a scoped window of the derived read state against a
  replay of the journal; returns the mismatch list.
* **Rebuild Financials Read-State** (`POST /financials/validation/sync`) —
  rebuild the derived read state from the journal under an exclusive
  company-level lock; returns per-table rebuilt counts. Idempotent, safe at
  any time.

### Operational reset (SUPER\_ADMIN)

* **Delete All Financial Data** (`POST /financials/deleteAll`) — hard-delete
  every financial **record** the company holds (invoices, payments, journal,
  ledger, balances) while keeping financial **configuration** (categories,
  line item types, approval config). The first rung of the breaking-config
  reset ladder: the per-type entity wipe
  (`POST .../entities/{entityType}/deleteAll`) rejects with `409` while
  financials hold a live claim, and this endpoint is its unblock path.
  SUPER\_ADMIN-only. Both bulk wipes are additionally **delete-guarded**: a
  company whose delete guard is `active` or `onboarding-active` is rejected
  with `409 DeleteGuardConflict` — the wipes require a non-active delete
  guard, set from the Control Plane.

## Attachments ride the documents API

Invoice endpoints take no files. Upload documents through the platform
[Company Files API](/api-reference/company-files/overview) and they attach
alongside the invoice — an invoice can arrive unlinked and gain its links
later for the price of one action.

## Deliberate exclusions

The API deliberately does **not** offer: configuration writes (categories and
line item types are managed in the app), a chart-of-accounts endpoint,
time-travel query parameters, raw journal/ledger dumps, or a per-invoice
action history. The journal is the system's internal source of truth; the
read surface above is the contract.

Bill review follows the same rule: the rules themselves are managed
in the app, so there is no endpoint to write them, no endpoint to dismiss a
finding, and no history of earlier runs — only the latest one per invoice.

## Permissions

| Operation                               | Method                                                                  | Required Permission                                    |
| --------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------ |
| List Invoices                           | `GET /financials/invoices`                                              | `company.payment:read`                                 |
| Create Invoice                          | `POST /financials/invoices`                                             | `company.payment:update`                               |
| Update Policy Invoices                  | `POST /financials/policies/{policyId}/invoices/batch`                   | `company.payment:update`                               |
| Get Invoice                             | `GET /financials/invoices/{invoiceId}`                                  | `company.payment:read`                                 |
| Update Invoice                          | `PUT /financials/invoices/{invoiceId}`                                  | `company.payment:update`                               |
| Delete Invoice                          | `DELETE /financials/invoices/{invoiceId}`                               | `company.payment:update`                               |
| Re-link Invoice                         | `POST /financials/invoices/{invoiceId}/relink`                          | `company.payment:update`                               |
| Change Invoice Payee                    | `POST /financials/invoices/{invoiceId}/payee`                           | `company.payment:update`                               |
| Record Payment                          | `POST /financials/invoices/{invoiceId}/payments`                        | `company.payment:update`                               |
| Remove Payment                          | `DELETE /financials/invoices/{invoiceId}/payments/{paymentId}`          | `company.payment:update`                               |
| Void Invoice                            | `POST /financials/invoices/{invoiceId}/void`                            | `company.payment:update`                               |
| Restore Invoice                         | `POST /financials/invoices/{invoiceId}/restore`                         | `company.payment:update`                               |
| Approve Invoice                         | `POST /financials/invoices/{invoiceId}/approve`                         | `company.payment:approve`                              |
| Unapprove Invoice                       | `POST /financials/invoices/{invoiceId}/unapprove`                       | `company.payment:approve`                              |
| Start Invoice Bill Review               | `POST /financials/invoices/{invoiceId}/bill-review`                     | `company.payment:update`                               |
| Get Invoice Bill Review                 | `GET /financials/invoices/{invoiceId}/bill-review`                      | `company.payment:read`                                 |
| Set Reserves                            | `PUT /financials/events/{eventId}/reserves/{categoryId}`                | `company.payment:update`                               |
| Reset Reserve History                   | `POST /financials/events/{eventId}/reserves/{categoryId}/history-reset` | `company.payment:update`                               |
| Import Event Financials                 | `POST /financials/events/{eventId}/import`                              | `company.payment:update`                               |
| Merge Payee                             | `POST /financials/payees/{payeeId}/merge`                               | `company.payment:update`                               |
| Get Balances                            | `GET /financials/balances`                                              | `company.payment:read`                                 |
| Get Entity Balances                     | `GET /financials/entities/{entityType}/{entityId}/balances`             | `company.payment:read`                                 |
| List Reserve Updates                    | `GET /financials/events/{eventId}/reserve-updates`                      | `company.payment:read`                                 |
| List Transaction Categories             | `GET /financials/config/categories`                                     | `company.payment:read`                                 |
| Reset Approval Config                   | `DELETE /financials/config/approvals`                                   | `company.payment:configure` (SUPER\_ADMIN only)        |
| Check Financials Read-State Consistency | `GET /financials/validation/check`                                      | `company.payment:read`                                 |
| Rebuild Financials Read-State           | `POST /financials/validation/sync`                                      | `company.payment:update`                               |
| Delete All Financial Data               | `POST /financials/deleteAll`                                            | `company.financial-data:deleteAll` (SUPER\_ADMIN only) |
