CommunityPay brings a bank's activity into the books four ways: a CSV file, a BAI2 file, an OFX or QFX file, and a daily bank feed. The machine matches a bank transaction to a posted entry only when the match is exact to the cent and no other entry fits. A person decides everything else. Reconciling a month clears the matched entries, and no match posts an entry. This page describes the pipeline from upload to reconciliation and its relation to the ledger integrity scan.
Sources
| Source | Arrives as | Detection | Notes |
|---|---|---|---|
| BAI2 | .bai2, .bai file | First non-empty line starts with 01, |
Commercial banking standard. Control totals, more than one hundred transaction type codes, integrity verification. |
| OFX/QFX | .ofx, .qfx file | OFXHEADER: or <OFX> in the first kilobyte |
Supports both OFX 1.x (SGML) and 2.x (XML). Credit card statements supported. |
| CSV | .csv file | The default when neither signature is found | Delimiter and encoding auto-detected. Common header names map to columns automatically. |
| Bank feed | Read from the bank once a day | Not a file | Posted transactions and the bank's live balance, assembled into one statement for each calendar month. |
The format of a file is detected from its content; nobody selects it. BAI2 and OFX files are structured, so they parse directly. CSV files need a column-mapping step because layouts vary by bank. Each file is hashed when it is uploaded, and a file that was already imported is refused.
The Bank Feed
The bank feed reads the bank's posted transactions and balance once a day through the processor's bank-connection service. Connecting or disconnecting a feed takes bookkeeping authority in the association. The feed never posts an entry.
A feed is reconciled one calendar month at a time, in order. Each month has one statement, and an empty month still gets one, because it still has to be reconciled. A month is in one of four states:
- Collecting: the month, or the date windows of its last lines, are still open.
- Established: the completeness proof ties, and the continuity checks hold. The balances are derived and frozen at the first qualifying reading.
- Not established: the proof cannot be made. The person types the balances at Reconcile.
- Failed: the proof was computed and does not tie.
The completeness proof asks whether the feed holds every transaction since the last completed reconciliation. The bank's posted balance, less every feed transaction posted since that reconciliation, must equal that reconciliation's ending balance to the cent. A transaction the bank reports after its month was reconciled is carried into the earliest open statement with its true dates.
Importing a File
Step 1: Upload
On the Bank Statements page, choose Import Statement. Importing needs authority to keep the books.
Page title: Import Bank Statement — Step 1 of 3: Select bank account and upload statement file.
The person selects an active bank account and uploads a file. Accepted extensions: .csv, .bai2, .bai, .ofx, .qfx. Beginning and ending balances may be typed. A balance the file carries is used instead, and an OFX file carries no beginning balance.
Button: Upload and Continue
BAI2 and OFX files parse in the same step and go to review. A CSV file goes to Step 2.
Step 2: Map Columns (CSV only)
Page title: Map Columns — Step 2 of 3: Map CSV columns to transaction fields.
The page shows a preview of the detected headers and sample rows. The person maps each field with a dropdown:
| Field | Required | Description |
|---|---|---|
| Transaction Date | Yes | The date of the bank transaction |
| Post Date | No | Settlement date, if different from the transaction date |
| Description | Yes | Transaction description or memo |
| Amount | Conditional | Single amount column (positive = deposit, negative = withdrawal) |
| Debit Column | Conditional | Separate debit amount column, as an alternative to a single Amount |
| Credit Column | Conditional | Separate credit amount column |
| Check Number | No | Check number for check transactions |
| Reference | No | Bank reference or confirmation number |
Either Amount or Debit and Credit columns are required. If an Amount column is mapped, it is the one used.
Additional options:
- Reverse sign convention: For banks that use positive for withdrawals and negative for deposits.
- Date Format Override: An explicit date format, such as
%m/%d/%Y. Leave blank for auto-detection.
A sidebar collects optional statement balances. A statement whose balances tie to its transactions is proven, which the machine requires before it matches anything on a file.
Button: Parse and Continue
Step 3: Review
Page heading: Review. The browser tab reads Review Matches for an imported file, and the month for a bank feed statement.
The page shows the statement's period and balances, and states whether the statement ties: its beginning balance plus its transactions must equal its ending balance. A statement with no balances cannot be checked. The person can enter the bank's balances instead, or the bank's balance at the end of one day.
Below that, each bank transaction that needs a decision is listed with one sentence saying why it is not matched, for example "No posted entry for $412.00 within 5 days." What a person can do with a line:
- Match an exact entry, or mark Not this entry. The exact entries are posted entries that equal the line to the cent.
- Match several entries, for one deposit of several checks: tick two or more entries whose amounts add up to the line exactly.
- Show all uncleared entries on the account.
- Record bank fee or Record interest. The page notes when the description reads like one, and asks before recording a second entry for an amount an entry already records. These write a decision. The entry is posted when the month is reconciled.
- Exclude the line, and Re-include it later.
- Undo a match, whether a person's or the machine's. A line holding a match accepts no other action until the match is undone.
Entries within fifty dollars of a line appear as context. A match must be exact, so none of them can be matched as it stands. A difference is recorded as its own entry first.
How a Line Is Matched
Matching has two parts, hints and the exact gate. Neither posts an entry.
Hints
When the review page renders, the matching engine offers each line the entries a person could match it to. It stores nothing and never labels a pair exact. It offers, in order:
- Exact entries. Posted entries whose net on the account equals the line to the cent, inside the account's date window, not cleared or claimed by another line, not one the person already rejected on this line, and not a reversal leg whose partner never reached the bank.
- Context. Entries within fifty dollars, and the vendor payment or trust ledger entry the line most likely is.
- A fee or interest pattern. Offered only when no exact entry exists and the sign fits: a fee is a withdrawal, and interest is a deposit.
- A split by the bank. A line that is part of one uncleared entry, where another undecided line makes up the rest.
The date window is the bank account's own setting. The default is five days.
The Exact Gate
The machine accepts a line only when all nine conditions hold. The rule is the same for every source:
- The statement is in review and not tied to a settled reconciliation.
- The line is open.
- No person has decided the line. A person's "no" ends automation for that line.
- The date window has closed. The machine waits until it has.
- The statement is proven. A feed month is proven when its state is established. A file is proven when its transactions tie to both of its balances, each taken from the file or typed by a person. A balance derived from a checkpoint does not prove a statement, because its own check would be circular.
- Exactly one eligible entry fits: the same amount to the cent, inside the window, not already cleared or claimed.
- That entry is dated on or before the statement's end.
- That entry is not a reversal leg. A person may match one. The machine never does.
- No other line wants the same entry.
The gate is pure: the same inputs give the same decisions. Each machine match records the nine results it passed, the candidates it considered, and the date it was evaluated. When a condition fails on a computed fact, a machine match is withdrawn. A withdrawal is never a person's "no". When the gate is withheld — the statement is not proven, or the feed is not connected — nothing is accepted and nothing is withdrawn.
The machine never accepts a bank fee or interest, a group of entries, a reversal leg, or a match with no entry. A database constraint confines a machine match to one existing entry at no difference with no entry created, so that the machine never posts is a database fact, not a convention.
Reconcile
Reconcile
- Every refusal is read first, so a refused Reconcile posts nothing: the period, the statement in review, the balances, an open overlap, and the order rule, which refuses a period on or before the latest settled one.
- Each bank fee or interest a person recorded, and no entry exists for yet, is posted through
JournalEngineand the enforcement dispatcher. A fee debits bank charges and credits cash. Interest debits cash and credits interest income. The entry books to the bank account's own cash account and fund, dated inside the statement period, and is stamped on the match at once, so pressing again reuses it and never posts a second. - The statement balances are resolved: the statement's own, or typed. A missing balance is never read as $0.00.
- The reconciliation for exactly that period is reused or created, and the statement is linked to it.
- Each accepted match is cleared. A machine match is re-proved first, and a computed failure withdraws it and skips it.
- Every applied machine match is re-proved. A failure withdraws it, which un-clears its items.
- The difference is refreshed. The statement stays in review until the reconciliation completes, so an undecided line is never frozen by Reconcile.
Pressing it again is the same call. Applied matches are skipped, items are flipped rather than duplicated, and created entries are reused. That is how a late decision reaches the reconciliation.
A bank fee entry receives the same enforcement decision as a manual journal entry. The bridge does not bypass any accounting control.
BAI2 Parser
The BAI2 parser handles the commercial banking standard:
- Integrity verification: 6 checks — the transaction count in each account trailer; balance integrity per account (opening plus credits less debits equals closing); the group control total against the sum of its account trailers; and the account count and group count in the trailers. The file trailer's control total is read and reported, and it is not compared.
- Type codes: A registry of more than one hundred BAI2 type codes with human-readable descriptions.
- Classification: Each transaction is classified as a deposit, withdrawal, check, wire, ACH, fee, loan (6xx codes), or non-monetary item from its type code.
- Value dates: Funds type V supplies the value date for accurate transaction dating.
- Several accounts: One account is read from a file: the first in the file, unless a target account number is given.
- Duplicate detection: A file that was already imported is caught by its hash. Within an import, each line carries a key hashed from its date, description, amount, and line number.
OFX/QFX Parser
The OFX parser handles both versions of the Open Financial Exchange standard:
- OFX 1.x (SGML): Unclosed tags are closed before the file is parsed.
- OFX 2.x (XML): Standard XML parsing.
- Credit card support: Handles
CCSTMTRS(credit card statement) response elements. - FITID: The Financial Institution Transaction ID is stored as the line's reference.
- CURDEF extraction: Reads the currency designation from the statement header.
- Date range: Extracts
DTSTARTandDTENDfor the statement period bounds.
Connection to Integrity Scanning
Matching reads the ledger. The nightly ledger integrity scan checks it. The scan runs 13 automated checks on the general ledger:
| Check | What It Verifies |
|---|---|
| Balance Check | Every journal entry has equal debits and credits |
| Orphaned Lines | No journal entry lines exist without a parent entry |
| Enforcement Decision | Every journal entry posted after the enforcement cutover has an associated enforcement decision |
| Fund Assignment | Every journal entry has a fund assignment |
| Closed Period | No entries posted to closed or locked fiscal periods without an override |
| Control Account Reconciliation | GL control accounts (AR, AP, Loans) match subledger totals within $0.02 tolerance |
| Payment Receipt | Every completed receipt has a GL posting, and every GL payment posting has a receipt |
| Subscription Invoice | Every paid platform subscription invoice has its expense entry posted |
| Report Recompute | The trial balance, balance sheet, accrual income statement, and general ledger endings recompute exactly from posted lines, and stored account balances match a full recompute |
| Decision Chain | The enforcement decision log is an unbroken hash chain |
| Interfund | Reserve cash has not paid an operating expense, budgeted reserve contributions have been transferred, and no statutory restoration deadline has passed |
| Vendor Payment Record | Every posted bill-payment entry has exactly one vendor payment record, every executed record names its entry, a reversed entry's record is stamped with the reversal, a paid platform payout carries its Stripe payout id, and a payout unconfirmed for more than a day is reported |
Viewing Scan Results
Navigation: Insights tab > Audit > Scans
The scan history page shows:
- KPI cards: Latest status, total scans, last scan date, checks passed count, findings (critical, warning, info)
- Run Scan button: Triggers an on-demand scan
- Scan history table: Date, type, status, checks, findings by severity, duration, content hash
Status display:
- GREEN: Plain text "Passed" (no badge)
- YELLOW: Badge "Warnings"
- RED: Badge "Failed"
Each scan creates an immutable IntegritySnapshot with a SHA-256 content hash. Clicking a scan row shows the detail page with its findings and the change since the previous scan.
Bank fee and interest journal entries created at Reconcile are included in the next integrity scan. They are verified like any other journal entry because they were posted through JournalEngine with full enforcement.
This describes pr
How CommunityPay Enforces This