> ## 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.

# Update Invoice

> Replaces the invoice's economic document — the `invoice.updated` action.
The body still carries the FULL document (`categoryId`, dates, memo,
`fieldData`, `lineItems`); links are excluded by schema (they change via
[re-link](/api-reference/financials/re-link-invoice) and
[payee change](/api-reference/financials/change-invoice-payee)).

**The immutability law.** Once an invoice is POSTED (a non-draft
creation, or finalize for drafts), its line items — ids, types, amounts —
and its category are FIXED: an update may change only `incurredDate`,
`dueDate`, `memo`, `fieldData` (and per-line memos), and must echo the
stored line-item set and `categoryId` verbatim. ANY line-item or category
deviation is rejected with `422 LINE_ITEMS_IMMUTABLE`, at any payment
count — zero included. Corrections are void-and-recreate, payments
removed first. A DRAFT edits freely until finalize — the document
(line items and category included) replaces wholesale while drafting.

**Concurrency.** Send the invoice's current `headJournalId` as `If-Match`
(from any read or write response). Stale → `409 IF_MATCH_CONFLICT` with
the current invoice in the body; missing/malformed → `400`.

**Idempotency.** `actionId` is a client-minted uuid; an identical retry
replays the original outcome (the watermark is not re-evaluated on
replay); reuse with a different payload is `409 ACTION_ID_REUSED`.

**Other preconditions (`422`).** Cited types must belong to the cited
category (`UNKNOWN_ID`, reachable on draft edits); newly-introduced
refs must be live (`DEPRECATED_CONFIG`); voided/deleted invoices reject
updates (`INVOICE_VOIDED` / `INVOICE_DELETED`).

**Required permission:** `company.payment:update`




## OpenAPI

````yaml /openapi/generated-external-api.yaml put /api/v1/companies/{companyId}/financials/invoices/{invoiceId}
openapi: 3.0.3
info:
  title: AI Insurance External API
  description: External API for AI Insurance platform
  version: 1.0.0
  contact:
    email: support@aiinsurance.io
