Contributing: Adding a New Bank¶
This guide walks you through configuring Bank Statement Parser to parse statements from a new bank. No Python code required — it's all TOML configuration.
Is This For You?¶
This guide covers three distinct scenarios:
- Scenario A: Adding support for an entirely new bank (never seen before)
- Scenario B: Adding a new account type to an existing bank (HSBC current accounts exist, now add HSBC savings)
- Scenario C: Improving or fixing an existing bank's configuration
Choose your scenario below, or continue reading for the complete walkthrough.
Scenario A: Adding an Entirely New Bank¶
Effort: 2–4 hours (medium–high difficulty)
Requirements: - Understanding of your bank's PDF layout (columns, table positions) - 3+ anonymised test PDFs (different statement dates) - TOML configuration (4 new files, ~100 lines each)
What you'll create:
- project/config/import/NEWBANK_UK/companies.toml — Bank identification
- project/config/import/NEWBANK_UK/accounts.toml — Account definitions
- project/config/import/NEWBANK_UK/statement_types.toml — Statement layout
- project/config/import/NEWBANK_UK/statement_tables.toml — Transaction table extraction
Next steps: 1. Read the Complete Reference section (auto-generated technical guide) 2. Test locally with your PDFs (see Local Testing Guide) 3. Prepare 3+ anonymised test PDFs + metadata JSON (see Test Data Submission Guide) 4. Submit PR with config files and note about test data
Scenario B: Adding a New Account Type to Existing Bank¶
Effort: 1–2 hours (medium difficulty)
Requirements: - Bank already supported (e.g., HSBC current accounts work) - New account type (e.g., HSBC savings accounts) - 3+ anonymised test PDFs for the new account type - Modify existing TOML files (don't create new bank folder)
What you'll modify:
- project/config/import/HSBC_UK/accounts.toml — Add new account definition
- project/config/import/HSBC_UK/statement_tables.toml — Add new table extraction rules (if layout differs)
- project/config/import/HSBC_UK/statement_types.toml — Add new statement type (if needed)
Next steps: 1. Read the Complete Reference section 2. Compare your new account type's PDF to the existing one (are columns in same place?) 3. Modify only the files that differ 4. Test locally (see Local Testing Guide) 5. Prepare test data and submit PR
Scenario C: Improving or Fixing Existing Config¶
Effort: 30 mins–1 hour (low–medium difficulty)
Requirements: - Bank already configured - Fix or enhancement (better extraction, missing fields, etc.) - If config changes: 3+ new anonymised PDFs + metadata JSON - If small fix (typo, comment): test locally to verify
What you'll do: 1. Identify the problem (via issue or your own discovery) 2. Fix the TOML in one or more files 3. Test locally to verify fix works (see Local Testing Guide) 4. If config changed meaningfully: prepare test data 5. Submit PR with explanation
Worked Example: Adding HSBC Credit Card Config¶
Let's walk through a real (anonymised) example of what TOML files look like.
File 1: companies.toml (Bank Identification)¶
This tells the parser how to identify that a PDF is from HSBC.
[HSBC_UK]
company = 'HSBC Bank UK'
[HSBC_UK.config]
config = 'Company Info'
locations = [
{page_number = 1, top_left = [475, 110], bottom_right = [575, 130]},
]
fields = [
{
field = 'website',
vital = true,
type = "string",
string_pattern = '^www\.hsbc\.co\.uk$'
},
]
What this does: - Looks for "www.hsbc.co.uk" on page 1 between pixels (475,110) and (575,130) - If found, this is an HSBC PDF
File 2: accounts.toml (Account Definitions)¶
This tells the parser what account types HSBC offers, and how to identify them.
[HSBC_UK_CRD]
account_type = "CRD" # Credit Card
company = 'HSBC_UK'
[HSBC_UK_CRD.config]
config = 'Account Identification'
account = 'Rewards Credit Card'
locations = [
{page_number = 1, top_left = [50, 150], bottom_right = [300, 180]},
]
fields = [
{field = 'account', vital = true, type = "string", string_pattern = 'Rewards.*Card'},
]
[HSBC_UK_SAV]
account_type = "SAV" # Savings Account
company = 'HSBC_UK'
[HSBC_UK_SAV.config]
config = 'Account Identification'
account = 'Easy Saver'
locations = [
{page_number = 1, top_left = [50, 150], bottom_right = [300, 180]},
]
fields = [
{field = 'account', vital = true, type = "string", string_pattern = 'Easy.*Saver'},
]
What this does: - First account type: If PDF says "Rewards ... Card", it's a Credit Card - Second account type: If PDF says "Easy ... Saver", it's a Savings Account
File 3: statement_types.toml (Statement Layout)¶
This tells the parser where to find statement-level data (opening balance, dates, etc.) and per-page transaction lines.
[HSBC_UK_CRD_STATEMENT]
account_ref = 'HSBC_UK_CRD'
statement_type = 'Standard'
[HSBC_UK_CRD_STATEMENT.header]
config = 'Header'
table_type = "statement_header"
locations = [
{page_number = 1, top_left = [50, 100], bottom_right = [500, 300]},
]
fields = [
{field = 'statement_date', column = 2, vital = true, type = "string", string_pattern = '^[0-3][0-9]\s[A-Z][a-z]{2}\s[0-9]{4}$'},
{field = 'opening_balance', column = 3, vital = true, type = "currency"},
{field = 'payments_in', column = 4, vital = true, type = "currency"},
]
[HSBC_UK_CRD_STATEMENT.lines]
config = 'Transaction Lines'
statement_table = 'HSBC_UK_CRD_TRANSACTIONS'
page_detection = {column = 0, value = "From beginning of statement"}
What this does: - Header section: Extracts statement date and opening balance from page 1 - Lines section: Extracts transaction rows (defined separately in statement_tables.toml)
File 4: statement_tables.toml (Transaction Table Extraction)¶
This tells the parser where the transaction table is and how to extract each column.
[HSBC_UK_CRD_TRANSACTIONS]
type = "transaction"
statement_table = 'Transactions'
table_columns = 6
[HSBC_UK_CRD_TRANSACTIONS.locations]
vertical_lines = [50, 100, 100, 150, 150, 250, 250, 350, 350, 450, 450, 555]
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = 'date'
column = 0
vital = false
type = "string"
string_pattern = '^[0-3][0-9]\s[A-Z][a-z]{2}$'
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = 'details'
column = 2
vital = true
type = "string"
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = '£_paid_in'
column = 4
vital = false
type = "currency"
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = '£_paid_out'
column = 5
vital = false
type = "currency"
What this does: - Defines a 6-column transaction table - Vertical dividers at pixels: 50–100 (col 1), 100–150 (col 2), etc. - Column 0: Transaction date (regex pattern matches "11 Jan" format) - Column 2: Transaction details (merchant name, etc.) - Column 4: Credits (paid in) - Column 5: Debits (paid out)
Finding Column Coordinates¶
The trickiest part of TOML config is measuring column coordinates. Here's how:
1. Get a PDF measurement tool: - macOS: Preview (built-in) has a Measure tool - Windows: Adobe Reader (free) or PDF tool - Online: Try pdfmeasure.com (browser-based)
2. Open your bank's PDF
3. Find the transaction table and measure: - Where does the first column start? (left edge of table) - Where does each column divider appear? (vertical lines between columns) - Where does the last column end? (right edge of table)
Example:
Becomes in TOML:
💡 Tip: Values are in points (PDF units), roughly equivalent to 1/72 inch. An 8.5"×11" page is about 612×792 points.
Complete Reference¶
Below is the complete technical reference (auto-generated from code). It covers all TOML fields, dataclasses, and configuration options.
Adding a New Bank — Technical Reference¶
This section contains the complete TOML configuration reference for adding bank support. Most users should read the Worked Example section first.
Overview¶
Adding support for a new bank involves creating and editing several TOML files that describe how to identify the bank's PDFs, locate tables on each page, extract field values, and map them to standard output columns.
The configuration lives in two places:
| Location | Purpose |
|---|---|
project/config/import/<BANK_COUNTRY>/ |
Bank-specific config folder (4 TOML files) |
project/config/import/account_types.toml |
Shared account type registry |
project/config/import/standard_fields.toml |
Shared standard field mappings |
Bank config folder structure¶
Each bank has its own subfolder named in SCREAMING_SNAKE_CASE (e.g. HSBC_UK, TSB_UK). A complete folder contains exactly four files:
| File | Purpose | Key Dataclass |
|---|---|---|
companies.toml |
Bank identification (name + PDF detection rule) | Company |
accounts.toml |
Account definitions (one per product/card type) | Account |
statement_types.toml |
Statement layout definitions (header + lines extraction) | StatementType |
statement_tables.toml |
Physical table extraction rules (locations, fields, bookends) | StatementTable |
Processing pipeline¶
Understanding the processing order helps when writing config:
- Company identification — the
Company.configextraction is run against page 1 to determine which bank issued the PDF. - Account identification — each
Account.configis tried until one matches, identifying the specific account product. - Header extraction — the
StatementType.headerconfigs run to extract statement-level metadata (dates, balances, account details). - Lines extraction — the
StatementType.linesconfigs run per-page to extract transaction rows. - Standard field mapping — raw extracted fields are mapped to
STD_*output columns viastandard_fields.toml. - Checks & balances — opening balance + payments in - payments out = closing balance is validated.
Step 1: Register the Account Type¶
If your bank uses an account type not already in account_types.toml, add a new entry. Most banks will use the existing types (CRD, CUR, SAV, ISA).
File: project/config/import/account_types.toml
[CRD]
account_type = "Credit Card"
[CUR]
account_type = "Current Account"
[SAV]
account_type = "Savings Account"
[ISA]
account_type = "ISA"
AccountType¶
Simple lookup label for an account type category.
| Field | Type | Status | Description |
|---|---|---|---|
account_type |
str |
ACTIVE | Account type label (e.g. "CRD" for credit card, "CUR" for current account). Populated at load time but not subsequently consumed by the pipeline; present for potential reporting or routing use. |
Step 2: Create the Bank Config Folder¶
Create a new subfolder under project/config/import/ using the naming convention <BANK>_<COUNTRY> in SCREAMING_SNAKE_CASE:
project/config/import/
HSBC_UK/ # existing
TSB_UK/ # existing
NEWBANK_UK/ # <- your new folder
companies.toml
accounts.toml
statement_types.toml
statement_tables.toml
Step 3: companies.toml — Bank Identification¶
Tells the parser how to identify a PDF as belonging to this bank.
File: project/config/import/<BANK_COUNTRY>/companies.toml
[HSBC_UK]
company = 'HSBC Bank UK'
[HSBC_UK.config]
config = 'Company Info'
locations = [
{page_number = 1, top_left = [475, 110], bottom_right = [575, 130]},
]
fields = [
{field = 'website', vital = true, type = "string", string_pattern = '^www\.hsbc\.co\.uk$'},
]
Company¶
| Field | Type | Status | Description |
|---|---|---|---|
company |
str |
ACTIVE | Display name of the bank (e.g., 'HSBC Bank UK') |
config |
Config |
ACTIVE | Extraction rule to identify the bank from page 1 |
Config (for Company Identification)¶
| Field | Type | Status | Description |
|---|---|---|---|
config |
str |
ACTIVE | Always set to "Company Info" for identification rules |
locations |
list[PDFLocation] |
ACTIVE | List of page regions to search for the bank name |
fields |
list[ConfigField] |
ACTIVE | Extraction rules (usually one: the company website or name) |
PDFLocation¶
Specifies a rectangular region on a PDF page (in points).
| Field | Type | Status | Description |
|---|---|---|---|
page_number |
int |
ACTIVE | Page number (1-indexed; 1 = first page) |
top_left |
list[int] |
ACTIVE | [x, y] coordinates of top-left corner in PDF points |
bottom_right |
list[int] |
ACTIVE | [x, y] coordinates of bottom-right corner in PDF points |
ConfigField¶
Defines a field to extract from a region.
| Field | Type | Status | Description |
|---|---|---|---|
field |
str |
ACTIVE | Field name (e.g., "website", "account_number") |
vital |
bool |
ACTIVE | If true, extraction must succeed or the entire identification fails |
type |
str |
ACTIVE | Field type: "string", "currency", "number", "date" |
string_pattern |
str |
CONDITIONAL | Regex pattern (required if type = "string"). Example: ^www\.hsbc\.co\.uk$ |
string_max_length |
int |
OPTIONAL | Max length of extracted string |
Step 4: accounts.toml — Account Definitions¶
Defines account types supported by this bank and how to identify them.
File: project/config/import/<BANK_COUNTRY>/accounts.toml
[HSBC_UK_CRD]
account_type = "CRD"
company = 'HSBC_UK'
[HSBC_UK_CRD.config]
config = 'Account Identification'
account = 'Rewards Credit Card'
locations = [
{page_number = 1, top_left = [50, 150], bottom_right = [300, 180]},
]
fields = [
{field = 'account', vital = true, type = "string", string_pattern = 'Rewards.*Card'},
]
Account¶
| Field | Type | Status | Description |
|---|---|---|---|
account_type |
str |
ACTIVE | Reference to account type (e.g., "CRD", "CUR", "SAV", "ISA") |
company |
str |
ACTIVE | Reference to company key (e.g., "HSBC_UK") |
config |
Config |
ACTIVE | Extraction rule to identify this specific account type |
Step 5: statement_types.toml — Statement Layout¶
Defines how to extract statement-level metadata (header) and per-page transaction rows (lines).
File: project/config/import/<BANK_COUNTRY>/statement_types.toml
[HSBC_UK_CRD_STATEMENT]
account_ref = 'HSBC_UK_CRD'
statement_type = 'Standard'
[HSBC_UK_CRD_STATEMENT.header]
config = 'Header'
table_type = "statement_header"
locations = [
{page_number = 1, top_left = [50, 100], bottom_right = [500, 300]},
]
fields = [
{field = 'statement_date', column = 2, vital = true, type = "string", string_pattern = '^[0-3][0-9]\s[A-Z][a-z]{2}\s[0-9]{4}$'},
{field = 'opening_balance', column = 3, vital = true, type = "currency"},
]
[HSBC_UK_CRD_STATEMENT.lines]
config = 'Transaction Lines'
statement_table = 'HSBC_UK_CRD_TRANSACTIONS'
page_detection = {column = 0, value = "From beginning of statement"}
StatementType¶
| Field | Type | Status | Description |
|---|---|---|---|
account_ref |
str |
ACTIVE | Reference to account key (e.g., "HSBC_UK_CRD") |
statement_type |
str |
ACTIVE | Type label (usually "Standard") |
header |
TableExtraction |
ACTIVE | Rules for extracting header/metadata |
lines |
TableExtraction |
ACTIVE | Rules for extracting transaction rows |
TableExtraction¶
| Field | Type | Status | Description |
|---|---|---|---|
config |
str |
ACTIVE | Description ("Header" or "Transaction Lines") |
statement_table |
str |
CONDITIONAL | Reference to StatementTable (required for lines) |
table_type |
str |
OPTIONAL | Table classification ("statement_header", etc.) |
locations |
list[PDFLocation] |
ACTIVE | Page regions to search |
fields |
list[ConfigField] |
ACTIVE | Fields to extract |
page_detection |
dict |
OPTIONAL | How to identify pages containing this table |
Step 6: statement_tables.toml — Transaction Table Extraction¶
Defines the physical layout of the transaction table (columns, vertical dividers, field mappings).
File: project/config/import/<BANK_COUNTRY>/statement_tables.toml
[HSBC_UK_CRD_TRANSACTIONS]
type = "transaction"
statement_table = 'Transactions'
table_columns = 6
[HSBC_UK_CRD_TRANSACTIONS.locations]
vertical_lines = [50, 100, 100, 150, 150, 250, 250, 350, 350, 450, 450, 555]
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = 'date'
column = 0
vital = false
type = "string"
string_pattern = '^[0-3][0-9]\s[A-Z][a-z]{2}$'
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = 'details'
column = 2
vital = true
type = "string"
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = '£_paid_in'
column = 4
vital = false
type = "currency"
[[HSBC_UK_CRD_TRANSACTIONS.fields]]
field = '£_paid_out'
column = 5
vital = false
type = "currency"
StatementTable¶
| Field | Type | Status | Description |
|---|---|---|---|
type |
str |
ACTIVE | Table type ("transaction" or "header") |
statement_table |
str |
ACTIVE | Display name of the table (e.g., "Transactions", "Header") |
table_columns |
int |
ACTIVE | Number of columns in the table |
locations |
object |
ACTIVE | Bounding box and divider lines |
fields |
list[TableField] |
ACTIVE | Field extraction rules per column |
TableField¶
| Field | Type | Status | Description |
|---|---|---|---|
field |
str |
ACTIVE | Field name |
column |
int |
ACTIVE | Column index (0-based) |
vital |
bool |
ACTIVE | If true, field must extract successfully |
type |
str |
ACTIVE | Field type ("string", "currency", "number", "date") |
string_pattern |
str |
CONDITIONAL | Regex pattern for string fields |
Step 7: Register Standard Field Mappings¶
The last step is to map your extracted fields to the standard output columns (STD_*).
File: project/config/import/standard_fields.toml
This file defines how raw extracted fields map to standardized output columns:
[HSBC_UK_CRD]
# [ACTIVE] Raw extracted field → Standard output column mappings
STD_STATEMENT_DATE = "statement_date"
STD_OPENING_BALANCE = "opening_balance"
STD_CLOSING_BALANCE = "closing_balance"
STD_PAYMENTS_IN = "payments_in"
STD_PAYMENTS_OUT = "payments_out"
STD_TRANSACTION_DATE = "date"
STD_TRANSACTION_DESC = "details"
STD_TRANSACTION_PAYMENTS_IN = "£_paid_in"
STD_TRANSACTION_PAYMENTS_OUT = "£_paid_out"
STD_ACCOUNT = "account"
STD_CURRENCY = "GBP" # or extract from PDF
STD_COMPANY = "HSBC_UK"
STD_STATEMENT_TYPE = "Standard"
Before You Submit¶
✅ Checklist: - [ ] All 4 TOML files created/modified in bank folder - [ ] Bank name and account types verified - [ ] Company identification rule tested - [ ] Account identification rule tested - [ ] Header fields extract correctly (dates, balances) - [ ] Transaction table columns measured and dividers set correctly - [ ] All transaction fields extract correctly - [ ] Checks-and-balances pass - [ ] Standard field mappings registered - [ ] 3+ anonymised PDFs tested locally - [ ] Metadata JSON created for each PDF - [ ] PR description explains bank name and account types
Next Steps¶
- Test your config locally (see Local Testing Guide)
- Prepare anonymised PDFs (3+ different statements)
- Create metadata JSON files (one per PDF) (see Test Data Submission Guide)
- Submit PR with config files
- Maintainers will request test PDFs and create private PR to activate tests
Questions?¶
See the CONTRIBUTING.md guide on GitHub for help resources.