# Invoices

A billing invoice is an immutable, numbered document. The relational fields
you see in responses (totals, dates, status) mirror a frozen, schema-valid
payload snapshotted at issue time; the PDF renders from that snapshot.

## Lifecycle

`draft → issued → sent → paid`, with `overdue` for a sent document still
unpaid past its due date, and three corrective exits:

- `voided` — the document was cancelled before money moved (`voidedAt`,
  `voidReason`).
- written off — collection was abandoned (`writtenOffAt`, `writeOffReason`).
- credit note — a separate document (`invoiceType: "credit_note"`) naming the
  original in `originalInvoiceNumber`.

`paidVia` records how a paid document settled: `payment_link` (the card rail —
either the buyer visiting the hosted page, or an off-session charge on their
saved card when the seller has auto-collection enabled), `bank_transfer` (a
deposit on the collection account named on the document) or `credit` (stored
credit covered it in full at issue).

A document can therefore reach `paid` without you doing anything, and can
appear on `GET /invoices` without anyone creating it: whether terms and
metered usage are invoiced on a schedule, and whether saved cards are charged
off-session, are per-seller operator switches. See **Sandbox & testing** for
the switches themselves.

## Numbering

Every issued document carries TWO numbers, and they are not interchangeable.

`invoiceNumber` (`UK2607AB-2607AAC`) is the document's identity: unique across
every Fynex invoice family, allocated from the shared per-(agreement, month)
sequence, and **also the bank payment reference** — it is the only string a
buyer may quote on a transfer, and it is what `bankTransfer.paymentDescription`
returns.

`customerDocumentNumber` (`0000042`) is the document's position in that
customer's own series: cumulative for the life of the (seller, customer) pair,
never reset, and counted independently for each customer. Credit notes take the
next position like any other document, and a voided document keeps the one it
had. It is a convenience reference printed as "Your document no." — unique only
within the pair, so it is never a lookup key and never a payment reference.
Absent on documents issued before the series existed; those are not backfilled.

## Numbering

An invoice number is `<agreement>-<YYMM><order>`: your agreement number, a
dash, the issue month as `YYMM`, and a three-letter order within that month
(`AAA`, `AAB`, …) — for example `UK2607AA-2608AAB` is the second document
issued in August 2026 under agreement `UK2607AA`. The series is allocated
inside the issuing transaction, so it is gapless per month, and it is shared
with top-up invoices so the two document families can never collide. Numbers
are what `corrects` and `originalInvoiceNumber` reference, so store them
verbatim — do not parse meaning out of the order suffix.

`customerName` and `customerEmail` carry who the document was addressed to at
issue — taken from the frozen payload, so a customer later renaming themselves
does not rewrite an issued legal record. They are returned by the
single-invoice read and by `send`, and omitted from list rows.

## Endpoints

- `GET /billing-api/v1/invoices` — list, newest first, paged with
  `cursor`/`limit`. Filters: `contractId`, `sellerCustomerId`, `status`,
  `invoiceType` (`standard`, `credit_note`, `simplified`, `modified`),
  `origin` (`recurring`, `usage`, `milestone`, `project`, `one_time`, `adhoc`,
  `marketplace`; `subscription` is a deprecated alias of `recurring`),
  `corrects` (credit notes against an invoice number), `issuedFrom`/`issuedTo`
  (half-open date range).
- `POST /billing-api/v1/invoices` — compose, number and (by default) send a
  document in one call, behind a required `Idempotency-Key`. It is issued and
  immutable the moment the call answers `201`; the same key replays the
  document with `200` and no link, and a key whose request differs in its
  contract, customer, currency, `invoiceType`, `dueDate`, net line total or
  line count answers `422` naming the field. There are no drafts on this API:
  send explicit lines with `unitPriceMinor`. `invoiceType` accepts `standard`
  (the default), `simplified` and `modified`; **`credit_note` is refused with
  `400`** — a correction is raised against the document it corrects, which this
  request cannot express, and there is no `originalInvoiceNumber` field on it.
  Correct a mistake in the dashboard.
- `GET /billing-api/v1/invoices/{invoiceId}` — one invoice with its lines.
- `GET /billing-api/v1/invoices/{invoiceId}/pdf` — the rendered PDF.
- `POST /billing-api/v1/invoices/{invoiceId}/send` — materialize the
  collection instrument: creates the invoice's hosted payment link and returns
  the invoice together with `paymentLinkUrl`. **It emails your customer**
  whenever the document carries a buyer email; there is no per-request
  suppression in v1, so the first call is customer-facing. The invoice is
  itself the collection anchor, so retries and simultaneous calls return the
  one existing link and do not send another email — no idempotency header is
  needed. Not applicable (`422`) to bank-transfer documents, to documents
  outside `issued`/`sent`/`overdue` (a `draft`, or one already `paid`,
  `voided` or `written_off`), or to documents with nothing left to collect.

## Amounts

`grandTotalMinor` is the legal document total. `collectibleMinor` is what
collection asks the customer for: the grand total less `creditAppliedMinor`
(stored credit drawn down at issue).

`creditSettledMinor` reports only the part discharged by that stored credit —
it is **not** a payment total, and a card- or transfer-paid invoice reports
`0`. Neither amount changes when the invoice is paid: `status` and `paidVia`
are what tell you the outcome.

`outstandingMinor` is the one to age in an AR report. `collectibleMinor` is
frozen at issue and stays there, so it keeps asking for the full amount after a
credit note, a write-off or any other adjustment; `outstandingMinor` is derived
from the adjustment ledger on every read and reports what is genuinely still
owed (`0` for a voided document). It is omitted when the adjustment ledger is
unavailable — absence means **unknown**, not `0`.