servers:
  - url: https://go.aiinsurance.io
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /api/v1/companies/{companyId}/financials/invoices/{invoiceId}:
    put:
      tags:
        - Financials
      summary: Update Invoice
      description: >
        Replaces the invoice's economic document — the `invoice.updated` action.

        The body still carries the FULL document (`categoryId`, dates, memo,

        `fieldData`, `lineItems`); links are excluded by schema (they change via

        [re-link](/api-reference/financials/re-link-invoice) and

        [payee change](/api-reference/financials/change-invoice-payee)).


        **The immutability law.** Once an invoice is POSTED (a non-draft

        creation, or finalize for drafts), its line items — ids, types, amounts
        —

        and its category are FIXED: an update may change only `incurredDate`,

        `dueDate`, `memo`, `fieldData` (and per-line memos), and must echo the

        stored line-item set and `categoryId` verbatim. ANY line-item or
        category

        deviation is rejected with `422 LINE_ITEMS_IMMUTABLE`, at any payment

        count — zero included. Corrections are void-and-recreate, payments

        removed first. A DRAFT edits freely until finalize — the document

        (line items and category included) replaces wholesale while drafting.


        **Concurrency.** Send the invoice's current `headJournalId` as
        `If-Match`

        (from any read or write response). Stale → `409 IF_MATCH_CONFLICT` with

        the current invoice in the body; missing/malformed → `400`.


        **Idempotency.** `actionId` is a client-minted uuid; an identical retry

        replays the original outcome (the watermark is not re-evaluated on

        replay); reuse with a different payload is `409 ACTION_ID_REUSED`.


        **Other preconditions (`422`).** Cited types must belong to the cited

        category (`UNKNOWN_ID`, reachable on draft edits); newly-introduced

        refs must be live (`DEPRECATED_CONFIG`); voided/deleted invoices reject

        updates (`INVOICE_VOIDED` / `INVOICE_DELETED`).


        **Required permission:** `company.payment:update`
      operationId: updateFinancialsInvoice
      parameters:
        - $ref: '#/components/parameters/companyId'
        - name: invoiceId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Invoice identifier
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - actionId
                - categoryId
                - lineItems
              properties:
                actionId:
                  type: string
                  format: uuid
                  description: >-
                    Client-minted idempotency key — becomes the action's journal
                    id. An identical retry replays the original outcome; reuse
                    with a different payload is `409 ACTION_ID_REUSED`
                author:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: >-
                    Optional display label for the source system's author (e.g.
                    the integrator-side user). Stamped as the journal record's
                    display attribution; the acting principal stays the External
                    API service user, so a label can never impersonate an in-app
                    user. Ignored on idempotent replays
                categoryId:
                  type: string
                  format: uuid
                  description: >-
                    The transaction category — changing it requires zero live
                    payments (`422 LIVE_PAYMENTS`)
                incurredDate:
                  type: string
                  format: date
                  description: >-
                    The recognition date for the charges (ISO `YYYY-MM-DD`) —
                    the period the amount lands in. May be in the future while
                    the invoice is policy-linked; on an event-linked or unlinked
                    invoice a future date is `422 FUTURE_DATE`
                dueDate:
                  type: string
                  format: date
                  description: When payment is owed — display metadata only
                memo:
                  type: string
                  description: Free-text memo
                fieldData:
                  type: object
                  additionalProperties: true
                  description: >-
                    Values for the tenant-defined custom invoice fields, keyed
                    by field referenceId — an opaque JSON object
                lineItems:
                  type: array
                  items:
                    $ref: '#/components/schemas/FinancialsV2WriteLineItem'
            examples:
              fullReplacement:
                summary: Replace the document (memo + item amount changed)
                value:
                  actionId: 8c1d3e5f-2a4b-4c6d-8e0f-3b5d7a9c1e24
                  categoryId: 550e8400-e29b-41d4-a716-446655440100
                  incurredDate: '2026-06-01'
                  dueDate: '2026-08-01'
                  memo: Legal defense — revised
                  lineItems:
                    - lineItemId: 550e8400-e29b-41d4-a716-446655440300
                      lineItemTypeId: 550e8400-e29b-41d4-a716-446655440110
                      amountCents: 12000
      responses:
        '200':
          description: The updated invoice — refreshed read state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancialsV2InvoiceWriteResponse'
              examples:
                updated:
                  summary: Updated invoice (new headJournalId)
                  value:
                    journalIds:
                      - 8c1d3e5f-2a4b-4c6d-8e0f-3b5d7a9c1e24
                    invoice:
                      id: 550e8400-e29b-41d4-a716-446655440700
                      invoiceNumber: INV-389538-455
                      categoryId: 550e8400-e29b-41d4-a716-446655440100
                      status: partially_paid
                      linkedEvent:
                        id: 550e8400-e29b-41d4-a716-446655440200
                        displayName: 'Claim #1042'
                      linkedPolicy: null
                      linkedPayee: null
                      incurredDate: '2026-06-01'
                      dueDate: '2026-08-01'
                      memo: Legal defense — revised
                      fieldData: null
                      lineItems:
                        - lineItemId: 550e8400-e29b-41d4-a716-446655440300
                          lineItemTypeId: 550e8400-e29b-41d4-a716-446655440110
                          memo: null
                          amountCents: 12000
                      totalAmountCents: 12000
                      amountPaidCents: 4000
                      balanceDueCents: 8000
                      paidDate: null
                      lastPaymentDate: '2026-06-10'
                      voidedAt: null
                      deletedAt: null
                      createdAt: '2026-06-01T10:30:00.000Z'
                      updatedAt: '2026-06-12T09:00:00.000Z'
                      headJournalId: 8c1d3e5f-2a4b-4c6d-8e0f-3b5d7a9c1e24
                    payments:
                      - id: 550e8400-e29b-41d4-a716-446655440800
                        journalId: 550e8400-e29b-41d4-a716-446655440900
                        lineItemId: 550e8400-e29b-41d4-a716-446655440300
                        amountCents: 4000
                        paymentDate: '2026-06-10'
                        erodeReserves: true
                        memo: 'check #100'
                        createdAt: '2026-06-10T09:00:00.000Z'
        '400':
          description: >-
            Bad Request — a malformed payload, unknown fields (links cannot ride
            an update), or a missing/malformed `If-Match` header
            (`IF_MATCH_REQUIRED` / `IF_MATCH_INVALID`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                ifMatchRequired:
                  summary: Missing If-Match header
                  value:
                    error:
                      code: IF_MATCH_REQUIRED
                      message: >-
                        The If-Match header is required: send the invoice's
                        current headJournalId.
                      userMessages:
                        - >-
                          The If-Match header is required: send the invoice's
                          current headJournalId.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: >-
            Not Found — no invoice with this id, or the company does not have
            this financials surface enabled (indistinguishable by design)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invoiceNotFound:
                  summary: Invoice not found
                  value:
                    error:
                      code: NotFoundError
                      message: Invoice not found
                      userMessages:
                        - Invoice not found
        '409':
          $ref: '#/components/responses/FinancialsV2IfMatchConflict'
        '422':
          $ref: '#/components/responses/FinancialsV2PreconditionFailed'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    companyId:
      name: companyId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Company identifier
    ifMatch:
      name: If-Match
      in: header
      required: true
      schema:
        type: string
        format: uuid
      description: >-
        The invoice's current `headJournalId` — the optimistic-concurrency
        watermark every single-invoice write after creation must send. Read it
        off any invoice read or write response and echo it verbatim (a bare
        uuid; an entity-tag dressing of it — `"uuid"` or `W/"uuid"` — is also
        accepted). Missing or malformed is a `400` (`IF_MATCH_REQUIRED` /
        `IF_MATCH_INVALID`); a stale value is a `409 IF_MATCH_CONFLICT` whose
        body carries the current invoice. An idempotent `actionId` replay
        short-circuits BEFORE the watermark is evaluated.
  schemas:
    FinancialsV2WriteLineItem:
      type: object
      description: >-
        One line item of an invoice write. `lineItemId` is caller-minted and
        stable across updates — payment marks target it. Amounts are integer
        cents; 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.
      required:
        - lineItemId
        - lineItemTypeId
        - amountCents
      properties:
        lineItemId:
          type: string
          format: uuid
          description: >-
            Caller-minted id for the line item — keep it stable across updates
            to preserve the item's identity (payment marks reference it)
        lineItemTypeId:
          type: string
          format: uuid
          description: >-
            A configured line item type OF THE CITED CATEGORY — discover ids
            with `GET
            /api/v1/companies/{companyId}/financials/config/categories`. A type
            outside the invoice's category is `422 UNKNOWN_ID`
        memo:
          type: string
          description: Free-text memo
        amountCents:
          type: integer
          description: >-
            The line item's amount in integer cents — the user-typed magnitude;
            negative = a credit within the item's frame, posting opposite the
            type's expected direction
    FinancialsV2InvoiceWriteResponse:
      type: object
      description: >-
        The response of every single-invoice write — refreshed read state, not
        an ack: the emitted journal id(s), THE invoice row (identical shape to
        the reads — one invoice representation everywhere; its `headJournalId`
        is the next `If-Match`), and the invoice's live payments. An idempotent
        `actionId` replay returns this same shape rebuilt from the original
        outcome, server-minted values included.
      required:
        - journalIds
        - invoice
        - payments
      properties:
        journalIds:
          type: array
          minItems: 1
          items:
            type: string
            format: uuid
          description: >-
            Every journal id the write emitted — the anchor action's id (the
            `actionId` you supplied) first, then any engine-minted siblings (a
            multi-mark payment's additional marks) and any companion the horizon
            rule composed (e.g. the reserve unwind of a pre-horizon eroding
            payment's removal, or a delete's payment sweep)
        invoice:
          $ref: '#/components/schemas/FinancialsV2Invoice'
        payments:
          type: array
          description: The invoice's live payment marks after the write, newest first
          items:
            $ref: '#/components/schemas/FinancialsV2Payment'
    ErrorResponse:
      type: object
      description: Standard error response for all external API endpoints
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: VALIDATION_ERROR
            message:
              type: string
              description: Human-readable error message
              example: 'submissionId: Required field is missing'
            userMessages:
              type: array
              description: >-
                Clean, verbatim-displayable messages — one entry per failure,
                free of error-code tags, field paths, and internal noise.
                Suitable for showing to end users as-is.
              items:
                type: string
              example:
                - Exposures of type 'company' require an address
            details:
              type: array
              description: Additional details for validation errors (field-level errors)
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: The field that caused the error
                    example: submissionId
                  message:
                    type: string
                    description: Description of the field error
                    example: Required field is missing
    FinancialsV2Invoice:
      type: object
      description: >-
        THE invoice representation — the same shape everywhere an endpoint
        returns an invoice (listing rows, the detail read, and every write's
        refreshed-row response). `headJournalId` is the invoice's current
        journal head — the optimistic-concurrency token subsequent writes echo
        back as `If-Match`.
      required:
        - id
        - invoiceNumber
        - categoryId
        - status
        - linkedEvent
        - linkedPolicy
        - linkedPayee
        - incurredDate
        - dueDate
        - memo
        - fieldData
        - lineItems
        - totalAmountCents
        - amountPaidCents
        - balanceDueCents
        - draftAmountPaidCents
        - paidDate
        - lastPaymentDate
        - voidedAt
        - deletedAt
        - approved
        - approvedAt
        - createdAt
        - updatedAt
        - headJournalId
      properties:
        id:
          type: string
          format: uuid
          description: The invoice's id
        invoiceNumber:
          type: string
          description: The invoice's system-minted display number
        categoryId:
          type: string
          format: uuid
          description: The transaction category the invoice belongs to
        status:
          type: string
          enum:
            - no_charges
            - owed
            - partially_paid
            - paid
            - voided
            - deleted
          description: >-
            Derived status, first match wins: `deleted` → `voided` →
            `no_charges` (every line item's amount is zero) → `paid` (every line
            item is settled — its amount minus its live marks is exactly zero) →
            `owed` (no live payment marks) → `partially_paid`. Settlement is per
            line: no scalar makes a document paid, and a zero-due document with
            unsettled lines still reads `owed`
        linkedEvent:
          type: object
          nullable: true
          description: >-
            The linked event, display-ready (the server resolves the display
            name), or `null` when not linked to an event
          required:
            - id
            - displayName
          properties:
            id:
              type: string
              format: uuid
              description: The linked event's id
            displayName:
              type: string
              description: The linked event's display name
        linkedPolicy:
          type: object
          nullable: true
          description: >-
            The linked policy, display-ready, or `null`. An invoice links to an
            event or a policy, never both
          required:
            - id
            - displayName
          properties:
            id:
              type: string
              format: uuid
              description: The linked policy's id
            displayName:
              type: string
              description: The linked policy's display name
        linkedPayee:
          type: object
          nullable: true
          description: >-
            The invoice's payee, display-ready, or `null` when none is set.
            `entityType` is an open string (e.g. `Person`, `Organization`) — the
            payee vocabulary is the entity module's, not part of this contract
          required:
            - id
            - entityType
            - displayName
          properties:
            id:
              type: string
              format: uuid
              description: The payee entity's id
            entityType:
              type: string
              description: The payee's entity type (e.g. Person, Organization)
            displayName:
              type: string
              description: The payee's display name
        incurredDate:
          type: string
          format: date
          nullable: true
          description: The date the charges were incurred (ISO `YYYY-MM-DD`), or `null`
        dueDate:
          type: string
          format: date
          nullable: true
          description: When payment is owed (display metadata only), or `null`
        memo:
          type: string
          nullable: true
          description: Free-text memo, or `null`
        fieldData:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Values for the tenant-defined custom invoice fields, keyed by field
            referenceId — an opaque JSON object, or `null`
        lineItems:
          type: array
          description: The invoice's line items
          items:
            $ref: '#/components/schemas/FinancialsV2InvoiceLineItem'
        totalAmountCents:
          type: integer
          description: >-
            Sum of the ORIENTED line-item amounts (a payable-direction line
            counts +, a receivable-direction line counts −), in integer cents —
            the net cash the document commits to move
        amountPaidCents:
          type: integer
          description: >-
            Sum of the ORIENTED live payment marks, in integer cents — the net
            cash moved so far
        balanceDueCents:
          type: integer
          description: >-
            `totalAmountCents - amountPaidCents`, in integer cents — the net
            cash remaining to move: positive = out, negative = in. May move
            non-monotonically as opposite-direction lines settle
        draftAmountPaidCents:
          type: integer
          nullable: true
          description: >-
            A DRAFT's annex-derived paid total: the oriented net-cash sum of the
            payment marks the draft was born carrying (the same frame as
            `amountPaidCents`, which counts LIVE posted marks only and reads 0
            while drafting) — what a draft's underlying payment picture will
            read once finalized. `null` on non-draft invoices (the annex is
            spent at finalize). Display-only — never a posted ledger amount
        paidDate:
          type: string
          format: date
          nullable: true
          description: >-
            Payment date of the mark that first made every line item settled;
            clears (`null`) whenever any line reopens
        lastPaymentDate:
          type: string
          format: date
          nullable: true
          description: Date of the most recent live payment, or `null`
        voidedAt:
          type: string
          format: date-time
          nullable: true
          description: When the invoice was voided, or `null`
        deletedAt:
          type: string
          format: date-time
          nullable: true
          description: When the invoice was deleted, or `null`
        approved:
          type: boolean
          description: >-
            The approval projection: `true` after `invoice.approved`, cleared
            only by `invoice.unapproved` — an edit never resets approval (the
            approval-relevant facts, line items and category, are immutable once
            posted). Only meaningful while the approvals feature is enabled;
            reads `false` otherwise
        approvedAt:
          type: string
          format: date-time
          nullable: true
          description: >-
            When the approval landed (`invoice.approved`), or `null` while
            unapproved — `approved` is exactly `approvedAt != null`. Like
            `approved`, only meaningful while the approvals feature is enabled;
            reads `null` otherwise
        createdAt:
          type: string
          format: date-time
          description: When the invoice was created
        updatedAt:
          type: string
          format: date-time
          description: When the invoice last changed
        headJournalId:
          type: string
          format: uuid
          description: >-
            The invoice's current journal head — send it as `If-Match` on the
            next write to this invoice; a mismatch is a `409` carrying the
            current row
    FinancialsV2Payment:
      type: object
      description: >-
        A recorded payment MARK on an invoice: one row = one per-line settlement
        mark against `lineItemId`. `amountCents` is signed in the LINE's frame —
        its sign matches the line's open remaining, and the mark settles that
        line toward zero. One journal action per mark (`journalId` is the record
        that created it), each independently removable by `id`; one gesture
        (e.g. paying a document's whole balance due) may create several marks.
        `erodeReserves` is the caller's choice at record time (`null` where the
        invoice's category has no reserves to erode).
      required:
        - id
        - journalId
        - lineItemId
        - amountCents
        - paymentDate
        - erodeReserves
        - memo
        - createdAt
      properties:
        id:
          type: string
          format: uuid
          description: The payment mark's id — the handle for removing it
        journalId:
          type: string
          format: uuid
          description: The journal record that recorded the mark
        lineItemId:
          type: string
          format: uuid
          description: The invoice line item the mark settles
        amountCents:
          type: integer
          description: >-
            The mark's amount in signed integer cents, in its line item's frame
            — the sign matches the line's remaining and the mark settles it
            toward zero
        paymentDate:
          type: string
          format: date
          description: The payment date (ISO `YYYY-MM-DD`)
        erodeReserves:
          type: boolean
          nullable: true
          description: >-
            Whether the mark erodes the linked event's reserves; `null` where
            the invoice's category carries no reserves
        memo:
          type: string
          nullable: true
          description: Free-text memo, or `null`
        createdAt:
          type: string
          format: date-time
          description: When the mark was recorded
    FinancialsV2IfMatchConflictError:
      type: object
      description: >-
        The conflict envelope of an `If-Match` invoice write.
        `IF_MATCH_CONFLICT`: the supplied watermark is stale —
        `error.currentInvoice` is the invoice's CURRENT row; re-read, reconcile,
        and retry with its `headJournalId`. `ACTION_ID_REUSED`: the `actionId`
        names a different recorded action (no `currentInvoice`).
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - IF_MATCH_CONFLICT
                - ACTION_ID_REUSED
              description: Which conflict occurred
            message:
              type: string
              description: Human-readable error message
            userMessages:
              type: array
              description: Clean, verbatim-displayable messages
              items:
                type: string
            currentInvoice:
              allOf:
                - $ref: '#/components/schemas/FinancialsV2Invoice'
              description: >-
                Present exactly when `code` is `IF_MATCH_CONFLICT` — the
                invoice's current row; its `headJournalId` is the fresh
                `If-Match` value for the retry
    FinancialsV2PreconditionError:
      type: object
      description: >-
        A failed write precondition (`422`): the request was well-formed but the
        books reject it. Preconditions fail closed — a rejected write leaves no
        journal record, no ledger change, and no read-state change. `error.code`
        is one of the STABLE financials error codes (single source:
        `FINANCIALS_V2_PRECONDITION_CODES` in the engine's error module,
        re-exported to this surface as `FINANCIALS_V2_API_ERROR_CODES`) —
        messages may be reworded, the codes will not.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - LIVE_PAYMENTS
                - AMOUNT_EXCEEDS_BALANCE_DUE
                - INVALID_AMOUNT
                - LINE_ITEMS_IMMUTABLE
                - INVOICE_DELETED
                - INVOICE_VOIDED
                - INVOICE_DRAFT
                - INVOICE_NOT_DRAFT
                - PAYMENT_NOT_FOUND
                - ERODE_RESERVES_MISMATCH
                - ERODE_REQUIRES_EVENT
                - DEPRECATED_CONFIG
                - NOT_RESERVED_CATEGORY
                - FUTURE_DATE
                - UNKNOWN_ID
                - LINK_TARGET_MISSING
                - UNAPPROVE_PAID
                - NOT_APPROVED
                - POLICY_INVOICE_BATCH_ONLY
              description: >-
                The stable precondition code — one per guard. `LIVE_PAYMENTS`
                (the action — re-link, payee change, or void — needs the
                invoice's live payments removed first);
                `AMOUNT_EXCEEDS_BALANCE_DUE` (a payment mark's magnitude
                overshoots its line item's remaining — per line, never a
                document scalar); `INVALID_AMOUNT` (a zero mark, or a mark whose
                sign opposes its line item's open remaining — a `payBalanceDue`
                against a document with nothing open included);
                `LINE_ITEMS_IMMUTABLE` (a posted invoice's line items — ids,
                types, amounts — and its category are fixed from the posting
                moment: any post-posting change is rejected, at any payment
                count; corrections are void-and-recreate, payments removed
                first; drafts edit freely until finalize); `INVOICE_DELETED` /
                `INVOICE_VOIDED` (lifecycle closures); `INVOICE_DRAFT` (drafts
                are unposted — money may not touch one: recording a payment on
                or voiding a draft is rejected; finalize it first);
                `INVOICE_NOT_DRAFT` (finalize targets exactly a draft);
                `PAYMENT_NOT_FOUND` (no live payment mark with that id on that
                invoice); `ERODE_RESERVES_MISMATCH` / `ERODE_REQUIRES_EVENT`
                (the erode flag missing on a reserved category or present on
                operating; eroding needs a linked event — there is no headroom
                bound: the remaining reserve is a signed balance and may cross
                zero); `DEPRECATED_CONFIG` / `NOT_RESERVED_CATEGORY` /
                `UNKNOWN_ID` (cited configuration guards); `FUTURE_DATE`;
                `LINK_TARGET_MISSING` (the named link target does not exist);
                `UNAPPROVE_PAID` (an approval cannot be revoked once payments
                have been recorded — remove them first); `NOT_APPROVED` (money
                cannot post against an unapproved invoice AND the calling key
                does not hold `company.payment:approve`, so it cannot approve it
                either — a key that holds the permission auto-approves instead
                of failing here); `POLICY_INVOICE_BATCH_ONLY` (the write touches
                a POLICY-LINKED invoice's document — creating one with
                `links.policy`, or updating, voiding, restoring, deleting, or
                re-linking one that is already policy-linked. A policy's
                invoices are issued as one set so that every price component of
                the policy's pricing contract stays netted to its own line item,
                so they are written only by the policy invoice batch: `POST
                /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch`.
                Change the policy or submit an explicit batch and the invoices
                are reissued. Payments, payee changes, and approvals on a policy
                invoice are unaffected)
            message:
              type: string
              description: Human-readable description of the failed precondition
            userMessages:
              type: array
              description: Clean, verbatim-displayable messages
              items:
                type: string
    FinancialsV2InvoiceLineItem:
      type: object
      description: A stored invoice line item as rendered on invoice reads.
      required:
        - lineItemId
        - lineItemTypeId
        - memo
        - amountCents
      properties:
        lineItemId:
          type: string
          format: uuid
          description: The line item's id
        lineItemTypeId:
          type: string
          format: uuid
          description: >-
            The configured line item type this item cites — discover ids with
            `GET /api/v1/companies/{companyId}/financials/config/categories`
        memo:
          type: string
          nullable: true
          description: Free-text memo, or `null`
        amountCents:
          type: integer
          description: >-
            The line item's amount in integer cents — the magnitude a user would
            type; 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
  responses:
    Unauthorized:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingApiKey:
              summary: Missing API key
              value:
                error:
                  code: AuthenticationError
                  message: API key authentication required
                  userMessages:
                    - API key authentication required
            invalidApiKey:
              summary: >-
                Invalid API key (e.g. unknown key, or a Bearer token used
                instead of an API key)
              value:
                error:
                  code: AuthenticationError
                  message: Invalid API key
                  userMessages:
                    - Invalid API key
    Forbidden:
      description: Forbidden - Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            insufficientPermissions:
              summary: Insufficient permissions
              value:
                error:
                  code: AuthorizationError
                  message: User is not authorized to perform the requested action
                  userMessages:
                    - User is not authorized to perform the requested action
            companyMismatch:
              summary: A valid API key naming another company in the URL
              value:
                error:
                  code: AuthorizationError
                  message: API key is not scoped to the requested company
                  userMessages:
                    - API key is not scoped to the requested company
    FinancialsV2IfMatchConflict:
      description: >-
        Conflict — a stale `If-Match` watermark (`IF_MATCH_CONFLICT`: the body
        carries the invoice's CURRENT row; re-read, reconcile, retry with its
        `headJournalId`), or an `actionId` reused by a different action
        (`ACTION_ID_REUSED`: supply a fresh uuid — an identical retry replays
        instead).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/FinancialsV2IfMatchConflictError'
          examples:
            ifMatchConflict:
              summary: Stale If-Match — the body carries the current invoice
              value:
                error:
                  code: IF_MATCH_CONFLICT
                  message: >-
                    If-Match 550e8400-e29b-41d4-a716-446655440901 does not match
                    the invoice's current head journal id
                  userMessages:
                    - >-
                      If-Match 550e8400-e29b-41d4-a716-446655440901 does not
                      match the invoice's current head journal id
                  currentInvoice:
                    id: 550e8400-e29b-41d4-a716-446655440700
                    invoiceNumber: INV-389538-455
                    categoryId: 550e8400-e29b-41d4-a716-446655440100
                    status: partially_paid
                    linkedEvent:
                      id: 550e8400-e29b-41d4-a716-446655440200
                      displayName: 'Claim #1042'
                    linkedPolicy: null
                    linkedPayee: null
                    incurredDate: '2026-06-01'
                    dueDate: null
                    memo: Legal defense
                    fieldData: null
                    lineItems:
                      - lineItemId: 550e8400-e29b-41d4-a716-446655440300
                        lineItemTypeId: 550e8400-e29b-41d4-a716-446655440110
                        memo: null
                        amountCents: 10000
                    totalAmountCents: 10000
                    amountPaidCents: 4000
                    balanceDueCents: 6000
                    paidDate: null
                    lastPaymentDate: '2026-06-10'
                    voidedAt: null
                    deletedAt: null
                    createdAt: '2026-06-01T10:30:00.000Z'
                    updatedAt: '2026-06-10T09:00:00.000Z'
                    headJournalId: 550e8400-e29b-41d4-a716-446655440900
            actionIdReused:
              summary: actionId reused by a different action
              value:
                error:
                  code: ACTION_ID_REUSED
                  message: >-
                    actionId 550e8400-e29b-41d4-a716-446655440000 was already
                    used by a different action; supply a fresh uuid (or retry
                    with the identical action to replay it)
                  userMessages:
                    - >-
                      actionId 550e8400-e29b-41d4-a716-446655440000 was already
                      used by a different action; supply a fresh uuid (or retry
                      with the identical action to replay it)
    FinancialsV2PreconditionFailed:
      description: >-
        Unprocessable — a write precondition failed. Fails closed: no journal
        record, no ledger change, no read-state change. `error.code` is one of
        the stable financials error codes; each operation's description names
        its signature codes.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/FinancialsV2PreconditionError'
          examples:
            livePayments:
              summary: The action requires the invoice's live payments removed first
              value:
                error:
                  code: LIVE_PAYMENTS
                  message: >-
                    The invoice has live payments; remove them before this
                    action
                  userMessages:
                    - >-
                      The invoice has live payments; remove them before this
                      action
            unknownId:
              summary: >-
                A cited configuration id is unknown (or outside the cited
                category)
              value:
                error:
                  code: UNKNOWN_ID
                  message: lineItemTypeId does not belong to the cited category
                  userMessages:
                    - lineItemTypeId does not belong to the cited category
            policyInvoiceBatchOnly:
              summary: >-
                Policy-linked invoice documents are changed only as one policy
                batch
              value:
                error:
                  code: POLICY_INVOICE_BATCH_ONLY
                  message: >-
                    Policy-linked invoice documents can only be changed through
                    POST
                    /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch
                  userMessages:
                    - >-
                      Policy-linked invoice documents can only be changed
                      through POST
                      /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch
    InternalServerError:
      description: Internal Server Error - Unexpected error occurred
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            internalError:
              summary: Unexpected server error
              value:
                error:
                  code: UncaughtActionError
                  message: Uncaught error occurred in <actionName>
                  userMessages:
                    - An unexpected error occurred. Please try again later.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        API key authentication. Send your raw API key as the `Authorization`
        header value with NO scheme prefix — `Authorization: YOUR-API-KEY`. Do
        NOT prefix it with `Bearer ` or `ApiKey `, and do not use an `X-API-Key`
        header; those are not accepted.

````