Skip to content

Bank Reconciliation

Bank reconciliation matches bank statement lines against journal entry lines to verify that the company's accounting records agree with the bank's records. The process supports automatic matching by amount and date, manual matching for complex cases, rule-based matching for recurring patterns, and the ability to create missing journal entries for unrecorded bank transactions.

Purpose

  • Import bank statement lines (manually or from structured data)
  • Automatically match statement lines to journal entry lines by amount and date
  • Manually match remaining unmatched items
  • Define reconciliation rules that auto-match based on description patterns
  • Create journal entries for bank transactions not yet recorded in the GL
  • Complete and approve reconciliations for audit trail
  • Generate reconciliation summary reports

Entities

Bank Reconciliation

FieldTypeDescription
idintegerPrimary key
company_idintegerOwning company
bank_account_idintegerBank account being reconciled
statement_datedateDate of the bank statement
period_startdateStart of the reconciliation period
period_enddateEnd of the reconciliation period
opening_balancedecimal(12,3)Opening balance per bank statement
closing_balancedecimal(12,3)Closing balance per bank statement
reconciled_balancedecimal(12,3)?Reconciled balance after matching
differencedecimal(12,3)?Difference between book and bank
statusenumin_progress, completed, approved
completed_atdatetime?When marked complete
approved_byinteger?User who approved
approved_atdatetime?Approval timestamp
notestext?Notes
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Bank Statement (imported lines)

FieldTypeDescription
idintegerPrimary key
company_idintegerOwning company
bank_reconciliation_idintegerParent reconciliation
referencestring?Bank reference number
datedateTransaction date
descriptionstring?Transaction description
debitdecimal(12,3)Debit amount (outflow)
creditdecimal(12,3)Credit amount (inflow)
balancedecimal(12,3)?Running balance
match_statusenumunmatched, matched, partially_matched

Reconciliation Match

FieldTypeDescription
idintegerPrimary key
company_idintegerOwning company
bank_reconciliation_idintegerParent reconciliation
bank_statement_idintegerMatched statement line
journal_entry_line_idintegerMatched journal entry line
match_methodenumauto or manual
matched_amountdecimal(12,3)Amount matched
notesstring?Match notes

Reconciliation Rule

FieldTypeDescription
idintegerPrimary key
company_idintegerOwning company
namestringRule name (English)
name_arstring?Rule name (Arabic)
description_patternstringRegex or substring to match statement descriptions
account_idintegerGL account to use when creating entries
is_activebooleanWhether the rule is active

Relationships

API Endpoints

Bank Reconciliations

MethodPathDescription
GET/api/accounting/bank-reconciliationsList reconciliations (paginated)
POST/api/accounting/bank-reconciliationsCreate a new reconciliation
GET/api/accounting/bank-reconciliations/{id}Get with statements and matches
DELETE/api/accounting/bank-reconciliations/{id}Delete (only while in progress)
POST/api/accounting/bank-reconciliations/{id}/importImport bank statement lines
POST/api/accounting/bank-reconciliations/{id}/auto-matchRun automatic matching (optional date_tolerance body param, default 5 days)
POST/api/accounting/bank-reconciliations/{id}/manual-matchManually match a pair
POST/api/accounting/bank-reconciliations/{id}/unmatchUnmatch a statement line (bank_statement_id) — returns it to unmatched
POST/api/accounting/bank-reconciliations/{id}/create-entryCreate journal entry for unmatched line
POST/api/accounting/bank-reconciliations/{id}/completeMark as completed
POST/api/accounting/bank-reconciliations/{id}/approveApprove a completed reconciliation
GET/api/accounting/bank-reconciliations/{id}/reportGet reconciliation summary report

Auto-match behaviour

Auto-match pairs a statement line with a journal entry line on exact amount within a date tolerancedate_tolerance days, default 5). When the statement line carries a reference, candidates are first narrowed to journal entries with a matching reference or description. A line is auto-matched only when exactly one candidate remains — ambiguous ties are returned in ambiguous_count and left for manual matching, never resolved arbitrarily.

Statement integrity

On import, the statement must foot: opening_balance + Σ(debit − credit) has to equal closing_balance. An unbalanced upload is rejected with 422 and no rows are written.

Reconciliation Rules

MethodPathDescription
GET/api/accounting/reconciliation-rulesList rules (paginated)
POST/api/accounting/reconciliation-rulesCreate a rule
GET/api/accounting/reconciliation-rules/{id}Get a rule
PUT/api/accounting/reconciliation-rules/{id}Update a rule
DELETE/api/accounting/reconciliation-rules/{id}Delete a rule

