---
id: CAP-ACC-REC-002
title: Correct payout records while preserving finalized money
feature: FEAT-ACC-REC-01
product_area: accounting
module: reconciliation
revision: 2
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - 'react-console acceptance inventory CAP-ACC-REC-002 (edit, void and evidence part)'
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§2 payouts: correction, void)'
  - 'services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§4: architecture §6 INV-08, INV-09)'
  - 'services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D1 (no silent edits after finalize)'
purpose: Finance staff need to repair entry errors and retain a visible cancellation history.
outcomes:
  - Corrections remain versioned and update the open explanation.
  - Cancellation preserves the entry and releases its reference.
  - Finalized financial details remain protected.
concepts:
  - CON-ACC-REC-001
actors:
  - ACT-ACC-017
  - ACT-ACC-018
surfaces:
  - api
preconditions: []
postconditions: []
permissions:
  - key: accounting.payouts.update
    where_checked: api
    scope: tenant
  - key: accounting.payouts.delete
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-ACC-REC-002-01
    proved_by: [ATS-ACC-REC-002-01, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: >-
      Payout correction replaces the editable fields using the saved version, clears omitted optional values
      and rejects a stale version.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#replaces
      the editable fields at the next version, stamped by the editor;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the payout at the next
      version with its new fields;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a replacement at
      another version with 409;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a reference another live payout of the merchant account has;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a bank account of another business unit
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope)
  - id: BR-CAP-ACC-REC-002-02
    proved_by: [ATS-ACC-REC-002-01, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: >-
      Correction applies the original duplicate and bank compatibility checks, refusing an occupied live
      reference or a bank from another unit.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a reference another live payout of the merchant account has;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the payout at the next
      version with its new fields;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a replacement at
      another version with 409;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a bank account of another business unit
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope)
  - id: BR-CAP-ACC-REC-002-03
    proved_by: [ATS-ACC-REC-002-02, ATS-ACC-REC-002-09, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: >-
      An amount correction updates the open reconciliation’s linked amount, payout total, difference and
      version in the same transaction, and records that change.
    kind: calculation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#carries
      a new amount into the open batch’s link and payouts total, bumping the batch’s version;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#records
      the open batch’s change with the edit;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#recomputes the open batch’s
      payouts total from the new amount
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope); journey 0593
  - id: BR-CAP-ACC-REC-002-04
    proved_by: [ATS-ACC-REC-002-03, TST-ACC-REC-025, TST-ACC-REC-033]
    statement: >-
      The service refuses correction, verification, void and evidence attachment after finalization; the
      database protects financial fields while allowing a note-only change.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: observed
    source: 'services-accounting:modules/reconciliation/services/persistence/payouts.ts:updatePayout'
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§4: the architecture §6 invariants hold;
      INV-09)
  - id: BR-CAP-ACC-REC-002-05
    proved_by: [ATS-ACC-REC-002-03, TST-ACC-REC-025, TST-ACC-REC-033]
    statement: A payout’s currency and entry origin remain fixed after creation.
    kind: invariant
    enforced_at:
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      a change to the currency (write-once);
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a payout in a finalized
      reconciliation with 409 payout-reconciled;
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      a change to the amount of a payout in a finalized batch (INV-09);
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      a change to the accounting date of a payout in a finalized batch;
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#lets a
      payout in a finalized batch take a note;
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      a change to the origin (write-once)
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§4: the architecture §6 invariants hold;
      INV-02)
  - id: BR-CAP-ACC-REC-002-06
    proved_by: [ATS-ACC-REC-001-12, ATS-ACC-REC-002-04, ATS-ACC-REC-002-10, ATS-ACC-REC-012-07, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: >-
      Void requires a reason and request key and fails while an open or finalized reconciliation holds the
      payout; an already voided payout has no further lifecycle actions.
    kind: lifecycle
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a payout in an open reconciliation with payout-in-reconciliation;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a payout in an open
      reconciliation with 409 payout-in-reconciliation;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a payout in a finalized reconciliation with payout-reconciled;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the payout voided with
      its reason; services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the same
      voided payout for the same key and body;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a payout already voided with payout-voided;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#voids
      the payout with its reason, stamped by the voider, and allows it nothing more
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope); journey 0593
  - id: BR-CAP-ACC-REC-002-07
    proved_by: [ATS-ACC-REC-001-12, ATS-ACC-REC-002-04, ATS-ACC-REC-002-10, ATS-ACC-REC-012-07, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: A void preserves the payout and its recorded reason for review in the list.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#voids
      the payout with its reason, stamped by the voider, and allows it nothing more;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a payout in an open
      reconciliation with 409 payout-in-reconciliation;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a payout in a finalized reconciliation with payout-reconciled;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the payout voided with
      its reason; services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#answers the same
      voided payout for the same key and body;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a payout already voided with payout-voided
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope)
  - id: BR-CAP-ACC-REC-002-08
    proved_by: [ATS-ACC-REC-002-05, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: Evidence is limited to fifty distinct files per payout and cannot be added after void or reconciliation.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a fifty-first file with evidence-limit;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses the same asset twice
      with 409 evidence-already-attached;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#refuses
      a voided payout with payout-voided;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a payout in a finalized
      reconciliation with 409 payout-reconciled
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§4: the architecture §6 invariants hold;
      INV-19)
  - id: BR-CAP-ACC-REC-002-09
    proved_by: [ATS-ACC-REC-002-06, TST-ACC-REC-090]
    statement: >-
      State controls the offered actions: an unheld payout allows all lifecycle commands, an open-held payout
      excludes void, and a finalized or voided payout allows none.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/unit/rules/payout-rules.spec.js#allows all but void in
      an open reconciliation;
      services-accounting:modules/reconciliation/tests/unit/rules/payout-rules.spec.js#allows every command
      outside a reconciliation;
      services-accounting:modules/reconciliation/tests/unit/rules/payout-rules.spec.js#allows nothing in a
      finalized reconciliation;
      services-accounting:modules/reconciliation/tests/unit/rules/payout-rules.spec.js#allows nothing once
      voided
    origin: >-
      services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (plan approved 2026-10-22 10:40 ET; §2
      scope)
  - id: BR-CAP-ACC-REC-002-10
    proved_by: [ATS-ACC-REC-002-07, TST-ACC-REC-033]
    statement: Payout deletion is prohibited except during complete workspace purge.
    kind: invariant
    enforced_at:
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      the owner a delete outside a tenant purge;
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#refuses
      the application role a delete (no DELETE privilege);
      services-accounting:modules/reconciliation/tests/integration/db/payouts.table.integration.spec.js#lets
      the owner delete inside a tenant purge
    origin: none
  - id: BR-CAP-ACC-REC-002-11
    proved_by: [ATS-ACC-REC-002-08, TST-ACC-REC-025, TST-ACC-REC-057]
    statement: >-
      Update governs correction and evidence, delete governs void, and audit history records the correction’s
      changed fields and the void.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a principal without
      accounting.payouts.delete with 403;
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a principal without
      accounting.payouts.update with 403;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#records
      the edit with the fields it changed and the versions;
      services-accounting:modules/reconciliation/tests/integration/services/persistence/payouts.integration.spec.js#records
      the void with its reason and key
    origin: 'services-accounting:docs/milestones/plans/ACCT-M8-plan.md#D10 (§2: permissions)'
