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:
| Status | Meaning |
|---|---|
unmatched | No accounting record linked |
categorised | Linked to an account with a ledger entry created |
reconciled | Confirmed 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 rulesPOST /v1/categorisation-rules/- create a new rulePOST /v1/categorisation-rules/test- test rules against a sample transactionPOST /v1/categorisation-rules/reorder- change evaluation priorityGET /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
| Practice | Rationale |
|---|---|
| Reconcile frequently | Weekly reconciliation reduces ambiguity and errors |
| Use strong references | Include invoice IDs in payment references wherever possible |
| Investigate unmatched | Persistent unmatched entries indicate missing or misclassified events |
| Lock completed periods | Reconcile and lock each month to preserve audit integrity |
| Review low-confidence suggestions | The 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
| Code | HTTP | Description |
|---|---|---|
validation_error | 400 | Invalid CSV format or missing required columns |
balance_mismatch | 422 | Balance proof failed - rows don't sum to closing balance |
not_found | 404 | Bank import or transaction does not exist |
conflict | 409 | Transaction already categorised or reconciled |
unprocessable_entity | 422 | Cannot 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
- Bank Imports API - upload, preview, remap, confirm
- Transactions API - categorise and reconcile
- Categorisation Rules API - manage auto-categorisation
- CSV Import - import pipeline overview