State Diagram

Workflow

Request/Response Examples

POST /api/accounting/bank-reconciliations

Create a new reconciliation.

Request

json
{
  "bank_account_id": 2,
  "statement_date": "2026-01-31",
  "period_start": "2026-01-01",
  "period_end": "2026-01-31",
  "opening_balance": "45000.000",
  "closing_balance": "52300.000",
  "notes": "January 2026 reconciliation"
}

Response 201 Created

json
{
  "data": {
    "id": 4,
    "company_id": 1,
    "bank_account_id": 2,
    "bank_account": {
      "id": 2,
      "name": "NBK Main Account"
    },
    "statement_date": "2026-01-31",
    "period_start": "2026-01-01",
    "period_end": "2026-01-31",
    "opening_balance": "45000.000",
    "closing_balance": "52300.000",
    "reconciled_balance": null,
    "difference": null,
    "status": "in_progress",
    "status_label": "In Progress",
    "notes": "January 2026 reconciliation",
    "created_at": "2026-02-05T09:00:00.000000Z"
  }
}

POST /api/accounting/bank-reconciliations/{id}/import

Import bank statement lines.

Request

json
{
  "lines": [
    {
      "reference": "TRN-001",
      "date": "2026-01-05",
      "description": "Customer payment - Al Safat Trading",
      "debit": "0.000",
      "credit": "5000.000",
      "balance": "50000.000"
    },
    {
      "reference": "TRN-002",
      "date": "2026-01-10",
      "description": "Rent payment - January",
      "debit": "1500.000",
      "credit": "0.000",
      "balance": "48500.000"
    },
    {
      "reference": "TRN-003",
      "date": "2026-01-15",
      "description": "Bank fees",
      "debit": "25.000",
      "credit": "0.000",
      "balance": "48475.000"
    }
  ]
}

Response 200 OK

json
{
  "message": "Bank statement imported successfully"
}

POST /api/accounting/bank-reconciliations/{id}/auto-match

Response 200 OK

json
{
  "message": "Auto-match completed: 2 transactions matched",
  "matched_count": 2
}

POST /api/accounting/bank-reconciliations/{id}/manual-match

Request

json
{
  "bank_statement_id": 7,
  "journal_entry_line_id": 45,
  "matched_amount": "25.000"
}

Response 201 Created

json
{
  "data": {
    "id": 5,
    "bank_reconciliation_id": 4,
    "bank_statement_id": 7,
    "journal_entry_line_id": 45,
    "match_method": "manual",
    "matched_amount": "25.000",
    "notes": null
  }
}

POST /api/accounting/bank-reconciliations/{id}/create-entry

Create a journal entry for an unmatched bank fee.

Request

json
{
  "bank_statement_id": 7,
  "account_id": 52
}

Response 201 Created

json
{
  "data": {
    "id": 115,
    "entry_number": "JE-2026-0115",
    "date": "2026-01-15",
    "description": "Bank fees",
    "status": "draft",
    "lines": [
      { "account_id": 52, "debit": "25.000", "credit": "0.000" },
      { "account_id": 15, "debit": "0.000", "credit": "25.000" }
    ]
  }
}

GET /api/accounting/bank-reconciliations/{id}/report

Response 200 OK

json
{
  "data": {
    "reconciliation_id": 4,
    "bank_account": "NBK Main Account",
    "period": "2026-01-01 to 2026-01-31",
    "opening_balance": "45000.000",
    "closing_balance": "52300.000",
    "total_matched": 3,
    "total_unmatched": 0,
    "reconciled_balance": "52300.000",
    "difference": "0.000",
    "status": "completed"
  }
}

Business Rules

RuleDescription
One active reconciliationOnly one in-progress reconciliation per bank account at a time
Complete requires all matchedCannot complete if any statement lines remain unmatched
Approve requires completedOnly completed reconciliations can be approved
Delete only in progressCannot delete a completed or approved reconciliation
Auto-match by amount+dateAutomatic matching compares amounts and dates within a tolerance window
Manual match overridesManual matching overrides any previous auto-match for the same line
Rule-based matchingReconciliation rules match statement descriptions to predefined GL accounts
Create entry fills gapWhen no journal entry exists for a bank transaction, one can be created inline
Company scopedAll reconciliation data is filtered by the authenticated user's company

Moon ERP API Documentation