Production-oriented local RPA platform for automating bank statement retrieval, validation, reconciliation and import preparation for Latvian accounting systems.
LV Bank RPA is a Python-based local automation platform designed to automate repetitive accounting operations:
Bank API / Gateway
│
▼
┌──────────────────┐
│ Bank Adapter │
│ Swedbank / SEB │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ XML / CAMT Parser│
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Domain Model │
│ Transactions │
└────────┬─────────┘
│
├──────────────► Idempotency
│
├──────────────► Validation
│
└──────────────► Reconciliation
│
▼
┌─────────────────┐
│ Accounting │
│ Adapter │
└────────┬────────┘
│
┌─────────┴─────────┐
▼ ▼
Zalktis Jumis
The application is designed for local execution and should not require banking credentials or private certificates to be stored in the source repository.
- Automate bank statement retrieval.
- Support multiple Latvian banks through adapters.
- Normalize banking transactions into a common domain model.
- Validate imported statements.
- Prevent duplicate transaction imports.
- Reconcile statement opening/closing balances.
- Generate accounting-system import files.
- Maintain an audit trail.
- Archive original bank documents.
- Support scheduled execution.
- Provide deterministic and testable processing.
- Keep bank-specific implementation isolated from accounting-specific implementation.
execute payments;
- create or approve bank transfers;
- bypass bank authentication;
- store banking passwords in source code;
- automate CAPTCHA or MFA bypass;
- modify accounting records directly without an approved import workflow;
- replace the accounting system;
- replace bank security mechanisms. The system is intended for read/import automation and controlled accounting integration.
Platform Status Windows 11 Supported Windows Server Planned Ubuntu Linux Supported Debian Linux Supported Docker Supported macOS Development only
The project follows a layered architecture.
┌───────────────────────────────────────────────┐
│ CLI / Scheduler │
├───────────────────────────────────────────────┤
│ Services │
│ Import │ Export │ Reconciliation │ Scheduler │
├───────────────────────────────────────────────┤
│ Domain │
│ Transactions │ Statements │ Validation │
├───────────────────────────────────────────────┤
│ Adapters │
│ Banks Accounting │
│ Swedbank │ SEB Zalktis │ Jumis │
├───────────────────────────────────────────────┤
│ Infrastructure │
│ HTTP │ Filesystem │ Secrets │ Database │
└───────────────────────────────────────────────┘lv-bank-rpa/
│
├── app/
│ ├── __init__.py
│ ├── cli.py
│ ├── config.py
│ ├── logging.py
│ ├── exceptions.py
│ │
│ ├── domain/
│ │ ├── models.py
│ │ ├── enums.py
│ │ ├── validators.py
│ │ └── fingerprints.py
│ │
│ ├── banks/
│ │ ├── base.py
│ │ ├── swedbank/
│ │ │ ├── client.py
│ │ │ ├── auth.py
│ │ │ ├── parser.py
│ │ │ └── schemas/
│ │ │
│ │ └── seb/
│ │ ├── client.py
│ │ ├── auth.py
│ │ ├── parser.py
│ │ └── schemas/
│ │
│ ├── accounting/
│ │ ├── base.py
│ │ │
│ │ ├── zalktis/
│ │ │ ├── exporter.py
│ │ │ ├── validator.py
│ │ │ └── schemas/
│ │ │
│ │ └── jumis/
│ │ ├── exporter.py
│ │ └── validator.py
│ │
│ ├── persistence/
│ │ ├── database.py
│ │ ├── repositories.py
│ │ └── migrations/
│ │
│ ├── services/
│ │ ├── import_service.py
│ │ ├── export_service.py
│ │ ├── reconciliation.py
│ │ └── scheduler.py
│ │
│ └── infrastructure/
│ ├── http.py
│ ├── filesystem.py
│ └── secrets.py
│
├── tests/
│ ├── unit/
│ ├── integration/
│ ├── fixtures/
│ └── contract/
│
├── data/
│ ├── incoming/
│ ├── processing/
│ ├── outgoing/
│ ├── archive/
│ └── failed/
│
├── config/
│ ├── config.example.yaml
│ └── schemas/
│
├── scripts/
│ ├── install-windows.ps1
│ ├── install-linux.sh
│ └── backup.sh
│
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
│
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── security.yml
│
├── .env.example
├── .gitignore
├── pyproject.toml
├── README.md
├── PROJECT.md
├── CHANGELOG.md
└── LICENSEAll banks implement a common interface:
class BankAdapter(ABC):
@abstractmethod
def get_statement(
self,
account_iban: str,
date_from: date,
date_to: date,
) -> Statement:This allows the application to work with different banks without changing the core business logic.
Example:
BankAdapter
│
├── SwedbankGatewayClient
│
└── SEBBalticGatewayClientThe Swedbank integration is intended to use the bank's official Gateway/API interfaces and supported statement formats.
The implementation must be based on the technical specification supplied by the bank.
Expected responsibilities:
Swedbank Client
│
├── Authentication
├── TLS / certificate handling
├── Request creation
├── Response validation
├── Retry handling
├── XML retrieval
└── ParserThe project must not contain:
real certificates
private keys
production credentials
production account numbersSEB integration follows the same adapter architecture.
SEB Client
│
├── Authentication
├── Certificate handling
├── API/Gateway communication
├── Response validation
└── CAMT/XML parserAll banks are converted to the same internal representation.
Example:
Transaction(
bank=BankName.SWEDBANK,
account_iban="LV...",
transaction_id="123456",
booking_date=date(...),
amount=Decimal("125.50"),
currency="EUR",
direction=TransactionDirection.CREDIT,
description="Customer payment",
)This prevents bank-specific fields from leaking into the accounting layer.
##11. Money Handling The project must never use floating-point numbers for monetary calculations.
Correct:
from decimal import Decimal
amount = Decimal("125.50")Incorrect:
amount = 125.50All monetary operations must use:
Decimalwith explicit currency handling.
Duplicate imports are one of the primary risks in financial automation.
Every transaction receives a deterministic fingerprint:
SHA-256(
bank
+
account
+
transaction_id
+
booking_date
+
amount
+
currency
+
direction
+
counterpart
+
end_to_end_id
)Example:
8a1f7f2c...Before inserting a transaction:
Transaction
│
▼
Generate fingerprint
│
▼
Already exists?
/ \
yes no
│ │
skip importThis makes repeated synchronization safe.
Every imported statement should be reconciled before accounting export.
Formula:
Closing Balance =
Opening Balance
+ Credits
- DebitsIf:
calculated closing != bank closingthe import is rejected.
Example:
Opening balance: 10,000.00 EUR
Credits: 5,000.00 EUR
Debits: -2,000.00 EUR
Expected closing: 13,000.00 EUR
Bank closing: 13,000.00 EUR
Result: PASSA reconciliation failure must prevent automatic export.
Accounting systems are implemented through adapters.
AccountingAdapter
│
├── ZalktisExporter
│
└── JumisExporterThe accounting layer must never know whether a transaction originated from:
Swedbank
SEB
manual import
another bankIt receives normalized transactions only.
The Zalktis integration should use a supported import format corresponding to the target Zalktis version and configuration.
Potential formats may include:
ISO/FiDAViSta XML
CSV
other officially supported import formatsThe exact formatter should be selected according to the accounting installation and import configuration.
Production implementation should include:
schema validation
encoding validation
date validation
currency validation
duplicate detection
required-field validationJumis should be implemented through an independent adapter.
class JumisExporter(AccountingAdapter):
...This prevents Jumis-specific formatting requirements from affecting Zalktis.
##17. File Processing Pipeline Files move through controlled directories.
data/incoming/
│
▼
data/processing/
│
├── validation
├── parsing
├── reconciliation
└── duplicate detection
│
├───────────────┐
▼ ▼
data/archive/ data/failed/
│
▼
data/outgoing/A file should never be modified in-place after successful processing.
Generated files must first be written to a temporary file.
output.csv.tmp
│
▼
flush
│
▼
atomic rename
│
▼
output.csvThis prevents partially written accounting files.
Configuration is separated from source code.
Example:
application:
environment: production
timezone: Europe/Riga
database:
url: postgresql://...
accounting:
provider: zalktis
zalktis:
output_directory: ./data/outgoing/zalktisSensitive values must not be committed.
The following must never be committed:
.env
private keys
client certificates
bank passwords
API tokens
production credentials
database passwordsExample:
.env.examplemay contain:
DATABASE_URL=
SWEDBANK_ENABLED=false
SEB_ENABLED=falsebut never real credentials.
Development:
SQLiteProduction:
PostgreSQLThe database stores metadata such as:
transaction fingerprint
transaction ID
bank
account
import timestamp
processing status
job IDRaw bank documents should additionally be archived on the filesystem or controlled object storage.
##22. Auditability Every import should be traceable.
Recommended audit information:
job_id
bank
account
period
source document
source hash
transaction count
new transactions
duplicates
reconciliation result
export result
timestamp
application versionExample:
Job: 2026-08-17-001
Bank: SEB
Period: 2026-08-16 → 2026-08-17
Records: 143
New: 138
Duplicate: 5
Reconcile: PASS
Export: SUCCESSErrors are classified.
ConfigurationError
AuthenticationError
BankAPIError
StatementParseError
ReconciliationError
ExportErrorThe CLI should return a non-zero exit code on failure.
Example:
lv-bank-rpa syncSuccess:
exit code 0Failure:
exit code 1This is important for Task Scheduler, cron, systemd and CI/CD.
Transient errors may be retried.
Example:
attempt 1
│
└── failure
│
▼
2 seconds
│
▼
attempt 2
│
└── failure
│
▼
5 seconds
│
▼
attempt 3Do not retry indefinitely.
Authentication and validation errors should generally fail immediately.
Logs should contain operational information but never secrets.
Allowed:
Bank: SEB
Account: LV********1234
Transactions: 145
Duration: 3.4 secNot allowed:
password=...
private_key=...
client_secret=...
full authorization tokenProduction logging should support structured JSON.
- least privilege;
- no credentials in Git;
- TLS verification enabled;
- certificate validation;
- encrypted secret storage where appropriate;
- restricted filesystem permissions;
- audit logs;
- immutable source documents;
- idempotent processing;
- no payment functionality;
- explicit dry-run mode.
The system should support:
lv-bank-rpa sync --dry-runDry run should:
retrieve
parse
validate
reconcile
calculate exportbut should not:
write accounting import
commit irreversible changesThis is useful for deployment testing.
Planned commands:
lv-bank-rpa healthCheck application health.
lv-bank-rpa syncSynchronize configured bank accounts.
lv-bank-rpa sync --from 2026-08-01 --to 2026-08-17Synchronize a specific period.
lv-bank-rpa sync --dry-runRun without final export.
lv-bank-rpa reconcileRun reconciliation.
lv-bank-rpa exportGenerate accounting import files.
lv-bank-rpa archiveArchive processed source documents.
Windows:
Windows Task Scheduler
│
▼
lv-bank-rpa syncLinux:
systemd timer
│
▼
lv-bank-rpa syncAlternative:
cronThe scheduler must not contain banking business logic.
Production deployment may use:
Docker
│
├── application
└── PostgreSQLExample:
docker compose up -dBank certificates and secrets should be mounted securely and never baked into the image.
The project uses several test layers.
$$ Unit tests Test individual functions:
IBAN validation
fingerprints
parsers
reconciliation
validators
exportersTest:
Bank adapter
Database
Filesystem
Accounting exporterValidate external formats:
CAMT XML
bank-specific schemas
accounting import schemasExample:
input:
tests/fixtures/swedbank/statement.xml
expected:
tests/fixtures/zalktis/expected.csvGitHub Actions should execute:
checkout
│
▼
Python setup
│
▼
pip install
│
├── ruff
├── mypy
├── pytest
└── security scanPull requests should not be merged when tests fail.
Recommended tools:
ruff
mypy
pip-audit
bandit
GitHub Dependabot
GitHub Secret ScanningThe repository must not contain:
private keys
bank credentials
API tokens
real customer data
production statementsBank statements can contain personal and financial information.
Therefore:
- real statements must not be committed to Git;
- fixtures must use synthetic data;
- logs must avoid personal data;
- archive permissions must be restricted;
- backups must be encrypted;
- retention policies should be defined.
Backup targets:
Database
Configuration
Processed source documents
Audit metadataDo not blindly back up:
temporary files
processing files
logs containing secretsExample:
scripts/backup.shshould create an encrypted backup according to the deployment environment.
Typical daily operation:
06:00
│
▼
Scheduler starts
│
▼
Authenticate with bank
│
▼
Download statement
│
▼
Validate XML
│
▼
Parse transactions
│
▼
Generate fingerprints
│
▼
Detect duplicates
│
▼
Reconcile balances
│
├── FAIL ──► data/failed/
│
▼
Generate accounting file
│
▼
data/outgoing/
│
▼
Archive original
│
▼
Audit logIf processing fails:
data/processing/
│
▼
ERROR
│
▼
data/failed/The original input must remain available for investigation.
A failed job should be safely retryable.
Because transaction fingerprints are deterministic, retrying the same statement should not create duplicate accounting transactions.
- Configuration validation
- Structured logging
- Error classification
- Retry policy
- Timeouts
- Idempotency
- Reconciliation
- Audit trail
- Dry-run mode
- Official API/Gateway contract
- TLS validation
- Client certificate handling
- Authentication tests
- XML schema validation
- Error mapping
- Rate-limit handling
- Zalktis format confirmed
- Zalktis import tested
- Jumis format confirmed
- Duplicate protection
- Encoding tests
- Decimal precision tests
- No secrets in Git
- Certificate permissions
- Encrypted backups
- Secret rotation procedure
- Audit logging
- Dependency scanning
- Windows scheduler
- Linux scheduler
- Backup
- Monitoring
- Alerting
- Recovery procedure
Clone repository:
git clone https://github.com/mscbuild/lv-bank-rpa/
cd lv-bank-rpaCreate environment:
python -m venv .venv.venv\Scripts\Activate.ps1source .venv/bin/activateInstall:
pip install -e ".[dev]"Run tests:
pytestRun linter:
ruff check .Run type checker:
mypy appRun application:
lv-bank-rpa healthRecommended branch model:
main
│
├── develop
│
├── feature/swedbank-api
│
├── feature/seb-api
│
├── feature/zalktis-export
│
└── fix/reconciliationCommit examples:
feat(bank): add Swedbank gateway adapter
feat(bank): add SEB statement parser
feat(accounting): add Zalktis exporter
feat(core): add transaction idempotency
fix(reconciliation): handle debit sign
test(bank): add CAMT fixtures
security: harden certificate validation
docs: update deployment guideThe project follows Semantic Versioning:
MAJOR.MINOR.PATCHExample:
0.1.0
0.2.0
1.0.0
1.0.1Before 1.0.0, API and database structures may change.
A production release should require:
✓ all tests pass
✓ lint passes
✓ type checking passes
✓ security scan passes
✓ bank contract tests pass
✓ accounting import tested
✓ reconciliation tested
✓ backup tested
✓ recovery tested
✓ documentation updatedDistributed under the Apache-2.0. See the LICENSE file for details.