scenarios:
  - id: SC-CAP-ACC-REC-002-01
    given: An open draft holds the payout.
    when: Request payout void.
    then: The active reconciliation prevents void.
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#refuses a payout in an open
      reconciliation with 409 payout-in-reconciliation
  - id: SC-CAP-ACC-REC-002-02
    given: The sample deposit belongs to a finalized explanation.
    when: Replace its amount.
    then: The service refuses correction of reconciled money.
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/e2e/reconciliation-scenario.e2e.spec.js#step 13:
      refuses an edit of the finalized payout’s amount with 409 payout-reconciled
edge_cases:
  - id: SC-CAP-ACC-REC-002-03
    given: An open explanation includes the payout.
    when: Correct its reported amount.
    then: The draft’s payout total updates with that correction.
    provenance: test
    source: >-
      services-accounting:modules/reconciliation/tests/e2e/payouts.e2e.spec.js#recomputes the open batch’s
      payouts total from the new amount
constraints: []
depends_on:
  - CAP-ACC-REC-001
related:
  - CAP-ACC-REC-008
  - CAP-ACC-REC-012
journeys:
  - JNY-ACC-REC-002
open_questions: []
evidence:
  - >-
    Examined supporting service material: payouts.routes.ts (updatePayout, voidPayout, attachPayoutEvidence),
    payouts.schemas.ts, services/persistence/payouts.ts, payout-evidence-files.ts, payout-refusals.ts,
    services/rules/payout-rules.ts; db/payouts.table.ts, reconciliation-evidence.table.ts; tests
    e2e/payouts.e2e.spec.js, integration payouts.integration.spec.js, db/payouts.table.integration.spec.js,
    unit/rules/payout-rules.spec.js.
  - >-
    Examined supporting console material: lib/accounting/reconciliation/client.ts (no payout update or void
    call); acceptance journeys ACT-ACC-0592, ACT-ACC-0593.
controls: [TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-DAT-001, TC-DAT-004, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005]
exercised_by: [FAS-ACC-REC-01-02, FAS-ACC-REC-01-03, FAS-ACC-REC-01-04]
suites: [TST-ACC-REC-025, TST-ACC-REC-033, TST-ACC-REC-057, TST-ACC-REC-090]
---

# Correct payout records while preserving finalized money

## Purpose

Finance staff need to repair entry errors and retain a visible cancellation history.

## How It Works

A bookkeeper reviews the payout before replacing editable information. Rhythm checks the version and whether an active explanation restricts the requested change. An amount correction carries through to an open draft rather than leaving the explanation behind. A mistaken unheld entry can instead be voided with a reason. Finalized money requires correction through the reconciliation’s reversal workflow.

## Relationships

Creation supplies the payout; draft abandonment releases its holdings; finalized corrections proceed through reversal.
