Bank Reconciliation
Match external bank transactions to ledger-backed accounting events.
Bank reconciliation in SpeyBooks is a controlled alignment process between:
- External bank statements — what the bank says happened
- Internal ledger events — what your books say happened
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 reconciliation operates on a separate data stream:
Bank statements (external, mutable source)
↓
Imported bank transactions (read-only)
↓
Matching / categorisation
↓
Ledger transactions (double-entry, immutable)
Key properties
- Imported bank transactions are not ledger entries
- Ledger transactions remain append-only
- Reconciliation is explicit and auditable
Supported import methods
SpeyBooks currently supports:
- Manual entry
- CSV import from your bank
- Open Banking (coming soon)
All methods normalise into the same internal bank transaction format.
Import bank transactions
CSV format
Prepare a CSV file with the following columns:
date,description,amount,reference
2026-01-15,BACS Payment - Acme Corp,1200.00,INV-0001
2026-01-16,Direct Debit - AWS,-45.00,
2026-01-17,Card Payment - Office Supplies,-89.50,
Rules
| Field | Format |
|---|---|
amount | Major units (GBP) in CSV, converted to minor units on import |
| Positive | Money into the bank account |
| Negative | Money out of the bank account |
Upload via API
curl -X POST https://api.speybooks.com/v1/bank/import \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: multipart/form-data" \
-F "file=@bank-statement.csv" \
-F "accountId=acc_1200"
Response
{
"success": true,
"data": {
"imported": 3,
"duplicatesSkipped": 0,
"transactions": [
{
"id": "btx_001",
"date": "2026-01-15",
"description": "BACS Payment - Acme Corp",
"amount": 120000,
"status": "unmatched"
}
]
}
}
Import guarantees
- Duplicate detection is deterministic (date + amount + reference)
- Imported bank transactions are immutable
- Re-importing the same file is safe and idempotent
Bank transaction lifecycle
Each imported bank transaction progresses through a strict state machine:
| Status | Meaning |
|---|---|
unmatched | No accounting record linked |
matched | Linked to an invoice, payment, or expense |
reconciled | Confirmed and locked |
Once a transaction reaches reconciled, it cannot be modified or re-matched.
Automatic matching
SpeyBooks attempts non-destructive matching using:
- Explicit references (e.g.,
INV-0001) - Exact amount matches
- Contact names found in descriptions
- Date proximity
Automatic matching never creates ledger entries. It only proposes links to existing records.
Manual matching
If a bank transaction is unmatched, you may explicitly link it to an existing accounting record.
curl -X POST https://api.speybooks.com/v1/bank/transactions/btx_001/match \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "inv_42"
}'
Response
{
"success": true,
"data": {
"id": "btx_001",
"status": "matched",
"matchedTo": {
"type": "invoice",
"id": "inv_42"
}
}
}
Effects
- The bank transaction is linked to the invoice payment
- No ledger data is altered
- The transaction moves to
matched
Create a new accounting transaction
If a bank transaction does not correspond to an existing record, you may categorise it.
curl -X POST https://api.speybooks.com/v1/bank/transactions/btx_002/categorise \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"accountId": "acc_5200",
"vatRate": "standard",
"description": "AWS monthly hosting"
}'
Response
{
"success": true,
"data": {
"id": "btx_002",
"status": "matched",
"transaction": {
"id": "txn_891",
"description": "AWS monthly hosting",
"accountId": "acc_5200",
"amount": 4500,
"vatRate": "standard"
}
}
}
Effects
- Creates a new double-entry ledger transaction
- Links it to the bank transaction
- Preserves full audit traceability
- Transitions the bank transaction to
matched
Reconciliation and locking
Once you have reviewed matched transactions, you may reconcile them.
Reconciliation means:
- The bank transaction is confirmed as correct
- The linkage to ledger data is locked
- The transaction becomes read-only
This step 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 |
Key invariants
- Bank imports never mutate the ledger
- Ledger entries are never inferred from bank data
- All reconciliation actions are explicit
- Every reconciled balance is reproducible
Error codes
| Code | HTTP | Description |
|---|---|---|
validation_error | 400 | Invalid CSV format or missing required columns |
invalid_id | 400 | Malformed account or transaction ID |
not_found | 404 | Bank transaction or invoice does not exist |
conflict | 409 | Transaction already matched or reconciled |
unprocessable_entity | 422 | Cannot modify reconciled transaction |
Amount format
Bank transaction amounts are stored in minor units (pence for GBP):
120000 = £1,200.00
4500 = £45.00
CSV imports use major units; SpeyBooks converts on import.