Skip to content

Account state machine

ID MOD-007
System SD01
Repo bank-core
Build status Deployed
Deployed Yes
Last commit 35402a8a7d9c6f1e2b5c8d0e4f7a3b6c9d2e5f8a

Accounts move through defined states: Pending → Active → Restricted → Dormant → Closed. State transitions enforce regulatory rules — a Restricted account cannot originate payments.

State transition model

Transitions are evaluated by the pure transition-engine-pure.ts service, which enforces the following rules:

  • PENDING → ACTIVE: Standard path requires a matching row in accounts.kyc_status_mirror with status = 'VERIFIED'. Multi-party account types (trust, community, joint) bypass the single-party KYC mirror gate when the activation is initiated with an approved reason_code from the multi-party module (see below).
  • ACTIVE → RESTRICTED: Requires a restriction_reason from the allowed domain.
  • RESTRICTED → ACTIVE: Reinstatement — triggered by MOD-007 FR-440 reinstatement flow.
  • ACTIVE / RESTRICTED → DORMANT: Triggered when last_transaction_at crosses the dormancy threshold.
  • ANY → CLOSED: Terminal state.

ReasonCode domain

The ReasonCode union type in src/types/account-state.ts controls which callers are permitted to drive specific transitions. The following values are defined:

ReasonCode Used by Purpose
TRUST_GATE_PASS MOD-133 Bypass single-party KYC mirror gate on PENDING→ACTIVE for trust accounts; MOD-133 evaluateActivationGate already verified all trustee and BO identities
COMMUNITY_GATE_PASS MOD-134 Bypass single-party KYC mirror gate on PENDING→ACTIVE for community accounts; MOD-134 evaluateActivationGate verified all signatory identities
JOINT_GATE_PASS MOD-125 Bypass single-party KYC mirror gate on PENDING→ACTIVE for joint accounts; MOD-125 evaluateActivationGate verified all holder identities
SIGNATORY_KYC_DEGRADED MOD-134 ACTIVE→RESTRICTED transition when a community account's verified signatory count drops below the signing-rule minimum

The PENDING→ACTIVE pure validator bypasses the single-party KYC mirror gate when reason_code is TRUST_GATE_PASS, COMMUNITY_GATE_PASS, or JOINT_GATE_PASS. Cross-reference: MOD-133 §Activation gate, MOD-134 §Activation gate, MOD-125 §Activation gate.

RestrictionReason domain

The restriction_reason column CHECK on accounts.accounts (and in lockstep on accounts.account_state_history) accepts the following values:

Value Set by Trigger
SANCTIONS MOD-013 / compliance staff Sanctions match confirmed
FRAUD_INVESTIGATION Fraud operations Manual or automated fraud flag
HARDSHIP_ARRANGEMENT Customer operations Hardship arrangement in place
ADMIN Platform operations Administrative hold
INSUFFICIENT_SIGNATORIES MOD-134 Active verified signatory count drops below the community signing-rule minimum (FR-600). ACTIVE→RESTRICTED. Cleared when KYC is restored and check-signatory-kyc passes.

Note: Adding a new restriction_reason value requires extending the CHECK constraint on both accounts.accounts and accounts.account_state_history in the same migration (the transition engine writes both rows in the same Postgres transaction — see SD01 data model §DB-enforced invariants).

Hardship-flag service (V007)

V007 adds a party-level hardship flag store and four IAM-authenticated Function URL endpoints. The hardship flag is a mutable set/clear record on accounts.party_hardship_flags (one active row per party; see SD01 data model). It is intentionally mutable — the flag is cleared when the hardship arrangement ends, and a party may be re-flagged if a subsequent hardship arrangement is opened.

Endpoints

All four endpoints use AuthType=AWS_IAM. Access is controlled by the broader bank-platform IAM layer; no per-Principal resource policies are applied.

Endpoint Method Description Response codes
hardship-flag-set-url POST Sets the hardship flag for a party. Body: { party_id, flagged_by_module, reason }. Idempotent — 200 on first set; 409 HARDSHIP_FLAG_ALREADY_SET if already flagged. Re-flags a previously cleared party (inserts new row). 200, 409
hardship-flag-clear-url DELETE Clears the active hardship flag. Body: { party_id, cleared_by_module }. 200, 404
hardship-flag-read-url GET Returns { flagged: bool, flagged_at?, flagged_by_module?, reason? }. 200
primary-deposit-account-url GET Returns { account_id } for the party's most-recently-opened ACTIVE deposit account (NZ_TRANSACTION_01 / AU_TRANSACTION_01 / NZ_SAVINGS_01 / AU_SAVINGS_01). 200, 404

SSM output paths

/bank/{env}/mod-007/hardship-flag-set-url
/bank/{env}/mod-007/hardship-flag-clear-url
/bank/{env}/mod-007/hardship-flag-read-url
/bank/{env}/mod-007/primary-deposit-account-url
/bank/{env}/mod-007/party-hardship-flags-table   → "accounts.party_hardship_flags"

Callers

  • MOD-116 (bank-credit) — Day-7 arrears path POSTs to hardship-flag-set-url; discharge handler GETs primary-deposit-account-url.
  • MOD-117 (bank-credit) — On consecutive_drawn_days ≥ 60, POSTs to hardship-flag-set-url with flagged_by_module='MOD-117'.
  • MOD-065 (bank-credit) — On HARDSHIP_RESOLUTION case closure, DELETEs via hardship-flag-clear-url.

Design note: soft FK to SD02

accounts.party_hardship_flags.party_id references party.parties in SD02's bank_kyc DB. Cross-DB FKs are not supported in this Neon deployment (same pattern as V005 account_party_relationships). The column accepts the SD02 UUID identifiers; referential integrity is enforced at the service layer.


Module dependencies

Depends on

Module Title Required? Contract Reason
MOD-001 Double-entry posting engine Required Account state transitions require posting capability — activation and restriction events generate ledger entries.
MOD-002 Immutable transaction log Required State transitions are persisted in the transaction log for auditability and reversal tracing.
MOD-009 eIDV & document verification Required contract/api/ KYC verification status from eIDV is a prerequisite gate for the Pending→Active state transition.
MOD-104 AWS shared infrastructure bootstrap Required AWS shared infrastructure provisioned by MOD-104 (EventBridge buses, S3, KMS, Kinesis, Cognito) is required before this module can be deployed.
MOD-103 Neon database platform bootstrap Required Neon database and schema provisioned by MOD-103 must exist before this module can read or write Postgres.

Required by

Module Title As Contract
MOD-008 Dormancy & escheatment engine Hard dependency
MOD-065 Credit servicing & collections Hard dependency
MOD-111 Term deposit maturity engine Hard dependency
MOD-117 Overdraft management engine Optional enhancement
MOD-118 Member equity and share registry Hard dependency
MOD-125 Joint account management Hard dependency
MOD-130 Notice account management Hard dependency
MOD-138 Deceased customer and estate management Hard dependency
MOD-139 Financial hardship formal variation workflow Hard dependency
MOD-159 Synthetic transaction engine Hard dependency

Policies satisfied

Policy Title Mode How
AML-002 Customer Due Diligence (CDD) Policy GATE Account cannot be activated until KYC status is Verified — GATE enforced at state machine level
AML-007 Sanctions Screening Policy GATE Account is automatically restricted if sanctions match is confirmed — no agent override without approval
PAY-005 Payment Fraud Prevention Policy GATE Fraud-flagged account automatically restricted pending investigation

Capabilities satisfied

(No capabilities mapped)


Part of SD01 — Core Banking Platform Compiled 2026-05-22 from source/entities/modules/MOD-007.yaml