Bank Reconciliation

Match external bank transactions to ledger-backed accounting events.

Bank reconciliation in SpeyBooks is a controlled alignment process. The bank statement tells you what the bank says happened. The ledger tells you what your books say happened. Reconciliation links the two explicitly.

SpeyBooks never mutates the ledger to "make things match". Reconciliation only links evidence to existing accounting records or creates new, explicit ones.

Conceptual model

Bank CSV (external evidence)
        |
        v
Universal Ingestion Engine (parse, detect, deduplicate)
        |
        v
Imported bank transactions (preview, remap, confirm)
        |
        v
Categorisation (manual, rule-based, or AI-suggested)
        |
        v
Ledger transactions (double-entry, immutable)
        |
        v
Reconciliation (confirm and lock)

Key properties

  • Imported bank transactions are external evidence, not ledger entries
  • Ledger transactions remain append-only
  • Every reconciliation action is explicit and auditable

Import bank transactions

Bank statements are imported via the Universal Ingestion Engine (UIE). Upload any bank's CSV and the engine detects the column structure automatically - you don't need to select a bank or specify a format.

curl -X POST https://api.speybooks.com/v1/bank-imports/upload \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: multipart/form-data" \
  -F "file=@natwest-jan-2026.csv" \
  -F "bankAccountId=acc_1200"

The UIE performs column detection (date, description, amount, balance, reference), balance proof verification (opening + movements = closing), duplicate detection against previously completed imports, and row conservation (every row is accounted for).

UK bank presets are available for NatWest, Starling, Monzo, HSBC, Lloyds, Tide, and others. These are verification hints, not requirements.

Smart Map (column remap)

If the engine's automatic column detection needs correction, remap before confirming:

curl -X POST https://api.speybooks.com/v1/bank-imports/bi_1/remap \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mappings": {
      "date": "Transaction Date",
      "description": "Details",
      "amount": "Amount"
    }
  }'

Confirm the import

Once you're satisfied with the preview:

curl -X POST https://api.speybooks.com/v1/bank-imports/bi_1/confirm \
  -H "Authorization: Bearer sk_live_..."

Import guarantees

  • Duplicate detection is deterministic (date + amount + description hash)
  • Imports enter a preview state before any data is committed
  • Re-importing the same statement is safe - duplicates are flagged, not created
  • Balance proof must pass before confirmation is allowed

Bank transaction lifecycle

Each imported bank transaction progresses through a strict state machine:

StatusMeaning
unmatchedNo accounting record linked
categorisedLinked to an account with a ledger entry created
reconciledConfirmed and locked

Once a transaction reaches reconciled, it cannot be modified or re-categorised.


Auto-categorisation

SpeyBooks uses a lattice-scored categorisation engine to suggest categories for imported transactions. The engine combines user-defined rules, global rules, and pattern matching.

curl -X POST https://api.speybooks.com/v1/transactions/txn_2/suggest-category \
  -H "Authorization: Bearer sk_live_..."

The response includes a confidence score and the rule that matched, so you can see exactly why a category was suggested. High-confidence suggestions can be accepted in bulk; low-confidence ones require manual review.

Categorisation rules

Rules are evaluated in priority order. You can create, reorder, and test them via the API:

  • GET /v1/categorisation-rules/ - list your rules
  • POST /v1/categorisation-rules/ - create a new rule
  • POST /v1/categorisation-rules/test - test rules against a sample transaction
  • POST /v1/categorisation-rules/reorder - change evaluation priority
  • GET /v1/categorisation-rules/global - view the built-in global rules

Categorise a transaction

When a bank transaction doesn't match an existing invoice or bill, categorise it to create a new ledger entry:

curl -X PATCH https://api.speybooks.com/v1/transactions/txn_1/categorise \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "acc_7400",
    "vatRate": "standard",
    "description": "AWS monthly hosting"
  }'

Effects

  • Creates a double-entry ledger transaction (debit the expense account, credit the bank)
  • Applies VAT split automatically based on the rate
  • Links the ledger entry to the bank transaction
  • Transitions the transaction to categorised

Reconcile

Once you've reviewed categorised transactions, reconcile them to lock the linkage:

curl -X PATCH https://api.speybooks.com/v1/transactions/txn_1/reconcile \
  -H "Authorization: Bearer sk_live_..."

Reconciliation means the bank transaction is confirmed as correct, the linkage to ledger data is locked, and the transaction becomes read-only. This prevents accidental drift between bank statements and the ledger.


Best practices

PracticeRationale
Reconcile frequentlyWeekly reconciliation reduces ambiguity and errors
Use strong referencesInclude invoice IDs in payment references wherever possible
Investigate unmatchedPersistent unmatched entries indicate missing or misclassified events
Lock completed periodsReconcile and lock each month to preserve audit integrity
Review low-confidence suggestionsThe categorisation engine explains its reasoning - use that to refine your rules

Key invariants

  • Bank imports never mutate the ledger
  • Ledger entries are never inferred from bank data without explicit categorisation
  • All reconciliation actions are explicit and auditable
  • Every reconciled balance is reproducible
  • The balance proof must verify before an import can be confirmed

Error codes

CodeHTTPDescription
validation_error400Invalid CSV format or missing required columns
balance_mismatch422Balance proof failed - rows don't sum to closing balance
not_found404Bank import or transaction does not exist
conflict409Transaction already categorised or reconciled
unprocessable_entity422Cannot modify reconciled transaction

Amount format

Bank transaction amounts are stored in minor units (pence for GBP):

120000 = GBP 1,200.00
4500   = GBP 45.00

CSV imports use major units; SpeyBooks converts on import.

Related endpoints